| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 |
- /**
- * 聊天历史记录管理模块
- * 负责保存聊天记录、生成AI总结、读取历史总结、读取所有聊天记录
- */
- const stub = (name) => ({ success: false, error: `${name} 需在主进程实现` })
- const electronAPI = {
- saveChatHistory: () => stub('saveChatHistory'),
- readChatHistory: () => stub('readChatHistory'),
- readAllChatHistory: () => stub('readAllChatHistory'),
- saveChatHistoryTxt: () => stub('saveChatHistoryTxt'),
- saveChatHistorySummary: () => stub('saveChatHistorySummary'),
- getChatHistorySummary: () => stub('getChatHistorySummary'),
- }
- /**
- * 保存聊天记录到 history 文件夹
- * @param {string|Array} chatHistory - 聊天记录(可以是JSON字符串、文本格式或消息数组)
- * @param {string} folderPath - 工作流文件夹路径(相对路径,如 "static/processing/微信聊天自动发送工作流")
- * @returns {Promise<{success: boolean, error?: string, filePath?: string}>}
- */
- async function saveChatHistory(chatHistory, folderPath) {
- try {
- if (!chatHistory) {
- return { success: false, error: '聊天记录为空' };
- }
- if (!electronAPI.saveChatHistory) {
- return { success: false, error: '保存聊天记录 API 不可用' };
- }
- // 解析聊天记录,转换为消息数组
- let newMessages = [];
-
- if (Array.isArray(chatHistory)) {
- // 如果已经是数组,直接使用
- newMessages = chatHistory;
- } else if (typeof chatHistory === 'string') {
- // 如果是字符串,尝试解析
- // 先检查是否是 chat-history.txt 格式(包含 "data" 和 "friend"/"me" 键)
- if (chatHistory.includes('"data"') && (chatHistory.includes('"friend"') || chatHistory.includes('"me"'))) {
- // 是 chat-history.txt 格式
- newMessages = parseChatHistoryTxtFormat(chatHistory);
- } else {
- // 尝试作为标准JSON字符串解析
- try {
- const parsed = JSON.parse(chatHistory);
- if (Array.isArray(parsed)) {
- newMessages = parsed;
- } else {
- // 如果不是数组,按文本格式解析
- newMessages = parseChatHistoryText(chatHistory);
- }
- } catch (e) {
- // JSON解析失败,按文本格式解析
- newMessages = parseChatHistoryText(chatHistory);
- }
- }
- }
- if (newMessages.length === 0) {
- return { success: false, error: '聊天记录解析后为空' };
- }
- // 读取历史聊天记录,进行去重
- let historyMessages = [];
- try {
- if (electronAPI.readChatHistory) {
- const historyResult = await electronAPI.readChatHistory(folderPath);
- if (historyResult.success && historyResult.messages && Array.isArray(historyResult.messages)) {
- historyMessages = historyResult.messages;
- }
- }
- } catch (error) {
- // 读取历史聊天记录失败,将保存所有新消息
- }
- // 对历史消息构建查找集合(用于去重)
- const historyMessageMap = new Map();
- historyMessages.forEach((msg, index) => {
- const normalizedText = (msg.text || '').trim();
- const key = `${msg.sender || ''}:${normalizedText}`;
- // 如果同一个 key 已存在,保留索引较小的(更早的消息)
- if (!historyMessageMap.has(key)) {
- historyMessageMap.set(key, {
- index,
- sender: msg.sender,
- text: normalizedText
- });
- }
- });
- // 找出真正新的消息(不在历史记录中的)
- const uniqueNewMessages = [];
- const duplicateMessages = [];
-
- for (const newMsg of newMessages) {
- const normalizedText = (newMsg.text || '').trim();
- const key = `${newMsg.sender || ''}:${normalizedText}`;
-
- // 检查是否已存在于历史记录中
- if (!historyMessageMap.has(key)) {
- // 这是新消息
- uniqueNewMessages.push(newMsg);
- } else {
- // 消息已存在,记录为重复消息
- const existingMsg = historyMessageMap.get(key);
- duplicateMessages.push({
- sender: existingMsg.sender,
- text: normalizedText.substring(0, 50) + (normalizedText.length > 50 ? '...' : '')
- });
- }
- }
-
- if (uniqueNewMessages.length === 0) {
- // console.log(`没有新消息(所有 ${newMessages.length} 条消息都与历史记录重合),跳过保存`);
- // if (duplicateMessages.length > 0) {
- // console.log(`重复消息示例: ${duplicateMessages[0].sender}: ${duplicateMessages[0].text}`);
- // }
- return { success: true, skipped: true, reason: 'no_new_messages' };
- }
- // 合并消息:历史消息 + 新的唯一消息
- const mergedMessages = [...historyMessages, ...uniqueNewMessages];
-
- // console.log(`将保存 ${uniqueNewMessages.length} 条新消息(已剔除 ${duplicateMessages.length} 条重复消息)`);
- // if (duplicateMessages.length > 0 && duplicateMessages.length <= 5) {
- // console.log(`重复消息: ${duplicateMessages.map(m => `${m.sender}: ${m.text}`).join('; ')}`);
- // } else if (duplicateMessages.length > 5) {
- // console.log(`重复消息示例: ${duplicateMessages.slice(0, 3).map(m => `${m.sender}: ${m.text}`).join('; ')} ...`);
- // }
- // 构建保存的数据结构
- const now = new Date();
- const historyData = {
- timestamp: now.toISOString(),
- messageCount: mergedMessages.length,
- messages: mergedMessages
- };
- // 通过 IPC 调用主进程保存聊天记录到 chat-history.txt(新格式)
- // 使用新的 API 保存为 chat-history.txt 格式
- if (electronAPI.saveChatHistoryTxt) {
- const result = await electronAPI.saveChatHistoryTxt(folderPath, uniqueNewMessages);
-
- if (!result.success) {
- return { success: false, error: result.error };
- }
- return { success: true, filePath: result.filePath };
- }
-
- // 向后兼容:如果没有新API,使用旧API
- const result = await electronAPI.saveChatHistory(folderPath, historyData);
-
- if (!result.success) {
- return { success: false, error: result.error };
- }
- return { success: true, filePath: result.filePath };
- } catch (error) {
- return { success: false, error: error.message };
- }
- }
- /**
- * 生成聊天记录的AI总结
- * @param {string} chatHistory - 聊天记录文本
- * @param {string} folderPath - 工作流文件夹路径
- * @param {string} modelName - AI模型名称(可选,默认 'gpt-5-nano-ca')
- * @returns {Promise<{success: boolean, error?: string, summary?: string}>}
- */
- async function generateHistorySummary(chatHistory, folderPath, modelName = 'gpt-5-nano-ca') {
- try {
- if (!chatHistory || !chatHistory.trim()) {
- return { success: false, error: '聊天记录为空' };
- }
- // 读取历史总结(如果存在)
- const existingSummary = await getHistorySummary(folderPath);
- const summaryContext = existingSummary ? `\n\n之前的总结:\n${existingSummary}` : '';
- // 构建AI提示词
- const prompt = `请总结以下微信聊天记录的主要内容、话题和氛围。总结要简洁明了,控制在100字以内,帮助理解对话的上下文和情感倾向。
- 聊天记录:
- ${chatHistory}${summaryContext}
- 请直接返回总结内容,不要包含其他说明文字。`;
- // 调用AI API
- const response = await fetch('https://ai-anim.com/api/text2textByModel', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- prompt: prompt,
- modelName: modelName
- })
- });
- if (!response.ok) {
- return { success: false, error: `AI请求失败: ${response.statusText}` };
- }
- const data = await response.json();
- const summary = data.data?.output_text || data.text || data.content || '';
- if (!summary || !summary.trim()) {
- return { success: false, error: 'AI返回的总结为空' };
- }
- // 通过 IPC 调用主进程保存总结
- if (!electronAPI.saveChatHistorySummary) {
- return { success: false, error: '保存聊天记录总结 API 不可用' };
- }
- const saveResult = await electronAPI.saveChatHistorySummary(folderPath, summary.trim());
-
- if (!saveResult.success) {
- return { success: false, error: saveResult.error };
- }
- // console.log(`聊天记录总结已保存`);
- return { success: true, summary: summary.trim() };
- } catch (error) {
- return { success: false, error: error.message };
- }
- }
- /**
- * 读取历史聊天记录的总结
- * @param {string} folderPath - 工作流文件夹路径
- * @returns {Promise<string>} 返回总结文本,如果不存在则返回空字符串
- */
- async function getHistorySummary(folderPath) {
- try {
- if (!electronAPI.getChatHistorySummary) {
- return '';
- }
- const result = await electronAPI.getChatHistorySummary(folderPath);
-
- if (!result.success) {
- // 文件不存在是正常情况,返回空字符串
- return '';
- }
- return result.summary || '';
- } catch (error) {
- return '';
- }
- }
- /**
- * 解析 chat-history.txt 格式的 JSON 字符串为消息数组
- * 格式:{"data":"时间戳","friend":"消息","me":"消息"},允许重复键
- * @param {string} jsonString - JSON 字符串
- * @returns {Array<{sender: string, text: string}>}
- */
- function parseChatHistoryTxtFormat(jsonString) {
- const messages = [];
- if (!jsonString || typeof jsonString !== 'string') {
- return messages;
- }
- try {
- // 由于JSON不支持重复键,我们需要从文本解析每一行
- const lines = jsonString.split('\n');
- for (const line of lines) {
- // 匹配格式:\t"key":"value", 或 \t"key":"value"
- const match = line.match(/^\s*"([^"]+)":\s*"([^"]*)"\s*,?\s*$/);
- if (match) {
- const key = match[1];
- const value = match[2];
- // 只处理 friend 和 me 键,忽略 data 键(时间戳)
- if (key === 'friend' || key === 'me') {
- messages.push({
- sender: key,
- text: value
- });
- }
- }
- }
- } catch (e) {
- // 解析失败,返回空数组
- return messages;
- }
- return messages;
- }
- /**
- * 解析聊天记录文本为结构化数据
- * @param {string} chatHistoryText - 聊天记录文本(格式:好友: xxx\n我: xxx)
- * @returns {Array<{sender: string, text: string}>}
- */
- function parseChatHistoryText(chatHistoryText) {
- const messages = [];
- const lines = chatHistoryText.split('\n').filter(line => line.trim());
- for (const line of lines) {
- // 匹配格式:对方: xxx、好友: xxx 或 我: xxx
- const match = line.match(/^(对方|好友|我):\s*(.+)$/);
- if (match) {
- const senderLabel = match[1];
- const sender = (senderLabel === '对方' || senderLabel === '好友') ? 'friend' : 'me';
- const text = match[2].trim();
- messages.push({ sender, text });
- }
- }
- return messages;
- }
- /**
- * 读取所有聊天记录(合并所有历史文件)
- * @param {string} folderPath - 工作流文件夹路径(相对路径,如 "static/processing/微信聊天自动发送工作流")
- * @returns {Promise<{success: boolean, error?: string, messages?: Array}>}
- */
- async function readAllChatHistory(folderPath) {
- try {
- // 优先使用新的 API,如果没有则使用旧的(向后兼容)
- const api = electronAPI.readChatHistory || electronAPI.readAllChatHistory;
- if (!api) {
- return { success: false, error: '读取聊天记录 API 不可用' };
- }
- const result = await api(folderPath);
- if (!result.success) {
- return { success: false, error: result.error };
- }
- // console.log(`已读取聊天记录,共 ${result.fileCount || 0} 个文件,${result.totalMessages || 0} 条消息`);
- return {
- success: true,
- messages: result.messages || [],
- fileCount: result.fileCount || 0,
- totalMessages: result.totalMessages || 0
- };
- } catch (error) {
- return { success: false, error: error.message || '读取聊天记录失败' };
- }
- }
- module.exports = { saveChatHistory, generateHistorySummary, getHistorySummary, readAllChatHistory }
|