Files
RGKT/rg-09112127/js/chat.js
2025-10-10 19:35:04 +08:00

375 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const chatIcon = document.getElementById('chatIcon');
const chatDialog = document.getElementById('chatDialog');
const closeChat = document.getElementById('closeChat');
const voiceInputBtn = document.getElementById('voiceInputBtn');
const voiceStatus = document.getElementById('voiceStatus');
const notificationBadge = document.querySelector('.notification-badge');
var APPID = "43341b7e";
var API_SECRET = "MWQzMWEyYTAyYzVlZWRjOTM1NjE0MjI4";
var API_KEY = "a46e1fc5d062a14b28cde0ff2c0046e1";
let btnStatus = "UNDEFINED"; // "UNDEFINED" "CONNECTING" "OPEN" "CLOSING" "CLOSED"
//大模型调用部分
const apiPassword = "xozAvYSDDAUrZtmZAMHe:BesWfhINSVtAumsVcxCt";
// 注意:请将 123456 替换为你自己的 APIPassword
let chatList = [
{
"role": "system",
"content": "请你扮演一名幼师,跟我聊聊天吧!"
}
]
async function sendRequest(str,callback) {
chatList.push({
"role": "user",
"content": str
})
const body =
{
"apiPassword": apiPassword,
"chatList": chatList
}
let data= await fetch('http://w.textbox.wang/index.php/english/Index/xhchat', {
method: 'POST', // 或者使用 'GET' 取决于API的要求
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body)
})
.then(response => response.json()) // 解析JSON响应
.then(data => {
// 处理响应数据
console.log(data.choices[0].message.content);
chatList.push({
"role": "assistant",
"content": data.choices[0].message.content
})
callback(data.choices[0].message.content)
// document.getElementById('output').innerText = data.text; // 假设响应中包含文本字段
})
.catch(error => console.error('Error:', error)); // 捕获并处理错误
}
// 执行请求
//语音播报部分
let preText = "";
function speakFly(text) {
if (preText == text) {
return
}
preText = text;
const audioPlayer = document.getElementById('chatAudio');
const vcn = ["x4_lingxiaoqi_cts", "x4_lingyouyou", "x4_lingfeizhe_zl", "aisjinger", "aisbabyxu"];
$.ajax({
url: 'http://rgclass.iflysse.com:8200/text2audio',
type: 'POST',
contentType: 'application/x-www-form-urlencoded',
data: {
text: text,
vcn: vcn[1],
speed: 50
},
headers: {
'Cache-Control': 'no-store'
},
success: function (data) {
let timer01 = setTimeout(function () {
$(".message-content:last").text(text)
$(".chat-content").animate({ scrollTop: $(".chat-content")[0].scrollHeight }, 1000);
const url = `http://rgclass.iflysse.com:8200${data}`;
audioPlayer.src = url;
audioPlayer.load();
audioPlayer.play();
audioPlayer.onerror = function (e) {
setTimeout(function () {
audioPlayer.load();
audioPlayer.play();
}, 500);
console.error(e);
};
audioPlayer.addEventListener('ended', () => {//连续对话
voiceInputBtn.click();
});
clearTimeout(timer01);
// console.log("text2audio success", url, audioPlayer);
}, 1000);
},
error: function (jqXHR, textStatus, errorThrown) {
console.error('text2audio error:', textStatus, errorThrown);
}
});
let timer02 = setTimeout(function () {
preText = "";
clearTimeout(timer02);
}, 5000);
}
const recorder = new RecorderManager("./dist");
recorder.onStart = () => {
changeBtnStatus("OPEN");
}
let iatWS;
let resultText = "";
let resultTextTemp = "";
let countdownInterval;
let isListening = false;
/**
* 获取websocket url
* 该接口需要后端提供,这里为了方便前端处理
*/
function getWebSocketUrl() {
// 请求地址根据语种不同变化
var url = "wss://iat-api.xfyun.cn/v2/iat";
var host = "iat-api.xfyun.cn";
var apiKey = API_KEY;
var apiSecret = API_SECRET;
var date = new Date().toGMTString();
var algorithm = "hmac-sha256";
var headers = "host date request-line";
var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/iat HTTP/1.1`;
var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);
var signature = CryptoJS.enc.Base64.stringify(signatureSha);
var authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;
var authorization = btoa(authorizationOrigin);
url = `${url}?authorization=${authorization}&date=${date}&host=${host}`;
return url;
}
function toBase64(buffer) {
var binary = "";
var bytes = new Uint8Array(buffer);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
function changeBtnStatus(status) {
btnStatus = status;
if (status === "CONNECTING") {
// btnControl.innerText = "建立连接中";
// document.getElementById("partial-result").innerText = "";
resultText = "";
resultTextTemp = "";
}
}
let lydjs=null;
function renderResult(resultData) {
clearTimeout(lydjs);
lydjs=setTimeout(function(){
iatWS.close();
},3000);
// 识别结束
let jsonData = JSON.parse(resultData);
if (jsonData.data && jsonData.data.result) {
let data = jsonData.data.result;
let str = "";
let ws = data.ws;
for (let i = 0; i < ws.length; i++) {
str = str + ws[i].cw[0].w;
}
console.log(str);
// 开启wpgs会有此字段(前提:在控制台开通动态修正功能)
// 取值为 "apd"时表示该片结果是追加到前面的最终结果;取值为"rpl" 时表示替换前面的部分结果替换范围为rg字段
if (data.pgs) {
if (data.pgs === "apd") {
// 将resultTextTemp同步给resultText
resultText = resultTextTemp;
// 移除打字指示器
}
// 将结果存储在resultTextTemp中
resultTextTemp = resultText + str;
} else {
resultText = resultText + str;
}
$(".typing-indicator").text(resultTextTemp);
// document.getElementById("partial-result").innerText =
// resultTextTemp || resultText || "";
}
if (jsonData.code === 0 && jsonData.data.status === 2) {
iatWS.close();
}
if (jsonData.code !== 0) {
iatWS.close();
console.error(jsonData);
}
}
function connectWebSocket() {
const websocketUrl = getWebSocketUrl();
if ("WebSocket" in window) {
iatWS = new WebSocket(websocketUrl);
} else if ("MozWebSocket" in window) {
iatWS = new MozWebSocket(websocketUrl);
} else {
alert("浏览器不支持WebSocket");
return;
}
changeBtnStatus("CONNECTING");
console.log(iatWS);
iatWS.onopen = (e) => {
isListening = true;
voiceInputBtn.classList.add('active');
voiceInputBtn.innerHTML = '<i class="fa fa-stop"></i>';
voiceStatus.textContent = '正在聆听...';
// 添加用户正在说话的UI反馈
addMessage('user', '<div class="typing-indicator"><span></span><span></span><span></span></div>');
// 开始录音
recorder.start({
sampleRate: 16000,
frameSize: 1280,
});
var params = {
common: {
app_id: APPID,
},
business: {
language: "zh_cn",
domain: "iat",
accent: "mandarin",
vad_eos: 5000,
dwa: "wpgs",
},
data: {
status: 0,
format: "audio/L16;rate=16000",
encoding: "raw",
},
};
console.log("asd");
iatWS.send(JSON.stringify(params));
clearTimeout(lydjs);
lydjs=setTimeout(function(){
iatWS.close();
},3000);
};
iatWS.onmessage = (e) => {
console.log("asdas")
renderResult(e.data);
console.log(e.data);
};
iatWS.onerror = (e) => {
console.error(e);
recorder.stop();
changeBtnStatus("CLOSED");
};
iatWS.onclose = (e) => {
removeLastUserMessage();
if(resultTextTemp!=''){
// 显示用户的语音输入
addMessage('user', resultTextTemp);
// 模拟AI回复
// setTimeout(() => {
addMessage('bot', '我正在处理你的请求...');
// }, 1000);
sendRequest(resultTextTemp,function(response){
speakFly(response)
});
}
voiceInputBtn.innerHTML = '<i class="fa fa-microphone"></i>';
voiceInputBtn.classList.remove('active');
voiceStatus.textContent = '';
isListening = false;
recorder.stop();
changeBtnStatus("CLOSED");
};
}
recorder.onFrameRecorded = ({ isLastFrame, frameBuffer }) => {
if (iatWS.readyState === iatWS.OPEN) {
iatWS.send(
JSON.stringify({
data: {
status: isLastFrame ? 2 : 1,
format: "audio/L16;rate=16000",
encoding: "raw",
audio: toBase64(frameBuffer),
},
})
);
if (isLastFrame) {
changeBtnStatus("CLOSING");
}
}
};
recorder.onStop = () => {
clearInterval(countdownInterval);
};
voiceInputBtn.onclick = function () {
if (btnStatus === "UNDEFINED" || btnStatus === "CLOSED") {
connectWebSocket();
} else if (btnStatus === "CONNECTING" || btnStatus === "OPEN") {
// 结束录音
recorder.stop();
}
};
// 打开/关闭聊天对话框
chatIcon.addEventListener('click', () => {
chatDialog.classList.toggle('open');
// 打开对话框后移除通知徽章
// if (chatDialog.classList.contains('open')) {
// notificationBadge.classList.remove('active');
// setTimeout(() => {
// notificationBadge.style.display = 'none';
// }, 300);
// }
});
closeChat.addEventListener('click', () => {
chatDialog.classList.remove('open');
});
// 添加消息到聊天界面
function addMessage(type, content) {
const chatContent = document.querySelector('.chat-content');
const messageDiv = document.createElement('div');
messageDiv.className = `message ${type}`;
messageDiv.innerHTML = `<div class="message-content">${content}</div>`;
chatContent.appendChild(messageDiv);
// 滚动到底部
chatContent.scrollTop = chatContent.scrollHeight;
}
// 移除最后一条用户消息(用于移除打字指示器)
function removeLastUserMessage() {
const chatContent = document.querySelector('.chat-content');
const messages = chatContent.querySelectorAll('.message');
if (messages.length > 0) {
const lastMessage = messages[messages.length - 1];
if (lastMessage.classList.contains('user')) {
chatContent.removeChild(lastMessage);
}
}
}
// 添加窗口点击事件,点击外部关闭聊天框
window.addEventListener('click', (event) => {
if (event.target === chatDialog) {
chatDialog.classList.remove('open');
}
});