From 9197fde723a9973899fb1111e4ead85dc0e31447 Mon Sep 17 00:00:00 2001 From: Wx-2025 <351320169@qq.com> Date: Mon, 7 Jul 2025 01:59:28 +0800 Subject: [PATCH] Delete index.js --- index.js | 1490 ------------------------------------------------------ 1 file changed, 1490 deletions(-) delete mode 100644 index.js diff --git a/index.js b/index.js deleted file mode 100644 index 1769a52..0000000 --- a/index.js +++ /dev/null @@ -1,1490 +0,0 @@ -import { extension_settings, getContext } from '/scripts/extensions.js'; -import { - saveSettingsDebounced, - eventSource, - event_types, - saveChatConditional, - reloadCurrentChat -} from '/script.js'; -import { SlashCommand } from '/scripts/slash-commands/SlashCommand.js'; - -let availableModels = []; -let isFetchingModels = false; -// 插件名称 -const extensionName = 'ST-Amily2-Chat-Optimisation'; -const extensionFolderPath = `scripts/extensions/third-party/ST-Amily2-Chat-Optimisation`; - -// === 动态密码生成器 === -function generateDynamicPassword(date = new Date()) { - // 种子值 - const seed = { - a: 1103515245, - c: 12345, - m: 2147483647, - }; - - // 核心哈希算法 - function customHash(input) { - let hash = 0; - for(let i = 0; i < input.length; i++) { - hash = ((hash << 5) - hash) + input.charCodeAt(i); - hash |= 0; // 转为32位整型 - } - return hash >>> 0; // 确保为正整数 - } - - // 使用传入的日期作为基准 - const month = date.getMonth() + 1; - const day = date.getDate(); - const year = date.getFullYear(); - const baseInput = `${month}-${day}-AMILY_${year}`; - - // 生成伪随机种子 - const str1 = `SD${customHash(baseInput)}`; - const str2 = `V${customHash(str1)}`; - - // 使用线性同余算法生成密码 - function lcgRandom(params) { - return function() { - params.seed = (params.a * params.seed + params.c) % params.m; - return params.seed; - }; - } - - const combinedSeed = customHash(str2) % seed.m; - const randFunc = lcgRandom({ - a: seed.a, - c: seed.c, - m: seed.m, - seed: combinedSeed - }); - - // 密码字符集(移除易混淆字符) - const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; - - // 生成密码段 - const segments = []; - for (let segIdx = 0; segIdx < 3; segIdx++) { - let segment = ''; - for (let i = 0; i < 4; i++) { - const randValue = Math.abs(randFunc()); - segment += chars.charAt(randValue % chars.length); - } - segments.push(segment); - } - - return segments.join('-'); -} - -// === 开发者使用的密码工具 === -function getPasswordForDate(date = new Date()) { - return generateDynamicPassword(date); -} - -// 密码有效期设置(默认为7天) -const PASSWORD_VALIDITY_DAYS = 7; - -// 开发者提示 - 在控制台显示今日密码 -console.warn("[Amily2号] 开发者提示:今日密码 - ", getPasswordForDate()); -console.log(`[Amily2号] 密码有效期为: ${PASSWORD_VALIDITY_DAYS}天`); - -// ================ 实际使用的授权配置 ================ -const AUTH_CONFIG = { - expiryDate: new Date('2024-12-31'), - validityDays: PASSWORD_VALIDITY_DAYS -}; - -// 默认设置 -const defaultSettings = { - enabled: true, - activated: false, - apiUrl: 'http://localhost:5001/v1', - apiKey: '', - model: 'deepseek-r1-250528', - maxTokens: 12000, - temperature: 1.2, - contextMessages: 2, - systemPrompt: '', - mainPrompt: '', - showOptimizationToast: true, - suppressToast: false, -}; - -// 授权状态变量 -window.pluginAuthStatus = { - authorized: false, - expired: false -}; - -// ============= 新增函数: 获取模型列表 ============= -async function fetchSupportedModels() { - const settings = extension_settings[extensionName]; - - if (!settings.apiUrl) { - toastr.error('请先配置API URL', '获取模型失败'); - return []; - } - - if (isFetchingModels) { - toastr.info('正在获取模型列表,请稍候...', '获取模型'); - return; - } - - isFetchingModels = true; - try { - const originalButtonText = $('#amily2_refresh_models').html(); - - $('#amily2_refresh_models') - .prop('disabled', true) - .html(' 加载中'); - - let modelListUrl = settings.apiUrl; - if (!modelListUrl.endsWith('/v1/models')) { - if (modelListUrl.endsWith('/')) { - modelListUrl += 'v1/models'; - } else { - modelListUrl += '/v1/models'; - } - } - - const headers = { - 'Content-Type': 'application/json' - }; - - if (settings.apiKey) { - headers['Authorization'] = `Bearer ${settings.apiKey}`; - } - - console.log('发送模型列表请求到:', modelListUrl); - - const response = await fetch(modelListUrl, { - method: 'GET', - headers: headers - }); - - if (!response.ok) { - throw new Error(`API返回错误: ${response.status} ${response.statusText}`); - } - - const data = await response.json(); - let models = []; - - if (Array.isArray(data)) { - models = data.map(m => m.id || m); - } else if (data.data && Array.isArray(data.data)) { - models = data.data.map(m => m.id); - } else if (data.models && Array.isArray(data.models)) { - models = data.models; - } else { - throw new Error('未知的模型列表格式'); - } - - availableModels = models.filter(m => - !m.includes('embed') && - !m.includes('search') && - !m.includes('similarity') && - !m.includes('audio') - ); - - availableModels.sort(); - - console.log(`获取模型列表成功 (${availableModels.length}个):`, availableModels); - toastr.success(`成功获取 ${availableModels.length} 个可用模型`, '模型加载完成'); - - return availableModels; - } catch (error) { - console.error('获取模型列表失败:', error); - toastr.error(`获取模型失败: ${error.message}`, '错误'); - return []; - } finally { - isFetchingModels = false; - - $('#amily2_refresh_models') - .prop('disabled', false) - .html(' 刷新模型'); - } -} - -// ============= 新增函数: 填充模型下拉菜单 ============= -function populateModelDropdown() { - const modelSelect = $('#amily2_model'); - const modelNotes = $('#amily2_model_notes'); - - modelSelect.empty(); - - const currentModel = extension_settings[extensionName].model || ''; - - if (availableModels.length === 0) { - modelSelect.append(''); - modelNotes.html('请检查API配置后点击"刷新模型"按钮'); - return; - } - - const defaultOption = $('') - .val('') - .text('-- 选择模型 --'); - modelSelect.append(defaultOption); - - availableModels.forEach(model => { - const option = $('') - .val(model) - .text(model); - - if (model === currentModel) { - option.attr('selected', 'selected'); - } - - modelSelect.append(option); - }); - - if (currentModel && modelSelect.val() === currentModel) { - modelNotes.html(`已选择: ${currentModel}`); - } else { - modelNotes.html(`已加载 ${availableModels.length} 个可用模型`); - } -} - -// 加载设置 -async function loadSettings() { - if (!extension_settings[extensionName]) { - extension_settings[extensionName] = {}; - } - - // ===== 授权过期检查 ===== - const now = new Date(); - window.pluginAuthStatus.expired = now > AUTH_CONFIG.expiryDate; - - if (window.pluginAuthStatus.expired) { - localStorage.removeItem('plugin_activated'); - localStorage.removeItem('plugin_auth_code'); - localStorage.removeItem('plugin_valid_until'); - console.log('[Amily2号] 检测到授权过期,已清理本地存储'); - } - - // 合并默认设置 - extension_settings[extensionName] = { - ...defaultSettings, - ...extension_settings[extensionName] - }; - - // 检查授权状态 - window.pluginAuthStatus.authorized = await checkAuthorization(); - - // 更新UI - updateUI(); - - // 自动加载模型 - if (window.pluginAuthStatus.authorized && extension_settings[extensionName].apiUrl) { - const cachedModels = localStorage.getItem('amily2_cached_models'); - if (cachedModels) { - availableModels = JSON.parse(cachedModels); - console.log('从缓存加载模型列表:', availableModels.length); - populateModelDropdown(); - } - - setTimeout(() => { - if (availableModels.length === 0) { - toastr.info('正在自动加载模型列表...', '模型初始化'); - $('#amily2_refresh_models').click(); - } - }, 1500); - } - - $('#amily2_api_url').on('input', function() { - localStorage.removeItem('amily2_cached_models'); - availableModels = []; - populateModelDropdown(); - }); -} - -// 检查授权状态(无UI更新) -function checkAuthorization() { - const now = new Date(); - window.pluginAuthStatus.expired = now > AUTH_CONFIG.expiryDate; - - const activated = localStorage.getItem('plugin_activated') === 'true'; - const savedAuthCode = localStorage.getItem('plugin_auth_code'); - const validUntil = localStorage.getItem('plugin_valid_until'); - - let withinValidityPeriod = false; - - if (validUntil) { - const validUntilDate = new Date(validUntil); - withinValidityPeriod = now <= validUntilDate; - console.log(`[Amily2号] 授权有效期检查: - 当前时间: ${now.toISOString()} - 授权有效期至: ${validUntilDate.toISOString()} - 是否在有效期内: ${withinValidityPeriod}`); - } - - let passwordMatches = false; - if (savedAuthCode) { - const today = new Date(); - for (let i = 0; i < AUTH_CONFIG.validityDays; i++) { - const checkDate = new Date(); - checkDate.setDate(today.getDate() - i); - const passwordForDay = getPasswordForDate(checkDate); - - if (savedAuthCode === passwordForDay) { - passwordMatches = true; - console.log(`[Amily2号] 密码匹配: ${savedAuthCode} 对应第${i+1}天前`); - break; - } - } - } - - window.pluginAuthStatus.authorized = activated && - !window.pluginAuthStatus.expired && - passwordMatches && - withinValidityPeriod; - - return window.pluginAuthStatus.authorized; -} - -// 激活授权 -async function activatePluginAuthorization(authCode) { - let isValidCode = false; - const today = new Date(); - - for (let i = 0; i < AUTH_CONFIG.validityDays; i++) { - const checkDate = new Date(); - checkDate.setDate(today.getDate() - i); - const passwordForDay = getPasswordForDate(checkDate); - - if (authCode === passwordForDay) { - isValidCode = true; - console.log(`[Amily2号] 输入的密码匹配第${i+1}天前的有效密码`); - break; - } - } - - if (!isValidCode) { - toastr.error('授权码无效', '激活失败'); - return false; - } - - const now = new Date(); - if (now > AUTH_CONFIG.expiryDate) { - toastr.error('授权已过期', '激活失败'); - return false; - } - - const validUntil = new Date(); - validUntil.setDate(now.getDate() + AUTH_CONFIG.validityDays); - - localStorage.setItem('plugin_valid_until', validUntil.toISOString()); - localStorage.setItem('plugin_auth_code', authCode); - localStorage.setItem('plugin_activated', 'true'); - - toastr.success(`授权激活成功,有效期至 ${validUntil.toLocaleDateString()}`, 'Amily2号启用'); - window.pluginAuthStatus.authorized = true; - - $('#auth_panel').hide(); - $('.plugin-features').show(); - - extension_settings[extensionName].enabled = true; - saveSettings(); - - return true; -} - -// 显示过期信息 -function displayExpiryInfo() { - const now = new Date(); - const daysLeft = Math.ceil((AUTH_CONFIG.expiryDate - now) / (1000 * 60 * 60 * 24)); - const validUntil = localStorage.getItem('plugin_valid_until'); - - if (window.pluginAuthStatus.expired) { - return '
授权已过期
'; - } else { - let validUntilHtml = ''; - if (validUntil) { - const validUntilDate = new Date(validUntil); - validUntilHtml = `当前授权有效期至: ${validUntilDate.toLocaleDateString()}`; - } - - return ` -
- 授权有效期: ${daysLeft}天 - 有效期至: ${AUTH_CONFIG.expiryDate.toLocaleDateString()} - ${validUntilHtml} -
- `; - } -} - -// ============= 配置验证函数 ============= -function validateSettings() { - const settings = extension_settings[extensionName] || {}; - const errors = []; - - if (!settings.apiUrl) { - errors.push('API URL未配置'); - } else if (!/^https?:\/\//.test(settings.apiUrl)) { - errors.push('API URL必须以http://或https://开头'); - } - - if (settings.apiKey) { - if (settings.apiKey.length < 8) { - errors.push('API密钥太短(至少8位)'); - } - if (/(key|secret|password)/i.test(settings.apiKey)) { - toastr.warning('请注意:API Key包含敏感关键词("key", "secret", "password")', '安全提醒', { timeOut: 5000 }); - } - } - - if (!settings.model) { - errors.push('未选择模型'); - } - - if (settings.maxTokens < 100 || settings.maxTokens > 20000) { - errors.push(`Token数超限 (${settings.maxTokens}) - 必须在100-20000之间`); - } - - return errors.length ? errors : null; -} - -// ============= 统一的设置项事件处理 ============= -function saveSettings() { - if (!window.pluginAuthStatus.authorized) return false; // 确保在未授权时不保存 - - const validationErrors = validateSettings(); - - if (validationErrors) { - const errorHtml = validationErrors.map(err => `
❌ ${err}
`).join(''); - toastr.error(`配置存在错误:${errorHtml}`, '设置未保存', { - timeOut: 8000, - extendedTimeOut: 0, - preventDuplicates: true - }); - return false; - } - - saveSettingsDebounced(); - return true; -} - -// 统一处理所有设置项变更 -$('[id^="amily2_"]').on('change', function() { - if (!window.pluginAuthStatus.authorized) return; - - // 获取设置名称(从ID转换) - const settingName = this.id.replace('amily2_', ''); - - // 根据控件类型获取值 - let value; - if ($(this).is(':checkbox')) { - value = $(this).prop('checked'); - } - else if (settingName === 'max_tokens' || settingName === 'context_messages') { - value = parseInt($(this).val()); - } - else if (settingName === 'temperature') { - value = parseFloat($(this).val()); - } - else { - value = $(this).val(); - } - - // 更新设置 - extension_settings[extensionName][settingName] = value; - - // 更新显示值(如果适用) - if (settingName === 'enabled') { - extension_settings[extensionName].enabled = value; - } - else if (settingName === 'max_tokens') { - $('#amily2_max_tokens_value').text(value); - } - else if (settingName === 'temperature') { - $('#amily2_temperature_value').text(value); - } - else if (settingName === 'context_messages') { - $('#amily2_context_messages_value').text(value); - } - else if (settingName === 'show_toast') { - extension_settings[extensionName].suppressToast = false; - } - - console.log(`[Amily2设置] ${settingName} 更新为:`, value); - - // 尝试保存,保存失败则恢复原值 - if (!saveSettings()) { - // 恢复原始值 - const originalValue = defaultSettings[settingName]; - - if ($(this).is(':checkbox')) { - $(this).prop('checked', originalValue); - } - else { - $(this).val(originalValue); - } - - // 特殊控件恢复 - if (settingName === 'max_tokens') { - $('#amily2_max_tokens_value').text(originalValue); - } - else if (settingName === 'temperature') { - $('#amily2_temperature_value').text(originalValue); - } - else if (settingName === 'context_messages') { - $('#amily2_context_messages_value').text(originalValue); - } - } -}); - -// ============= 更新UI ============= -function updateUI() { - const authStatus = window.pluginAuthStatus; - - if (!authStatus.authorized) { - $('#amily2_enabled').prop('checked', false); - $('#amily2_enabled').prop('disabled', true); - $('[id^="amily2_"]').not('#auth_input, #auth_submit').prop('disabled', true); - toastr.warning('插件未授权,功能已禁用', 'Amily2号'); - } else { - const settings = extension_settings[extensionName]; - $('#amily2_enabled').prop('disabled', false); - $('[id^="amily2_"]').prop('disabled', false); - - $('#amily2_enabled').prop('checked', settings.enabled); - $('#amily2_api_url').val(settings.apiUrl); - $('#amily2_api_key').val(settings.apiKey); - - populateModelDropdown(); - - $('#amily2_max_tokens').val(settings.maxTokens); - $('#amily2_max_tokens_value').text(settings.maxTokens); - $('#amily2_temperature').val(settings.temperature); - $('#amily2_temperature_value').text(settings.temperature); - $('#amily2_context_messages').val(settings.contextMessages); - $('#amily2_context_messages_value').text(settings.contextMessages); - $('#amily2_main_prompt').val(settings.mainPrompt); - $('#amily2_system_prompt').val(settings.systemPrompt); - - if ($('#amily2_show_toast').length) { - $('#amily2_show_toast').prop('checked', settings.showOptimizationToast); - extension_settings[extensionName].suppressToast = settings.suppressToast; - } - } -} - -// 检查最新消息 -async function checkLatestMessage() { - const context = getContext(); - const chat = context.chat || []; - - if (!chat || chat.length === 0) { - console.log('[聊天回复检查器] 没有聊天记录'); - return { message: null, previousMessages: [] }; - } - - const latestMessage = chat[chat.length - 1]; - - console.log('[聊天回复检查器] 检查消息:', { - isUser: latestMessage.is_user, - messageLength: latestMessage.mes?.length, - messagePreview: latestMessage.mes?.substring(0, 50) + '...' - }); - - if (latestMessage.is_user) { - console.log('[聊天回复检查器] 跳过用户消息'); - return { message: latestMessage, previousMessages: [] }; - } - - // 获取上下文消息(根据用户设置) - const settings = extension_settings[extensionName]; - const contextCount = settings.contextMessages || 2; - const startIndex = Math.max(0, chat.length - contextCount - 1); - const previousMessages = chat.slice(startIndex, chat.length - 1); - - console.log('[聊天回复检查器] 上下文设置:', { - contextMessages: settings.contextMessages, - contextCount: contextCount, - chatLength: chat.length, - startIndex: startIndex, - previousMessagesCount: previousMessages.length - }); - - console.log('[聊天回复检查器] 获取上下文消息:', { - previousMessages: previousMessages.length, - startIndex: startIndex - }); - - return { message: latestMessage, previousMessages }; -} - -// 使用API检查和修复消息(添加防止无限重试) -async function checkAndFixWithAPI(latestMessage, previousMessages, isRetry = false, retryCount = 0) { - const settings = extension_settings[extensionName]; - - if (!settings.apiUrl) { - console.error('[聊天回复检查器] 未配置API URL'); - return null; - } - - // 优先使用主要提示词,如果为空则使用系统提示词 - const usePrompt = settings.mainPrompt || settings.systemPrompt; - - if (!usePrompt) { - console.error('[聊天回复检查器] 未配置主要或系统提示词'); - toastr.error('请配置主要提示词或系统提示词', '聊天回复检查器'); - return null; - } - - // 构建检查内容 - let checkContent = `请检查并优化以下文本:\n\n"${latestMessage.mes}"\n\n`; - - // 始终提供上下文参考(让AI自主判断是否需要考虑) - if (previousMessages.length > 0) { - checkContent += '上下文参考:\n'; - const recentMessages = previousMessages.slice(-2); - recentMessages.forEach((msg, index) => { - const speaker = msg.is_user ? '用户' : 'AI'; - checkContent += `${speaker}: "${msg.mes}"\n`; - }); - checkContent += '\n'; - } - - checkContent += '请按照系统提示的格式分析并回复。'; - - // 构建请求消息(使用优先的提示词) - const messages = [ - { - role: 'system', - content: usePrompt - }, - { - role: 'user', - content: checkContent - } - ]; - - try { - // 确保URL格式正确 - let apiUrl = settings.apiUrl; - // 针对不同API提供商处理URL - if (apiUrl.includes('ark.cn-beijing.volces.com')) { - // 火山引擎 ARK API - if (!apiUrl.endsWith('/completion')) { - apiUrl = apiUrl.replace(/\/completion$/, ''); - if (apiUrl.endsWith('/')) { - apiUrl += 'completion'; - } else { - apiUrl += '/completion'; - } - } - } else if (!apiUrl.endsWith('/chat/completions')) { - // 标准 OpenAI 格式 - if (apiUrl.endsWith('/v1')) { - apiUrl = apiUrl + '/chat/completions'; - } else if (apiUrl.endsWith('/')) { - apiUrl = apiUrl + 'v1/chat/completions'; - } else { - apiUrl = apiUrl + '/v1/chat/completions'; - } - } - - const requestBody = { - model: settings.model, - messages: messages, - max_tokens: settings.maxTokens, - temperature: settings.temperature, - stream: false - }; - - console.log('[聊天回复检查器] API请求:', { - url: apiUrl, - model: settings.model, - messagesCount: messages.length, - isRetry: isRetry, - retryCount: retryCount - }); - - const headers = { - 'Content-Type': 'application/json' - }; - - // 只有在有API Key时才添加Authorization头 - if (settings.apiKey) { - headers['Authorization'] = `Bearer ${settings.apiKey}`; - } - - const response = await fetch(apiUrl, { - method: 'POST', - headers: headers, - body: JSON.stringify(requestBody) - }); - - if (!response.ok) { - const errorText = await response.text(); - console.error('[聊天回复检查器] API请求失败详情:', { - status: response.status, - statusText: response.statusText, - headers: Object.fromEntries(response.headers.entries()), - errorBody: errorText - }); - throw new Error(`API请求失败: ${response.status} ${response.statusText} - ${errorText}`); - } - - const data = await response.json(); - const apiResponse = data.choices?.[0]?.message?.content; - - if (!apiResponse) { - console.error('[聊天回复检查器] API响应格式错误:', data); - throw new Error('API返回的消息为空'); - } - - console.log('[聊天回复检查器] API返回内容:', apiResponse); - - // 检查API是否返回错误信息 - if (apiResponse.includes('无法生成回复') || - apiResponse.includes('请尝试修改') || - apiResponse.includes('内容过滤') || - apiResponse.includes('违反政策')) { - console.log('[聊天回复检查器] API返回错误信息', apiResponse); - - // 添加重试限制:最多4次 - if (!isRetry && retryCount < 4) { - const nextRetryCount = retryCount + 1; - console.log(`[聊天回复检查器] API错误,开始第${nextRetryCount}次重试...`); - toastr.info(`API优化失败,正在重试 (${nextRetryCount}/4)`, '聊天回复检查器'); - // 增加延迟(随重试次数增加) - await new Promise(resolve => setTimeout(resolve, 1000 * nextRetryCount)); - return await checkAndFixWithAPI(latestMessage, previousMessages, true, nextRetryCount); - } else { - console.log('[聊天回复检查器] API重试次数已达上限,放弃优化'); - toastr.warning('API优化失败已达到最大重试次数', '聊天回复检查器'); - return null; - } - } - - const hasThinkTag = apiResponse.includes('think'); - const hasContentTag = apiResponse.includes('content'); - - if (has888Tag && has666Tag) { - // 匹配think标签 - const thinkMatch = apiResponse.match(/([\s\S]*?)<\/think>/); - // 匹配content标签 - const contentMatch = apiResponse.match(/([\s\S]*?)<\/content>/); - - if (!thinkMatch) { - console.log('[聊天回复检查器] API响应格式错误,未找到888标签'); - return null; - } - - const thinkContent = thinkMatch[1].trim(); - const fixedContent = contentMatch ? contentMatch[1].trim() : ''; - - console.log('[聊天回复检查器] 分析结果:', thinkContent); - console.log('[聊天回复检查器] 修复内容:', fixedContent); - - // 在界面显示分析结果(带"不再显示"选项) - const settings = extension_settings[extensionName]; - if (thinkContent && settings.showOptimizationToast && !settings.suppressToast) { - // 构建弹窗内容 - const toastContent = ` -
${thinkContent.substring(0, 100)}${thinkContent.length > 100 ? "..." : ""}
-
- -
- `; - - // 显示带选项的弹窗 - const toast = toastr.info(toastContent, 'AI优化分析', { - timeOut: 0, // 不会自动关闭 - extendedTimeOut: 0, - preventDuplicates: true, - closeButton: true, - tapToDismiss: false, - onclick: null, - onShown: function() { - // 绑定"不再显示"复选框的事件 - $('#amily2_dont_show_again').on('change', function() { - if (this.checked) { - // 更新设置 - extension_settings[extensionName].suppressToast = true; - saveSettings(); - toastr.remove(toast); - toastr.success('已隐藏优化通知', '设置更新'); - } - }); - } - }); - } - - // 如果修复内容为空,则不需要修复 - if (!fixedContent) { - console.log('[聊天回复检查器] API判定:不需要优化'); - return null; - } - - console.log('[聊天回复检查器] API判定:需要优化'); - // 返回完整的API响应,包含标签 - return apiResponse; - } else { - // 如果没有标签格式 - console.log('[聊天回复检查器] API返回普通文本格式'); - - // 如果返回"无需改进"类似内容,则不修复 - if (apiResponse.includes('无需改进') || - apiResponse.includes('不需要改进') || - apiResponse.includes('质量良好') || - apiResponse.includes('没有问题')) { - console.log('[聊天回复检查器] API判定:不需要优化'); - return null; - } - - console.log('[聊天回复检查器] API判定:需要优化'); - return apiResponse; - } - } catch (error) { - console.error('[聊天回复检查器] API调用出错:', error); - toastr.error(`API调用失败: ${error.message}`, '聊天回复检查器', {timeOut: 8000}); - - return null; - } -} - -// 存储已处理的消息,防止循环修复 -const processedMessages = new Set(); - -// 处理消息接收事件(在渲染前拦截) -async function onMessageReceived(data) { - console.log('[聊天回复检查器] 消息接收事件触发:', { data, eventType: 'onMessageReceived' }); - - const settings = extension_settings[extensionName]; - - console.log('[聊天回复检查器] 当前设置:', { - enabled: settings.enabled, - hasApiUrl: !!settings.apiUrl, - apiUrl: settings.apiUrl - }); - - if (!settings.enabled) { - console.log('[聊天回复检查器] 插件未启用,跳过检查'); - return; - } - - if (!settings.apiUrl) { - console.log('[聊天回复检查器] 未配置API URL,跳过检查'); - return; - } - - const context = getContext(); - const chat = context.chat; - - if (!chat || chat.length === 0) { - console.log('[聊天回复检查器] 没有聊天记录'); - return; - } - - const latestMessage = chat[chat.length - 1]; - - // 只处理AI的回复 - if (latestMessage.is_user) { - console.log('[聊天回复检查器] 跳过用户消息'); - return; - } - - // 跳过第一条消息(通常是系统消息或角色介绍) - if (chat.length <= 1) { - console.log('[聊天回复检查器] 跳过第一条消息'); - return; - } - - // 跳过过短的消息(可能是系统消息) - if (latestMessage.mes.length < 10) { - console.log('[聊天回复检查器] 跳过过短的消息'); - return; - } - - // 防循环检查:为消息生成唯一标识 - const messageKey = `${chat.length}-${latestMessage.mes.substring(0, 50)}`; - - if (processedMessages.has(messageKey)) { - console.log('[聊天回复检查器] 消息已处理过,跳过检查避免循环'); - return; - } - - // 标记消息为已处理 - processedMessages.add(messageKey); - - // 清理过期的标记(保留最近10条) - if (processedMessages.size > 50) { - const entries = Array.from(processedMessages); - processedMessages.clear(); - entries.slice(-50).forEach(id => processedMessages.add(id)); -} - - // 获取上下文消息 - const contextCount = settings.contextMessages || 2; - const startIndex = Math.max(0, chat.length - 1 - contextCount); - const previousMessages = chat.slice(startIndex, chat.length - 1); - - console.log('[聊天回复检查器] 开始检查生成的回复...'); - - // 使用API检查和修复(添加初始重试次数0) - const fixedMessage = await checkAndFixWithAPI(latestMessage, previousMessages, false, 0); - - if (fixedMessage && fixedMessage !== latestMessage.mes) { - console.log('[聊天回复检查器] 内容已优化,显示优化版本'); - - // 直接修改消息内容,不需要重新加载 - latestMessage.mes = fixedMessage; - - console.log('[聊天回复检查器] 回复已在显示前优化'); - } else { - console.log('[聊天回复检查器] 内容无需优化,正常显示'); - } -} - -// 手动检查命令(使用API检查) -async function checkCommand() { - const settings = extension_settings[extensionName]; - if (!settings.apiUrl) { - toastr.error('请先配置API URL', '聊天回复检查器'); - return ''; - } - - const checkResult = await checkLatestMessage(); - - if (!checkResult.message) { - toastr.info('没有可检查的消息', '聊天回复检查器'); - return ''; - } - - if (checkResult.message.is_user) { - toastr.info('最新消息是用户消息,无需检查', '聊天回复检查器'); - return ''; - } - - toastr.info('正在使用API检查回复...', '聊天回复检查器'); - - // 添加重试初始参数 - const fixedMessage = await checkAndFixWithAPI(checkResult.message, checkResult.previousMessages, false, 0); - if (fixedMessage && fixedMessage !== checkResult.message.mes) { - toastr.warning('检测到问题,建议使用修复功能', '聊天回复检查器'); - } else { - toastr.success('未检测到问题', '聊天回复检查器'); - } - - return ''; -} - -// 手动修复命令 -async function fixCommand() { - const settings = extension_settings[extensionName]; - if (!settings.apiUrl) { - toastr.error('请先配置API URL', '聊天回复检查器'); - return ''; - } - - const context = getContext(); - const chat = context.chat; - - if (!chat || chat.length === 0) { - toastr.info('没有可修复的消息', '聊天回复检查器'); - return ''; - } - - const latestMessage = chat[chat.length - 1]; - - if (latestMessage.is_user) { - toastr.info('最新消息是用户消息,无需修复', '聊天回复检查器'); - return ''; - } - - // 获取上下文消息 - const contextCount = settings.contextMessages || 2; - const startIndex = Math.max(0, chat.length - 1 - contextCount); - const previousMessages = chat.slice(startIndex, chat.length - 1); - - toastr.info('正在检查并修复回复...', '聊天回复检查器'); - - // 添加重试初始参数 - const fixedMessage = await checkAndFixWithAPI(latestMessage, previousMessages, false, 0); - - if (fixedMessage && fixedMessage !== latestMessage.mes) { - latestMessage.mes = fixedMessage; - await saveChatConditional(); - await reloadCurrentChat(); - toastr.success('回复已修复', '聊天回复检查器'); - } else { - toastr.info('未检测到需要修复的问题', '聊天回复检查器'); - } - - return ''; -} - -// 测试命令(使用API测试) -async function testReplyChecker() { - const settings = extension_settings[extensionName]; - if (!settings.apiUrl) { - toastr.error('请先配置API URL', '聊天回复检查器'); - return ''; - } - - const context = getContext(); - const chat = context.chat; - - if (!chat || chat.length < 2) { - toastr.warning('需要至少2条消息才能测试', '聊天回复检查器'); - return ''; - } - // 获取倒数第二条AI消息 - let testMessage = null; - for (let i = chat.length - 2; i >= 0; i--) { - if (!chat[i].is_user) { - testMessage = chat[i].mes; - break; - } - } - - if (!testMessage) { - toastr.warning('没有找到可用于测试的AI消息', '聊天回复检查器'); - return ''; - } - - const lastMessage = chat[chat.length - 1]; - - if (lastMessage.is_user) { - toastr.warning('最后一条消息是用户消息,无法测试', '聊天回复检查器'); - return ''; - } - - // 临时修改最后一条消息来模拟重复 - const originalMessage = lastMessage.mes; - lastMessage.mes = testMessage + '\n\n' + testMessage; - - toastr.info('正在使用API测试检测功能...', '聊天回复检查器'); - - // 获取上下文消息 - const contextCount = settings.contextMessages || 2; - const startIndex = Math.max(0, chat.length - contextCount - 1); - const previousMessages = chat.slice(startIndex, chat.length - 1); - - // 使用API检查(添加重试初始参数) - const fixedMessage = await checkAndFixWithAPI(lastMessage, previousMessages, false, 0); - - // 恢复原始消息 - lastMessage.mes = originalMessage; - if (fixedMessage && fixedMessage !== (testMessage + '\n\n' + testMessage)) { - toastr.success('测试成功!API检测到重复内容并提供了修复建议', '聊天回复检查器'); - } else { - toastr.warning('测试结果:API未检测到问题,请检查API配置或提示词', '聊天回复检查器'); - } - - return ''; -} - -jQuery(async () => { - // 加载设置 - await loadSettings(); - - // 添加设置面板HTML - const settingsHtml = await $.get(`${extensionFolderPath}/settings.html`); - $('#extensions_settings2').append(settingsHtml); - $('#expiry_info').html(displayExpiryInfo()); - - // 添加华丽的CSS样式 - addAuthStyles(); - - // 注册激活按钮事件 - $('#auth_submit').on('click', async function() { - const authCode = $('#auth_input').val().trim(); - if (!authCode) { - toastr.error('请输入授权码', '验证失败'); - return; - } - - const success = await activatePluginAuthorization(authCode); - if (success) { - // 隐藏授权面板,显示功能面板 - $('#auth_panel').slideUp(400); - setTimeout(() => { - $('.plugin-features').slideDown(400); - }, 400); - } - }); - - // ============= 模型下拉菜单事件 ============= - // 在API URL输入框的change事件中添加 - $('#amily2_api_url').on('change', function() { - const url = $(this).val(); - if (url && !/^https?:\/\//.test(url)) { - $(this).css('border', '2px solid #ff5252'); - toastr.error('API URL必须以http://或https://开头'); - } else { - $(this).css('border', ''); - } - }); - // 在Token输入框的change事件中添加 - $('#amily2_max_tokens').on('change', function() { - const tokens = parseInt($(this).val()); - if (tokens < 100 || tokens > 20000) { - $(this).siblings('label').css('color', '#ff5252'); - } else { - $(this).siblings('label').css('color', ''); - } - }); - $('#amily2_model').on('change', function() { - const selectedModel = $(this).val(); - extension_settings[extensionName].model = selectedModel; - saveSettings(); - - // 更新状态信息 - if (selectedModel && selectedModel.length > 0) { - $('#amily2_model_notes').html(`已选择模型: ${selectedModel}`); - } else { - $('#amily2_model_notes').html('请选择一个模型'); - } - }); - - // ============= 刷新模型按钮事件 ============= - $('#amily2_refresh_models').on('click', async function() { - // 添加视觉反馈 - 按钮动画 - $(this).addClass('pulse'); - setTimeout(() => $(this).removeClass('pulse'), 500); - - // 获取模型列表 - await fetchSupportedModels(); - - // 填充下拉菜单 - populateModelDropdown(); - - // 缓存模型列表 - if (availableModels.length > 0) { - localStorage.setItem('amily2_cached_models', JSON.stringify(availableModels)); - } - }); - - // 绑定设置事件(所有其他设置项) - $('#amily2_enabled').on('change', function() { - if (!window.pluginAuthStatus.authorized) return; - - extension_settings[extensionName].enabled = $(this).prop('checked'); - saveSettings(); - }); - - $('#amily2_api_url').on('input', function() { - extension_settings[extensionName].apiUrl = String($(this).val()); - saveSettings(); - }); - - $('#amily2_api_key').on('input', function() { - extension_settings[extensionName].apiKey = String($(this).val()); - saveSettings(); - }); - - $('#amily2_max_tokens').on('input', function() { - extension_settings[extensionName].maxTokens = parseInt(String($(this).val())); - $('#amily2_max_tokens_value').text(extension_settings[extensionName].maxTokens); - saveSettings(); - }); - - $('#amily2_temperature').on('input', function() { - extension_settings[extensionName].temperature = parseFloat(String($(this).val())); - $('#amily2_temperature_value').text(extension_settings[extensionName].temperature); - saveSettings(); - }); - - $('#amily2_context_messages').on('input', function() { - const newValue = parseInt(String($(this).val()), 10); - extension_settings[extensionName].contextMessages = newValue; - $('#amily2_context_messages_value').text(newValue); - console.log('[聊天回复检查器] 上下文消息数量已更新为:', newValue); - saveSettings(); - }); - - // 新增主要提示词事件绑定 - $('#amily2_main_prompt').on('input', function() { - extension_settings[extensionName].mainPrompt = $(this).val(); - saveSettings(); - }); - - // 系统提示词事件绑定 - $('#amily2_system_prompt').on('input', function() { - extension_settings[extensionName].systemPrompt = $(this).val(); - saveSettings(); - }); - - // 新增开关状态更新 - $('#amily2_show_toast').on('change', function() { - extension_settings[extensionName].showOptimizationToast = $(this).prop('checked'); - saveSettings(); - - // 如果重新开启通知,清除抑制状态 - if ($(this).prop('checked')) { - extension_settings[extensionName].suppressToast = false; - saveSettings(); - } - }); - - // 新增重置开关 - $('#amily2_reset_toast').on('click', function() { - extension_settings[extensionName].showOptimizationToast = true; - extension_settings[extensionName].suppressToast = false; - saveSettings(); - toastr.success('通知设置已重置', 'Amily2号'); - $('#amily2_show_toast').prop('checked', true); - }); - - // 绑定测试按钮 - $('#amily2_test').on('click', checkCommand); - $('#amily2_fix_now').on('click', fixCommand); - - // 监听消息生成完成但未渲染的事件 - if (!window.amily2EventsRegistered) { - eventSource.on(event_types.MESSAGE_RECEIVED, onMessageReceived); - eventSource.on(event_types.IMPERSONATE_READY, onMessageReceived); - window.amily2EventsRegistered = true; - console.log('Amily2消息事件监听已注册'); - } - - // 注册斜杆命令 - SlashCommand.registerCommand(SlashCommand.fromProps({ - name: 'check-reply', - callback: checkCommand, - helpString: '检查最新的AI回复是否有问题', - })); - - SlashCommand.registerCommand(SlashCommand.fromProps({ - name: 'fix-reply', - callback: fixCommand, - helpString: '修复最新的AI回复中的问题', - })); - - SlashCommand.registerCommand(SlashCommand.fromProps({ - name: 'test-reply-checker', - callback: testReplyChecker, - helpString: '测试聊天回复检查器功能', - })); - - // 更新UI - updateUI(); - console.log('Amily2号优化助手已加载'); -}); - -// ============= 添加华丽CSS样式 ============= -function addAuthStyles() { - const style = document.createElement('style'); - style.textContent = ` - .flex-container { - display: flex; - gap: 10px; - margin-bottom: 10px; - align-items: center; - } - - #amily2_model { - flex: 1; - height: 42px; - padding: 0 15px; - background: rgba(50, 50, 75, 0.5); - border: 1px solid rgba(255,255,255,0.15); - color: white; - border-radius: 8px; - font-size: 0.95rem; - appearance: auto; - outline: none; - transition: all 0.3s; - } - - #amily2_model:focus { - border-color: #4CAF50; - box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.3); - } - - #amily2_refresh_models { - height: 42px; - padding: 0 15px; - display: flex; - align-items: center; - background: linear-gradient(to right, #4CAF50, #8BC34A); - color: white; - border: none; - border-radius: 8px; - cursor: pointer; - font-weight: 500; - font-size: 14px; - transition: all 0.3s; - justify-content: center; - } - - #amily2_refresh_models:hover { - background: linear-gradient(to right, #43A047, #7CB342); - } - - #amily2_refresh_models:disabled { - background: #9E9E9E; - cursor: not-allowed; - opacity: 0.7; - } - - /* 按钮脉冲动画 */ - .pulse { - animation: pulseAnimation 0.6s ease; - } - - @keyframes pulseAnimation { - 0% { box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.7); } - 70% { box-shadow: 0 0 0 10px rgba(76, 175, 80, 0); } - 100% { box-shadow: 0 0 0 0 rgba(76, 175, 80, 0); } - } - - /* 旋转动画 */ - .fa-spinner { - animation: spin 1.5s linear infinite; - } - - @keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } - } - - #amily2_model_notes { - margin-top: 8px; - font-size: 0.85em; - color: #aaa; - min-height: 1.2em; - } - - #auth_panel { - background: linear-gradient(135deg, #1a237e, #4a148c); - padding: 20px; - border-radius: 12px; - margin-bottom: 20px; - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); - color: #ffffff; - position: relative; - overflow: hidden; - transform: perspective(1000px) rotateX(5deg); - transition: all 0.5s ease; - } - - #auth_panel:before { - content: ''; - position: absolute; - top: -50%; - left: -50%; - width: 200%; - height: 200%; - background: radial-gradient(circle at center, rgba(255,255,255,0.1) 0%, transparent 70%); - animation: rotate 20s linear infinite; - } - - .auth-header { - position: relative; - z-index: 2; - text-align: center; - margin-bottom: 20px; - } - .auth-title { - font-size: 1.8rem; - font-weight: 700; - margin-bottom: 5px; - background: linear-gradient(to right, #ff9800, #ff5722); - -webkit-background-clip: text; - background-clip: text; - color: transparent; - text-shadow: 0 2px 5px rgba(0,0,0,0.2); - } - .auth-subtitle { - font-size: 1rem; - color: #e0e0e0; - margin-bottom: 15px; - } - .auth-code-input { - position: relative; - z-index: 2; - display: flex; - gap: 10px; - margin-bottom: 15px; - } - #auth_input { - flex: 1; - padding: 15px; - border-radius: 8px; - background: rgba(255, 255, 255, 0.15); - border: 1px solid rgba(255, 255, 255, 0.2); - color: white; - font-size: 1.1rem; - backdrop-filter: blur(5px); - box-shadow: 0 4px 10px rgba(0,0,0,0.1); - transition: all 0.3s ease; - } - - #auth_input:focus { - background: rgba(255, 255, 255, 0.25); - border-color: #ff9800; - outline: none; - box-shadow: 0 4px 15px rgba(255,152,0,0.3); - } - #auth_submit { - background: linear-gradient(to right, #ff9800, #ff5722); - color: white; - border: none; - border-radius: 8px; - padding: 0 25px; - cursor: pointer; - font-weight: 600; - box-shadow: 0 4px 10px rgba(255,152,0,0.4); - transition: all 0.3s ease; - } - - #auth_submit:hover { - transform: translateY(-2px); - box-shadow: 0 6px 15px rgba(255,152,0,0.5); - } - - .auth-footer { - position: relative; - z-index: 2; - text-align: center; - margin-top: 15px; - font-size: 0.9rem; - color: #bdbdbd; - } - - .auth-status { - background: rgba(0, 0, 0, 0.3); - padding: 8px 15px; - border-radius: 6px; - font-weight: 500; - margin-bottom: 15px; - display: flex; - align-items: center; - gap: 10px; - } - - .valid { - color: #76ff03; - border-left: 3px solid #76ff03; - } - - .expired { - color: #ff5252; - border-left: 3px solid #ff5252; - } - - .plugin-features { - display: none; - background: rgba(30,30,46,0.8); - backdrop-filter: blur(10px); - border-radius: 12px; - padding: 20px; - box-shadow: 0 10px 25px rgba(0,0,0,0.3); - margin-bottom: 20px; - } - - @keyframes rotate { - from { transform: rotate(0deg); } - to { transform: rotate(360deg); } - } - `; - document.head.appendChild(style); -}