migpt / config.js
asemxin
Fix segmented playback with dynamic wait time calculation
06d014e
// 🔄 会话状态管理 - 跟踪是否在连续对话模式
let inConversationMode = false;
let lastActivityTime = Date.now();
const CONVERSATION_TIMEOUT = 5 * 60 * 1000; // 5分钟无活动自动退出
export default {
speaker: {
userId: "104544581",
password: "xin12345",
passToken: "V1:J7rrshrufaw8uWrlTMO7xxdYqvWIT0JZUqwbl9YuVuCnljlYhobJm6vO/rem/hRUvTASl/w3akwM4pDSC2GNH1gIVn6P7SCGjR4HIbpKzPzP3XtYeSW+5WYU9GIBlll9cqOzhi+5fi0VI3mU3K3F/tpZXa4JoTLxbRpRt9YB7DgFLjsKYD9elKSR7jr6zSnTIzgXsAntm1b7Yq//DHm1OB4fpy1Ldn4DXhR7PvwgA2MMRCrUMbE645GAKTb6fbf6mxz7MuTuDv4u56Juz+37yeoZBf3PNvyA+EbflsgnFeaE9cdVWArLIKFuBU983IZw4WuBpjxvo1X9qXvcfYSxBg==",
did: "多多2",
},
openai: {
// 使用带搜索能力的模型
model: "gemini-2.5-flash-search",
baseURL: "https://tdhxxzcu.ap-southeast-1.clawcloudrun.com/v1",
apiKey: "Csrchina@163.com",
},
context: {
historyMaxLength: 10,
},
prompt: {
system: "你是一个智能助手,请以下方式回答:不需客套,直接回答问题;句子简短,但一定要完整清晰回答用户的所有问题;不用英文缩写、表情、Markdown、代码;数字用中文读法,时间与金额说全称。"
},
// 首次触发关键词
callAIKeywords: ["请", "你"],
// 退出连续对话的关键词
exitKeywords: ["退下", "再见", "拜拜", "结束对话"],
async onMessage(engine, msg) {
const text = msg.text;
const now = Date.now();
// 检查是否超时,自动退出连续对话模式
if (inConversationMode && (now - lastActivityTime > CONVERSATION_TIMEOUT)) {
inConversationMode = false;
console.log("⏰ 连续对话超时,已自动退出");
}
// 检查是否要退出连续对话
if (this.exitKeywords.some((e) => text.includes(e))) {
if (inConversationMode) {
inConversationMode = false;
// 打断小爱并播放退出提示
await engine.speaker.abortXiaoAI();
await engine.MiOT.doAction(7, 3, "好的,有事再叫我");
console.log("👋 退出连续对话模式");
return { handled: true };
}
}
// 判断是否应该由 MiGPT 处理
const isFirstTrigger = this.callAIKeywords.some((e) => text.startsWith(e));
const shouldHandle = isFirstTrigger || inConversationMode;
if (shouldHandle) {
// 🔥 立即打断小爱(支持随时打断)
await engine.speaker.abortXiaoAI();
// 进入/保持连续对话模式
if (!inConversationMode) {
inConversationMode = true;
console.log("🎯 进入连续对话模式");
}
lastActivityTime = now;
// 调用 AI 回答
const { text: reply } = await engine.askAI(msg);
console.log(`🔊 ${reply}`);
// 🎯 分段播放长文本,防止 TTS 超时中断
const maxLength = 200; // 单次播放最大字数
if (reply.length > maxLength) {
// 按句号、问号、感叹号分段
const sentences = reply.match(/[^。!?]+[。!?]/g) || [reply];
let currentChunk = "";
for (const sentence of sentences) {
if ((currentChunk + sentence).length > maxLength) {
// 播放当前段落
if (currentChunk) {
await engine.MiOT.doAction(7, 3, currentChunk);
// 根据文字数量计算等待时间(每字约350ms)+ 1秒缓冲
const waitTime = currentChunk.length * 350 + 1000;
await new Promise(resolve => setTimeout(resolve, waitTime));
}
currentChunk = sentence;
} else {
currentChunk += sentence;
}
}
// 播放最后一段
if (currentChunk) {
await engine.MiOT.doAction(7, 3, currentChunk);
}
} else {
// 短文本直接播放
await engine.MiOT.doAction(7, 3, reply);
}
return { handled: true };
}
// 不在连续对话模式,且不是触发词,让原始小爱处理
},
};