/** * 历史回顾和日志功能 */ import { requestSave } from './save-manager.js'; import { getSettings, LOREBOOK_NAME_PREFIX, LOREBOOK_NAME_SUFFIX } from './config.js'; import { escapeHtml } from './utils.js'; import { showToast } from './toast.js'; // 最大日志数量 const MAX_LOGS = 20; // 获取错误日志 export function getErrorLogs() { const settings = getSettings(); return settings.errorLogs || []; } // 添加错误日志 export function addErrorLog(error, context = '') { const settings = getSettings(); if (!settings.errorLogs) settings.errorLogs = []; const now = new Date(); const timeStr = now.getHours().toString().padStart(2,'0') + ':' + now.getMinutes().toString().padStart(2,'0') + ':' + now.getSeconds().toString().padStart(2,'0'); // 生成简短的错误摘要(约15字) const errorMsg = error?.message || String(error); let summary = context ? context + ': ' : ''; // 截取关键信息 if (errorMsg.length > 15 - summary.length) { summary += errorMsg.substring(0, 15 - summary.length) + '...'; } else { summary += errorMsg; } const logEntry = { time: timeStr, summary: summary.substring(0, 18), // 确保不超过18字 message: errorMsg, context: context }; settings.errorLogs.unshift(logEntry); // 只保留最近的 MAX_LOGS 条 if (settings.errorLogs.length > MAX_LOGS) { settings.errorLogs = settings.errorLogs.slice(0, MAX_LOGS); } requestSave(); return logEntry; } // 清空错误日志 export function clearErrorLogs() { const settings = getSettings(); settings.errorLogs = []; requestSave(); } // 刷新日志列表显示 export function refreshLogsList() { const listEl = document.getElementById('wechat-logs-list'); if (!listEl) return; const logs = getErrorLogs(); if (logs.length === 0) { listEl.innerHTML = '
暂无错误日志 ✅
'; return; } listEl.innerHTML = logs.map((log, idx) => `
${escapeHtml(log.summary || log.message?.substring(0, 15) + '...')} ${escapeHtml(log.time)}
${log.message && log.message !== log.summary ? `
${escapeHtml(log.message.substring(0, 80))}${log.message.length > 80 ? '...' : ''}
` : ''}
`).join(''); } // 判断世界书是否是总结生成的 function isSummaryLorebook(lorebook) { // 检查名称格式:【可乐】和xxx的聊天 if (lorebook.name?.startsWith(LOREBOOK_NAME_PREFIX) && lorebook.name?.endsWith(LOREBOOK_NAME_SUFFIX)) { return true; } // 检查标记 if (lorebook.fromSummary === true) { return true; } return false; } // 判断是否是群聊总结 function isGroupSummary(lorebook) { // 从名称中提取人名部分 if (!lorebook.name?.startsWith(LOREBOOK_NAME_PREFIX)) return false; const nameContent = lorebook.name.slice(LOREBOOK_NAME_PREFIX.length, -LOREBOOK_NAME_SUFFIX.length); // 如果包含逗号,说明是多人(群聊) return nameContent.includes(',') || nameContent.includes(','); } // 获取总结世界书列表(按类型分类) export function getSummaryLorebooks(filter = 'all') { const settings = getSettings(); const selectedLorebooks = settings.selectedLorebooks || []; const summaryBooks = selectedLorebooks .map((lb, idx) => ({ ...lb, originalIndex: idx })) .filter(lb => isSummaryLorebook(lb)); if (filter === 'all') { return summaryBooks; } else if (filter === 'contact') { return summaryBooks.filter(lb => !isGroupSummary(lb)); } else if (filter === 'group') { return summaryBooks.filter(lb => isGroupSummary(lb)); } return summaryBooks; } // 刷新历史回顾列表 export function refreshHistoryList(filter = 'all') { const listEl = document.getElementById('wechat-history-list'); if (!listEl) return; const summaryBooks = getSummaryLorebooks(filter); if (summaryBooks.length === 0) { const emptyText = filter === 'contact' ? '暂无单聊总结' : filter === 'group' ? '暂无群聊总结' : '暂无总结记录'; listEl.innerHTML = `
${emptyText}
前往"总结"功能生成总结
`; return; } listEl.innerHTML = summaryBooks.map(lb => { // 从名称中提取人名 let displayName = lb.name; if (lb.name?.startsWith(LOREBOOK_NAME_PREFIX)) { displayName = lb.name.slice(LOREBOOK_NAME_PREFIX.length, -LOREBOOK_NAME_SUFFIX.length); } const isGroup = isGroupSummary(lb); const entriesCount = lb.entries?.length || 0; return `
${escapeHtml(displayName)}
${entriesCount} 杯总结 · ${lb.lastUpdated || lb.addedTime || '未知时间'}
`; }).join(''); // 绑定点击事件 listEl.querySelectorAll('.wechat-history-item').forEach(item => { item.addEventListener('click', () => { const idx = parseInt(item.dataset.index); showHistoryDetail(idx); }); }); // 绑定开关事件 listEl.querySelectorAll('.wechat-history-toggle').forEach(toggle => { toggle.addEventListener('change', (e) => { e.stopPropagation(); const idx = parseInt(toggle.dataset.index); toggleHistoryItem(idx, toggle.checked); }); }); } // 切换历史记录项启用状态 export function toggleHistoryItem(index, enabled) { const settings = getSettings(); if (settings.selectedLorebooks?.[index]) { settings.selectedLorebooks[index].enabled = enabled; requestSave(); showToast(enabled ? '已启用' : '已禁用'); } } // 显示历史记录详情 export function showHistoryDetail(index) { const settings = getSettings(); const lorebook = settings.selectedLorebooks?.[index]; if (!lorebook) return; // 从名称中提取人名 let displayName = lorebook.name; if (lorebook.name?.startsWith(LOREBOOK_NAME_PREFIX)) { displayName = lorebook.name.slice(LOREBOOK_NAME_PREFIX.length, -LOREBOOK_NAME_SUFFIX.length); } const entries = lorebook.entries || []; // 创建详情弹窗 const modal = document.createElement('div'); modal.className = 'wechat-modal'; modal.id = 'wechat-history-detail-modal'; modal.innerHTML = `
${escapeHtml(displayName)}
${isGroupSummary(lorebook) ? '👥 群聊总结' : '💬 单聊总结'} · ${entries.length} 杯
${entries.length === 0 ? '
暂无条目
' : entries.map((entry, idx) => `
${escapeHtml(entry.comment || '第' + (idx + 1) + '杯')}
${(entry.keys || []).map(k => `${escapeHtml(k)}`).join('')}
${escapeHtml(entry.content || '').substring(0, 200)}${(entry.content?.length || 0) > 200 ? '...' : ''}
`).join('') }
`; document.body.appendChild(modal); // 关闭按钮 modal.querySelector('#wechat-history-detail-close').addEventListener('click', () => modal.remove()); // 点击背景关闭 modal.addEventListener('click', (e) => { if (e.target === modal) modal.remove(); }); // 条目开关 modal.querySelectorAll('.wechat-entry-toggle').forEach(toggle => { toggle.addEventListener('change', (e) => { const entryIdx = parseInt(toggle.dataset.entryIndex); if (settings.selectedLorebooks?.[index]?.entries?.[entryIdx]) { settings.selectedLorebooks[index].entries[entryIdx].enabled = toggle.checked; requestSave(); } }); }); // 同步到酒馆按钮 modal.querySelector('#wechat-history-sync').addEventListener('click', async () => { const btn = modal.querySelector('#wechat-history-sync'); btn.disabled = true; btn.textContent = '同步中...'; try { const { syncEntryToSillyTavern } = await import('./summary.js'); for (let i = 0; i < entries.length; i++) { await syncEntryToSillyTavern(entries[i], i + 1, lorebook.name); } showToast('已同步到酒馆'); } catch (err) { console.error('[可乐] 同步失败:', err); showToast('同步失败: ' + err.message, '⚠️'); addErrorLog(err, '历史回顾同步'); } finally { btn.disabled = false; btn.textContent = '同步到酒馆'; } }); // 从酒馆刷新按钮 modal.querySelector('#wechat-history-refresh').addEventListener('click', async () => { const btn = modal.querySelector('#wechat-history-refresh'); btn.disabled = true; btn.textContent = '刷新中...'; try { const { refreshLorebookFromTavern } = await import('./favorites.js'); await refreshLorebookFromTavern(lorebook.name, index); showToast('已从酒馆刷新'); modal.remove(); refreshHistoryList(); } catch (err) { console.error('[可乐] 从酒馆刷新失败:', err); showToast('刷新失败: ' + err.message, '⚠️'); addErrorLog(err, '历史回顾刷新'); } finally { btn.disabled = false; btn.textContent = '从酒馆刷新'; } }); } // 初始化错误捕获(仅捕获插件内部错误) export function initErrorCapture() { // 插件错误由各模块调用 addErrorLog 主动记录 // 不再全局捕获 console.error,避免记录酒馆其他错误 console.log('[可乐不加冰] 错误日志系统已初始化'); } // 渲染心动瞬间历史记录 export function renderToyHistory(contact) { const contentEl = document.getElementById('wechat-history-content'); if (!contentEl) return; const toyHistory = contact?.toyHistory || []; if (toyHistory.length === 0) { contentEl.innerHTML = `
暂无心动瞬间记录
`; return; } // 按时间倒序排列 const sortedHistory = [...toyHistory].sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0)); contentEl.innerHTML = sortedHistory.map((session, sortedIdx) => { const targetText = session.target === 'character' ? 'TA在用' : '你在用'; const messages = session.messages || []; const previewMessages = messages.slice(0, 5); // 只显示前5条消息预览 const originalIndex = toyHistory.indexOf(session); return `
${escapeHtml(session.gift?.emoji || '')} ${escapeHtml(session.gift?.name || '未知玩具')}
${targetText}
${escapeHtml(session.time || '未知时间')} 时长 ${escapeHtml(session.duration || '00:00')}
${previewMessages.length === 0 ? '
暂无对话记录
' : previewMessages.map(msg => `
${msg.role === 'user' ? '你' : 'TA'}: ${escapeHtml((msg.content || '').substring(0, 50))}${(msg.content?.length || 0) > 50 ? '...' : ''}
`).join('') } ${messages.length > 5 ? `
还有 ${messages.length - 5} 条消息...
` : ''}
`; }).join(''); }