您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在AI对话网站中增强用户输入,使用飞书多维表格作为知识库存储和检索方法论。v1.2.9: 简化代码逻辑,移除复杂验证,优化用户体验
// ==UserScript== // @name AI提示词增强器 - 飞书知识库版 // @namespace http://tampermonkey.net/ // @version 1.2.9 // @description 在AI对话网站中增强用户输入,使用飞书多维表格作为知识库存储和检索方法论。v1.2.9: 简化代码逻辑,移除复杂验证,优化用户体验 // @author AI Assistant // @license MIT // @match https://chat.deepseek.com/* // @match https://kimi.moonshot.cn/* // @match https://www.kimi.com/* // @match https://kimi.com/* // @match https://www.doubao.com/chat/* // @match http://*/* // @match https://*/* // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @grant GM_registerMenuCommand // @connect open.feishu.cn // @connect api.moonshot.cn // @connect api.deepseek.com // @connect * // @require https://cdn.jsdelivr.net/npm/sweetalert2@11 // ==/UserScript== (function() { 'use strict'; // ==================== 配置和常量 ==================== // 网站类型枚举 const SiteType = { DEEPSEEK: 'deepseek', KIMI: 'kimi', DOUBAO: 'doubao', UNKNOWN: 'unknown' }; // 按钮状态枚举 const ButtonState = { NORMAL: 'normal', LOADING: 'loading', SUCCESS: 'success', ERROR: 'error' }; // 飞书多维表格配置 const FEISHU_CONFIG = { API_URL: 'https://open.feishu.cn/open-apis', // 用户需要配置的字段 get APP_ID() { return GM_getValue('feishu_app_id', ''); }, get APP_SECRET() { return GM_getValue('feishu_app_secret', ''); }, get BASE_ID() { return GM_getValue('feishu_base_id', ''); }, get TABLE_ID() { return GM_getValue('feishu_table_id', ''); } }; // AI模型配置 const AI_CONFIG = { get MODEL_NAME() { return GM_getValue('ai_model_name', 'moonshot-v1-8k'); }, get BASE_URL() { return GM_getValue('ai_base_url', 'https://api.moonshot.cn/v1'); }, get API_KEY() { return GM_getValue('ai_api_key', ''); } }; // 网站配置 const SITE_CONFIGS = { [SiteType.DEEPSEEK]: { type: SiteType.DEEPSEEK, name: 'DeepSeek', inputSelector: '#chat-input, textarea[placeholder*="输入"], textarea[placeholder*="问题"], .chat-input textarea, textarea', inputType: 'textarea', buttonContainerSelector: '.ec4f5d61, .chat-input-container, .input-container, .toolbar, .chat-toolbar, [class*="toolbar"], [class*="input-container"], [class*="container"]', sendButtonSelector: '.bcc55ca1, [data-testid*="send"], button[type="submit"]', isEnabled: true }, [SiteType.KIMI]: { type: SiteType.KIMI, name: 'Kimi', inputSelector: '.chat-input-editor', inputType: 'contenteditable', buttonContainerSelector: '.left-area', sendButtonSelector: '.send-button-container', isEnabled: true }, [SiteType.DOUBAO]: { type: SiteType.DOUBAO, name: '豆包', inputSelector: '[data-testid="chat_input_input"]', inputType: 'textarea', buttonContainerSelector: '.left-tools-wrapper-INTHKl', sendButtonSelector: '[data-testid="chat_input_send_button"]', isEnabled: true } }; // 全局变量 let currentSiteType = SiteType.UNKNOWN; let enhanceButton = null; let inputHandler = null; let accessTokenCache = { token: null, expireTime: 0 }; let isInitialized = false; // ==================== 工具函数 ==================== // 检测当前网站类型 function detectSiteType() { const hostname = window.location.hostname; console.log('🔍 检测网站类型 - 当前域名:', hostname); console.log('🔍 检测网站类型 - 当前完整URL:', window.location.href); if (hostname.includes('deepseek.com')) { console.log('✅ 检测到DeepSeek网站'); return SiteType.DEEPSEEK; } else if (hostname.includes('moonshot.cn') || hostname.includes('kimi.com')) { console.log('✅ 检测到Kimi网站'); return SiteType.KIMI; } else if (hostname.includes('doubao.com')) { console.log('✅ 检测到豆包网站'); return SiteType.DOUBAO; } console.log('❌ 未识别的网站类型'); return SiteType.UNKNOWN; } // 等待元素出现 function waitForElement(selector, timeout = 10000) { return new Promise((resolve) => { const element = document.querySelector(selector); if (element) { resolve(element); return; } const observer = new MutationObserver(() => { const element = document.querySelector(selector); if (element) { observer.disconnect(); resolve(element); } }); observer.observe(document.body, { childList: true, subtree: true }); setTimeout(() => { observer.disconnect(); resolve(null); }, timeout); }); } // 判断是否为聊天页面 function isChatPage() { switch (currentSiteType) { case SiteType.DEEPSEEK: // DeepSeek网站的所有页面都可能需要增强功能,不限制特定路径 return true; case SiteType.KIMI: return true; case SiteType.DOUBAO: return window.location.pathname === '/' || window.location.pathname.includes('/chat'); default: return false; } } // 添加样式 function addStyles() { GM_addStyle(` .prompt-enhancer-button { display: inline-flex; align-items: center; gap: 6px; padding: 8px 12px; border: 1px solid rgba(0, 0, 0, 0.12); border-radius: 6px; background: #fff; color: #4c4c4c; font-size: 14px; cursor: pointer; transition: all 0.2s ease; white-space: nowrap; margin-right: 8px; } .prompt-enhancer-button:hover { background: #f5f5f5; border-color: rgba(0, 0, 0, 0.2); } .prompt-enhancer-button.pe-loading { opacity: 0.7; cursor: not-allowed; } .prompt-enhancer-button.pe-success { background: #e8f5e8; border-color: #4caf50; color: #2e7d32; } .prompt-enhancer-button.pe-error { background: #ffebee; border-color: #f44336; color: #c62828; } .pe-tooltip { position: absolute; top: -40px; left: 50%; transform: translateX(-50%); background: rgba(0, 0, 0, 0.8); color: white; padding: 6px 12px; border-radius: 4px; font-size: 12px; white-space: nowrap; z-index: 10000; pointer-events: none; } .pe-config-panel { position: fixed; top: 20px; right: 20px; width: 450px; max-width: 90vw; max-height: 90vh; background: white; border: 1px solid #ddd; border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); z-index: 10000; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; display: flex; flex-direction: column; } .pe-config-header { padding: 16px; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center; background: #f8f9fa; border-radius: 8px 8px 0 0; flex-shrink: 0; } .pe-config-content { padding: 16px; overflow-y: auto; flex: 1; min-height: 0; } .pe-config-section { margin-bottom: 20px; } .pe-config-section h3 { margin: 0 0 12px 0; font-size: 16px; color: #333; } .pe-form-group { margin-bottom: 12px; } .pe-form-group label { display: block; margin-bottom: 4px; font-size: 14px; color: #555; } .pe-form-group input { width: 100%; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; box-sizing: border-box; } .pe-button { padding: 8px 16px; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; margin-right: 8px; } .pe-button-primary { background: #007bff; color: white; } .pe-button-secondary { background: #6c757d; color: white; } .pe-close-btn { background: none; border: none; font-size: 18px; cursor: pointer; color: #666; } /* 右侧悬浮增强窗样式 */ .pe-floating-enhancer { position: fixed; top: 50%; right: 20px; transform: translateY(-50%); width: 60px; height: 60px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 50%; box-shadow: 0 4px 20px rgba(102, 126, 234, 0.4); cursor: pointer; z-index: 9999; display: flex; align-items: center; justify-content: center; color: white; font-size: 24px; transition: all 0.3s ease; user-select: none; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } .pe-floating-enhancer:hover { transform: translateY(-50%) scale(1.1); box-shadow: 0 6px 25px rgba(102, 126, 234, 0.6); } .pe-floating-enhancer.pe-loading { animation: pe-spin 1s linear infinite; } .pe-floating-enhancer.pe-success { background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); } .pe-floating-enhancer.pe-error { background: linear-gradient(135deg, #f44336 0%, #d32f2f 100%); } @keyframes pe-spin { 0% { transform: translateY(-50%) rotate(0deg); } 100% { transform: translateY(-50%) rotate(360deg); } } .pe-floating-tooltip { position: absolute; right: 70px; top: 50%; transform: translateY(-50%); background: rgba(0, 0, 0, 0.8); color: white; padding: 8px 12px; border-radius: 6px; font-size: 12px; white-space: nowrap; pointer-events: none; opacity: 0; transition: opacity 0.3s ease; } .pe-floating-enhancer:hover .pe-floating-tooltip { opacity: 1; } `); } // ==================== 飞书API相关 ==================== // 从飞书表格URL中提取Base ID和Table ID function parseFeishuUrl(url) { if (!url || !url.trim()) { throw new Error('URL不能为空'); } // 匹配Base ID (支持多种URL格式) const baseIdMatch = url.match(/\/(?:base|sheets)\/([^\/\?]+)/); // 匹配Table ID const tableIdMatch = url.match(/[?&]table=([^&]+)/); if (!baseIdMatch) { throw new Error('无法从URL中解析Base ID,请检查URL格式'); } if (!tableIdMatch) { throw new Error('无法从URL中解析Table ID,请确保URL包含table参数'); } return { baseId: baseIdMatch[1], tableId: tableIdMatch[1] }; } // 获取飞书访问令牌 function getFeishuAccessToken() { return new Promise((resolve, reject) => { // 检查缓存 const now = Date.now(); if (accessTokenCache.token && now < accessTokenCache.expireTime) { resolve(accessTokenCache.token); return; } console.log('正在获取飞书访问令牌...'); console.log('飞书配置:', { APP_ID: FEISHU_CONFIG.APP_ID ? '已配置' : '未配置', APP_SECRET: FEISHU_CONFIG.APP_SECRET ? '已配置' : '未配置', BASE_ID: FEISHU_CONFIG.BASE_ID ? '已配置' : '未配置', TABLE_ID: FEISHU_CONFIG.TABLE_ID ? '已配置' : '未配置' }); GM_xmlhttpRequest({ method: 'POST', url: `${FEISHU_CONFIG.API_URL}/auth/v3/tenant_access_token/internal`, headers: { 'Content-Type': 'application/json; charset=utf-8' }, timeout: 15000, // 15秒超时 data: JSON.stringify({ app_id: FEISHU_CONFIG.APP_ID, app_secret: FEISHU_CONFIG.APP_SECRET }), onload: function(response) { console.log('飞书令牌响应状态:', response.status); console.log('飞书令牌响应内容:', response.responseText); try { const data = JSON.parse(response.responseText); if (data.code === 0 && data.tenant_access_token) { // 缓存令牌,有效期设为90分钟 accessTokenCache.token = data.tenant_access_token; accessTokenCache.expireTime = Date.now() + (90 * 60 * 1000); console.log('飞书访问令牌获取成功'); resolve(data.tenant_access_token); } else { console.error('飞书API错误:', data); let errorMsg = '获取飞书访问令牌失败'; if (data.msg) { errorMsg += ': ' + data.msg; } if (data.msg && data.msg.includes('app secret invalid')) { errorMsg = '飞书App Secret无效,请检查配置'; } reject(new Error(errorMsg)); } } catch (e) { console.error('解析飞书响应失败:', e, '原始响应:', response.responseText); reject(new Error('响应解析失败: ' + e.message)); } }, onerror: function(error) { console.error('飞书网络请求失败:', error); reject(new Error('网络请求失败')); }, ontimeout: function() { console.error('飞书请求超时'); reject(new Error('获取访问令牌超时')); } }); }); } // 列出多维表格的数据表 function listFeishuTables(appToken, accessToken) { return new Promise((resolve, reject) => { console.log('正在获取数据表列表...'); GM_xmlhttpRequest({ method: 'GET', url: `${FEISHU_CONFIG.API_URL}/bitable/v1/apps/${appToken}/tables`, headers: { 'Authorization': `Bearer ${accessToken}` }, onload: function(response) { console.log('获取数据表列表响应状态:', response.status); console.log('获取数据表列表响应内容:', response.responseText); try { const data = JSON.parse(response.responseText); if (data.code === 0) { const tables = data.data.items || []; console.log(`获取到 ${tables.length} 个数据表`); resolve(tables); } else { console.error('获取数据表列表API错误:', data); reject(new Error(data.msg || '获取数据表列表失败')); } } catch (e) { console.error('解析获取数据表列表响应失败:', e, '原始响应:', response.responseText); reject(new Error('响应解析失败: ' + e.message)); } }, onerror: function(error) { console.error('获取数据表列表网络请求失败:', error); reject(new Error('网络请求失败')); } }); }); } // 查询多维表格记录(标准四步流程) function queryFeishuRecords(appToken, tableId, accessToken, options = {}, retryCount = 0) { return new Promise((resolve, reject) => { console.log('正在查询多维表格记录...'); const { pageSize = 100, fieldNames = null, filter = null, sort = null } = options; const params = new URLSearchParams(); if (pageSize) params.append('page_size', pageSize.toString()); if (fieldNames && fieldNames.length > 0) { fieldNames.forEach(name => params.append('field_names', name)); } let url = `${FEISHU_CONFIG.API_URL}/bitable/v1/apps/${appToken}/tables/${tableId}/records`; if (params.toString()) { url += '?' + params.toString(); } const requestData = {}; if (filter) requestData.filter = filter; if (sort) requestData.sort = sort; const requestOptions = { method: 'GET', url: url, headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json; charset=utf-8' }, timeout: 30000 // 增加超时时间 }; // 如果有filter或sort,使用POST方法 if (filter || sort) { requestOptions.method = 'POST'; requestOptions.url = `${FEISHU_CONFIG.API_URL}/bitable/v1/apps/${appToken}/tables/${tableId}/records/search`; requestOptions.data = JSON.stringify(requestData); } GM_xmlhttpRequest({ ...requestOptions, onload: function(response) { console.log('查询记录响应状态:', response.status); // 检查响应是否完整 if (!response.responseText || response.responseText.trim() === '') { console.warn('查询记录获取到空响应,尝试重试...'); if (retryCount < 3) { setTimeout(() => { queryFeishuRecords(appToken, tableId, accessToken, options, retryCount + 1) .then(resolve) .catch(reject); }, 1000 * (retryCount + 1)); return; } else { reject(new Error('查询记录失败:响应为空')); return; } } // 检查响应是否被截断 const responseText = response.responseText.trim(); if (!responseText.endsWith('}') && !responseText.endsWith(']')) { console.warn('查询记录响应可能被截断,尝试重试...', '响应长度:', responseText.length); if (retryCount < 2) { // 查询记录重试次数稍少一些 setTimeout(() => { queryFeishuRecords(appToken, tableId, accessToken, options, retryCount + 1) .then(resolve) .catch(reject); }, 1500 * (retryCount + 1)); return; } } console.log('查询记录响应内容长度:', responseText.length); console.log('查询记录响应内容:', responseText.substring(0, 300) + (responseText.length > 300 ? '...' : '')); try { const data = JSON.parse(responseText); if (data.code === 0) { const records = data.data.items || []; console.log(`查询到 ${records.length} 条记录`); resolve({ records: records, hasMore: data.data.has_more || false, pageToken: data.data.page_token || null, total: data.data.total || records.length }); } else { console.error('查询记录API错误:', data); let errorMsg = '查询记录失败'; if (data.msg) { errorMsg += ': ' + data.msg; } if (response.status === 403 || data.msg?.includes('Forbidden')) { errorMsg = '飞书应用权限不足,请检查应用权限配置:\n1. 确保应用有"查看、评论、编辑和管理多维表格"权限\n2. 确保应用已发布并获得管理员审批\n3. 检查Base ID和Table ID是否正确'; } reject(new Error(errorMsg)); } } catch (e) { console.error('解析查询记录响应失败:', e); console.error('原始响应长度:', responseText.length); console.error('原始响应前300字符:', responseText.substring(0, 300)); // 如果是JSON解析错误且还有重试次数,尝试重试 if (retryCount < 2) { console.log(`查询记录JSON解析失败,进行第${retryCount + 1}次重试...`); setTimeout(() => { queryFeishuRecords(appToken, tableId, accessToken, options, retryCount + 1) .then(resolve) .catch(reject); }, 2000 * (retryCount + 1)); return; } reject(new Error('响应解析失败: ' + e.message)); } }, onerror: function(error) { console.error('查询记录网络请求失败:', error); if (retryCount < 2) { console.log(`查询记录网络请求失败,进行第${retryCount + 1}次重试...`); setTimeout(() => { queryFeishuRecords(appToken, tableId, accessToken, options, retryCount + 1) .then(resolve) .catch(reject); }, 2000 * (retryCount + 1)); return; } reject(new Error('网络请求失败')); }, ontimeout: function() { console.error('查询记录请求超时'); if (retryCount < 2) { console.log(`查询记录请求超时,进行第${retryCount + 1}次重试...`); setTimeout(() => { queryFeishuRecords(appToken, tableId, accessToken, options, retryCount + 1) .then(resolve) .catch(reject); }, 3000 * (retryCount + 1)); return; } reject(new Error('请求超时')); } }); }); } // 从飞书知识库检索方法论(改进版,使用标准四步流程) function retrieveFromFeishu(query, topK = 3) { return new Promise(async (resolve, reject) => { try { console.log('开始标准四步流程检索飞书数据...'); // 步骤一:获取访问令牌 const accessToken = await getFeishuAccessToken(); console.log('✓ 步骤一:访问令牌获取成功'); // 步骤二:获取数据表信息(验证table_id是否存在) const appToken = FEISHU_CONFIG.BASE_ID; const targetTableId = FEISHU_CONFIG.TABLE_ID; try { const tables = await listFeishuTables(appToken, accessToken); const targetTable = tables.find(table => table.table_id === targetTableId); if (!targetTable) { throw new Error(`未找到指定的数据表 ID: ${targetTableId}`); } console.log(`✓ 步骤二:找到目标数据表 "${targetTable.name}"`); } catch (tableError) { console.warn('获取数据表列表失败,继续使用配置的Table ID:', tableError.message); } // 步骤三:获取字段信息 let fieldInfo = {}; let availableFieldNames = []; try { const fields = await getFeishuFields(appToken, targetTableId, accessToken); fields.forEach(field => { fieldInfo[field.field_name] = { field_id: field.field_id, type: field.type }; availableFieldNames.push(field.field_name); }); console.log(`✓ 步骤三:获取到 ${fields.length} 个字段信息`); console.log('可用字段名称:', availableFieldNames); // 检查必需字段是否存在 const requiredFields = ['标题', '内容', '关键词', '助手']; const missingFields = requiredFields.filter(field => !availableFieldNames.includes(field)); if (missingFields.length > 0) { console.warn('缺少必需字段:', missingFields); console.log('将使用可用字段进行查询'); } } catch (fieldError) { console.warn('获取字段信息失败,使用字段名称查询:', fieldError.message); // 字段信息获取失败不影响后续查询,继续执行 availableFieldNames = ['标题', '内容', '关键词', '助手']; // 使用默认字段名 } // 辅助函数:智能提取搜索关键词 const extractSearchKeywords = (query) => { const keywords = []; // 添加原始查询 keywords.push(query.trim()); // 中文分词(简单实现) const chineseWords = query.match(/[\u4e00-\u9fff]+/g) || []; chineseWords.forEach(word => { if (word.length >= 2) { keywords.push(word); // 添加子词 for (let i = 0; i <= word.length - 2; i++) { for (let j = i + 2; j <= word.length; j++) { keywords.push(word.substring(i, j)); } } } }); // 英文单词分割 const englishWords = query.match(/[a-zA-Z]+/g) || []; englishWords.forEach(word => { if (word.length >= 2) { keywords.push(word.toLowerCase()); } }); // 去重并按长度排序(长的在前) return [...new Set(keywords)].sort((a, b) => b.length - a.length); }; // 辅助函数:安全地构建查询条件,只使用存在的字段 const buildSafeQueryConditions = (searchFields, searchTerms) => { const conditions = []; searchFields.forEach(fieldName => { if (availableFieldNames.includes(fieldName)) { searchTerms.forEach(term => { conditions.push({ field_name: fieldName, operator: "contains", value: [term] }); }); } else { console.warn(`字段 "${fieldName}" 不存在,跳过此字段的查询条件`); } }); return conditions; }; // 辅助函数:安全提取字段文本值 const extractFieldText = (fieldValue) => { if (!fieldValue) return ''; if (typeof fieldValue === 'string') return fieldValue; if (Array.isArray(fieldValue)) { return fieldValue.map(item => { if (typeof item === 'string') return item; if (item && typeof item === 'object' && item.text) return item.text; return String(item || ''); }).join(' '); } if (typeof fieldValue === 'object' && fieldValue.text) { return fieldValue.text; } return String(fieldValue); }; // 辅助函数:计算文本相似度 const calculateSimilarity = (text1, text2) => { const t1 = extractFieldText(text1).toLowerCase(); const t2 = extractFieldText(text2).toLowerCase(); if (!t1 || !t2) return 0; // 直接包含得分最高 if (t1.includes(t2) || t2.includes(t1)) { return 0.9; } // 计算共同字符数 const chars1 = new Set(t1); const chars2 = new Set(t2); const intersection = new Set([...chars1].filter(x => chars2.has(x))); const union = new Set([...chars1, ...chars2]); return intersection.size / union.size; }; // 步骤四:查询记录数据 console.log('开始查询记录,搜索关键词:', query); // 提取智能搜索关键词 const searchKeywords = extractSearchKeywords(query); console.log('提取的搜索关键词:', searchKeywords.slice(0, 5)); // 只显示前5个 // 首先尝试获取所有记录进行调试(不指定字段名,获取所有字段) const debugOptions = { pageSize: 10 // 移除 fieldNames 参数,让API返回所有字段 }; try { const debugResult = await queryFeishuRecords(appToken, targetTableId, accessToken, debugOptions); console.log('调试:获取到的所有记录数量:', debugResult.records.length); debugResult.records.forEach((record, index) => { console.log(`调试:记录${index + 1}:`, { 所有字段名: Object.keys(record.fields), 标题字段: record.fields['标题'] || record.fields['1'], 助手字段内容: (record.fields['助手'] || record.fields['助手']) ? extractFieldText(record.fields['助手'] || record.fields['助手']).substring(0, 100) + '...' : '无', 助手字段长度: record.fields['助手'] ? extractFieldText(record.fields['助手']).length : 0, 原始字段结构: record.fields }); }); } catch (debugError) { console.warn('调试查询失败:', debugError.message); } // 尝试多种搜索策略 let result = null; let searchStrategy = ''; // 策略1: 使用主要关键词在助手字段中搜索 try { const primaryKeywords = searchKeywords.slice(0, 3); // 使用前3个最重要的关键词 const assistantConditions = buildSafeQueryConditions(['助手'], primaryKeywords); if (assistantConditions.length === 0) { console.warn('策略1跳过:助手字段不存在'); throw new Error('助手字段不存在'); } const queryOptions1 = { pageSize: topK, filter: { conjunction: "or", // 改为OR,增加匹配可能性 conditions: assistantConditions } }; result = await queryFeishuRecords(appToken, targetTableId, accessToken, queryOptions1); searchStrategy = '助手字段关键词匹配'; console.log(`策略1(${searchStrategy}):找到 ${result.records.length} 条记录`); } catch (error1) { console.warn('策略1失败:', error1.message); } // 策略2: 如果助手字段搜索无结果,尝试使用更多关键词在多个字段中搜索 if (!result || result.records.length === 0) { try { const extendedKeywords = searchKeywords.slice(0, 5); // 使用前5个关键词 const multiFieldConditions = buildSafeQueryConditions(['助手', '内容', '标题', '关键词'], extendedKeywords); if (multiFieldConditions.length === 0) { console.warn('策略2跳过:没有可用的搜索字段'); throw new Error('没有可用的搜索字段'); } const queryOptions2 = { pageSize: topK * 2, // 获取更多结果用于后续排序 filter: { conjunction: "or", conditions: multiFieldConditions } }; result = await queryFeishuRecords(appToken, targetTableId, accessToken, queryOptions2); searchStrategy = '多字段扩展搜索'; console.log(`策略2(${searchStrategy}):找到 ${result.records.length} 条记录`); } catch (error2) { console.warn('策略2失败:', error2.message); } } // 策略3: 如果仍无结果,尝试获取所有记录并在客户端过滤 if (!result || result.records.length === 0) { try { console.log('策略3:尝试获取所有记录进行客户端过滤...'); const queryOptions3 = { pageSize: 50 // 移除 fieldNames,获取所有字段 }; const allRecords = await queryFeishuRecords(appToken, targetTableId, accessToken, queryOptions3); console.log(`策略3:获取所有记录进行客户端过滤,总记录数: ${allRecords.records.length}`); // 智能客户端过滤和排序 const scoredRecords = allRecords.records.map(record => { try { let maxScore = 0; let matchedField = ''; const searchFields = ['助手', '内容', '标题', '关键词']; // 计算每个字段的相似度得分 for (const fieldName of searchFields) { if (availableFieldNames.includes(fieldName)) { const fieldValue = record.fields[fieldName]; const fieldText = extractFieldText(fieldValue); // 对每个搜索关键词计算相似度 for (const keyword of searchKeywords.slice(0, 8)) { const similarity = calculateSimilarity(fieldText, keyword); if (similarity > maxScore) { maxScore = similarity; matchedField = fieldName; } } // 特别关注助手字段,给予额外权重 if (fieldName === '助手' && fieldText) { for (const keyword of searchKeywords.slice(0, 5)) { if (fieldText.toLowerCase().includes(keyword.toLowerCase())) { maxScore = Math.max(maxScore, 0.8); matchedField = '助手'; } } } } } return { record, score: maxScore, matchedField }; } catch (filterError) { console.warn('计算记录得分时出错:', filterError.message); return { record, score: 0, matchedField: '' }; } }); // 过滤出有意义的匹配(得分 > 0.1)并按得分排序 const filteredRecords = scoredRecords .filter(item => item.score > 0.1) .sort((a, b) => { // 助手字段匹配优先 if (a.matchedField === '助手' && b.matchedField !== '助手') return -1; if (b.matchedField === '助手' && a.matchedField !== '助手') return 1; // 然后按得分排序 return b.score - a.score; }) .map(item => item.record); console.log('智能过滤结果:', filteredRecords.length, '条记录'); if (filteredRecords.length > 0) { console.log('最佳匹配得分:', scoredRecords.find(item => item.record === filteredRecords[0])?.score); } result = { records: filteredRecords.slice(0, topK), total: filteredRecords.length }; searchStrategy = '客户端模糊匹配'; console.log(`策略3(${searchStrategy}):过滤后找到 ${result.records.length} 条记录`); } catch (error3) { console.warn('策略3失败:', error3.message); // 策略4: 最后的降级策略,尝试简单获取记录(不使用过滤) try { console.log('策略4:降级策略,尝试简单获取记录...'); const simpleOptions = { pageSize: Math.min(topK * 2, 10) // 移除 fieldNames,获取所有字段 }; const simpleRecords = await queryFeishuRecords(appToken, targetTableId, accessToken, simpleOptions); console.log(`策略4:简单获取到 ${simpleRecords.records.length} 条记录`); result = { records: simpleRecords.records.slice(0, topK), total: simpleRecords.records.length }; searchStrategy = '降级简单获取'; console.log(`策略4(${searchStrategy}):返回 ${result.records.length} 条记录`); } catch (error4) { console.warn('策略4也失败:', error4.message); // 如果所有策略都失败,返回空结果但不抛出错误 result = { records: [], total: 0 }; searchStrategy = '所有策略失败,返回空结果'; console.log('所有检索策略都失败,将继续使用AI增强(不使用知识库)'); } } } console.log(`✓ 步骤四:查询完成,使用策略"${searchStrategy}",找到 ${result.records.length} 条相关记录`); // 对所有策略的结果进行智能排序(除了策略3已经排序过的) if (searchStrategy !== '客户端模糊匹配' && result.records.length > 1) { console.log('对查询结果进行智能排序...'); const scoredResults = result.records.map(record => { let maxScore = 0; const fields = record.fields; // 计算与查询的相关性得分 for (const keyword of searchKeywords.slice(0, 5)) { const assistantText = extractFieldText(fields['助手']); const titleText = extractFieldText(fields['标题']); const contentText = extractFieldText(fields['内容']); // 助手字段权重最高 const assistantScore = calculateSimilarity(assistantText, keyword) * 1.0; const titleScore = calculateSimilarity(titleText, keyword) * 0.8; const contentScore = calculateSimilarity(contentText, keyword) * 0.6; maxScore = Math.max(maxScore, assistantScore, titleScore, contentScore); } return { record, score: maxScore }; }); // 按得分排序 result.records = scoredResults .sort((a, b) => b.score - a.score) .map(item => item.record); console.log('排序完成,最高得分:', scoredResults[0]?.score); } // 处理查询结果(安全地访问字段) const methodologies = result.records.map(item => { const fields = item.fields; // 安全地获取字段值(支持字段名和字段ID) const getFieldValue = (fieldName, defaultValue = '') => { // 首先尝试使用字段名 let fieldValue = fields[fieldName]; // 如果字段名不存在,尝试使用字段ID(数字键) if (!fieldValue) { const fieldKeys = Object.keys(fields); // 查找可能的字段ID或其他键名 for (const key of fieldKeys) { const keyFieldValue = fields[key]; if (keyFieldValue) { const textValue = extractFieldText(keyFieldValue); // 简单的启发式匹配:如果是标题字段,通常内容较短且不包含大量文本 if (fieldName === '标题' && textValue && textValue.length < 200 && !textValue.includes('来源:')) { fieldValue = keyFieldValue; break; } // 如果是助手字段,通常内容较长 else if (fieldName === '助手' && textValue && textValue.length > 100) { fieldValue = keyFieldValue; break; } // 如果是内容字段,通常包含'来源:'或很长 else if (fieldName === '内容' && textValue && (textValue.includes('来源:') || textValue.length > 500)) { fieldValue = keyFieldValue; break; } // 如果是关键词字段,通常包含逗号分隔的短词 else if (fieldName === '关键词' && textValue && textValue.includes(',') && textValue.length < 500) { fieldValue = keyFieldValue; break; } } } } return extractFieldText(fieldValue) || defaultValue; }; const methodology = { title: getFieldValue('标题', '未知标题'), content: getFieldValue('内容'), keywords: getFieldValue('关键词'), assistant: getFieldValue('助手'), recordId: item.record_id }; // 计算匹配度用于调试 let matchScore = 0; const assistantText = methodology.assistant || ''; for (const keyword of searchKeywords.slice(0, 3)) { if (assistantText.toLowerCase().includes(keyword.toLowerCase())) { matchScore += 1; } } methodology.matchScore = matchScore; return methodology; }); console.log('飞书数据检索完成,返回结果'); console.log('返回的方法论数据:', methodologies.map((m, index) => ({ 序号: index + 1, 标题: m.title, 匹配得分: m.matchScore, 助手字段长度: m.assistant.length, 助手内容预览: m.assistant ? m.assistant.substring(0, 150) + '...' : '无内容', 关键词: m.keywords }))); // 如果找到了结果,显示最佳匹配的详细信息 if (methodologies.length > 0) { const bestMatch = methodologies[0]; console.log('🎯 最佳匹配方法论:'); console.log('标题:', bestMatch.title); console.log('匹配得分:', bestMatch.matchScore); console.log('助手内容长度:', bestMatch.assistant.length); console.log('关键词:', bestMatch.keywords); // 显示匹配的关键词 const matchedKeywords = searchKeywords.slice(0, 5).filter(keyword => bestMatch.assistant.toLowerCase().includes(keyword.toLowerCase()) ); if (matchedKeywords.length > 0) { console.log('匹配的关键词:', matchedKeywords); } } else { console.log('⚠️ 未找到匹配的方法论'); console.log('搜索关键词:', searchKeywords.slice(0, 5)); console.log('建议:'); console.log('1. 检查飞书表格中是否有相关内容'); console.log('2. 尝试使用更通用的关键词'); console.log('3. 检查【助手】字段是否包含相关方法论'); } resolve(methodologies); } catch (error) { console.error('飞书数据检索失败:', error); reject(error); } }); } // 创建飞书多维表格字段 function createFeishuField(baseId, tableId, accessToken, fieldName, fieldType = 1) { return new Promise((resolve, reject) => { console.log(`正在创建字段: ${fieldName}`); const requestData = { field_name: fieldName, type: fieldType }; GM_xmlhttpRequest({ method: 'POST', url: `${FEISHU_CONFIG.API_URL}/bitable/v1/apps/${baseId}/tables/${tableId}/fields`, headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json; charset=utf-8' }, data: JSON.stringify(requestData), onload: function(response) { console.log(`创建字段${fieldName}响应状态:`, response.status); console.log(`创建字段${fieldName}响应内容:`, response.responseText); try { const data = JSON.parse(response.responseText); if (data.code === 0) { console.log(`字段"${fieldName}"创建成功`); resolve(data.data.field); } else { console.error(`创建字段${fieldName}API错误:`, data); let errorMsg = `创建字段"${fieldName}"失败`; if (data.msg) { errorMsg += ': ' + data.msg; } reject(new Error(errorMsg)); } } catch (e) { console.error(`解析创建字段${fieldName}响应失败:`, e, '原始响应:', response.responseText); reject(new Error('响应解析失败: ' + e.message)); } }, onerror: function(error) { console.error(`创建字段${fieldName}网络请求失败:`, error); reject(new Error('网络请求失败')); } }); }); } // 获取飞书多维表格字段列表 function getFeishuFields(baseId, tableId, accessToken, retryCount = 0) { return new Promise((resolve, reject) => { console.log('正在获取飞书表格字段列表...'); GM_xmlhttpRequest({ method: 'GET', url: `${FEISHU_CONFIG.API_URL}/bitable/v1/apps/${baseId}/tables/${tableId}/fields`, headers: { 'Authorization': `Bearer ${accessToken}` }, timeout: 30000, // 增加超时时间到30秒 onload: function(response) { console.log('获取字段列表响应状态:', response.status); // 检查响应是否完整 if (!response.responseText || response.responseText.trim() === '') { console.warn('获取到空响应,尝试重试...'); if (retryCount < 3) { setTimeout(() => { getFeishuFields(baseId, tableId, accessToken, retryCount + 1) .then(resolve) .catch(reject); }, 1000 * (retryCount + 1)); return; } else { reject(new Error('获取字段列表失败:响应为空')); return; } } // 检查响应是否被截断 const responseText = response.responseText.trim(); if (!responseText.endsWith('}') && !responseText.endsWith(']')) { console.warn('响应可能被截断,尝试重试...', '响应长度:', responseText.length); if (retryCount < 3) { setTimeout(() => { getFeishuFields(baseId, tableId, accessToken, retryCount + 1) .then(resolve) .catch(reject); }, 1000 * (retryCount + 1)); return; } } console.log('获取字段列表响应内容长度:', responseText.length); console.log('获取字段列表响应内容:', responseText.substring(0, 500) + (responseText.length > 500 ? '...' : '')); try { const data = JSON.parse(responseText); if (data.code === 0) { const fields = data.data.items || []; console.log(`获取到 ${fields.length} 个字段`); resolve(fields); } else { console.error('获取字段列表API错误:', data); reject(new Error(data.msg || '获取字段列表失败')); } } catch (e) { console.error('解析获取字段列表响应失败:', e); console.error('原始响应长度:', responseText.length); console.error('原始响应前500字符:', responseText.substring(0, 500)); // 如果是JSON解析错误且还有重试次数,尝试重试 if (retryCount < 3) { console.log(`JSON解析失败,进行第${retryCount + 1}次重试...`); setTimeout(() => { getFeishuFields(baseId, tableId, accessToken, retryCount + 1) .then(resolve) .catch(reject); }, 2000 * (retryCount + 1)); return; } reject(new Error('响应解析失败: ' + e.message)); } }, onerror: function(error) { console.error('获取字段列表网络请求失败:', error); if (retryCount < 3) { console.log(`网络请求失败,进行第${retryCount + 1}次重试...`); setTimeout(() => { getFeishuFields(baseId, tableId, accessToken, retryCount + 1) .then(resolve) .catch(reject); }, 2000 * (retryCount + 1)); return; } reject(new Error('网络请求失败')); }, ontimeout: function() { console.error('获取字段列表请求超时'); if (retryCount < 3) { console.log(`请求超时,进行第${retryCount + 1}次重试...`); setTimeout(() => { getFeishuFields(baseId, tableId, accessToken, retryCount + 1) .then(resolve) .catch(reject); }, 3000 * (retryCount + 1)); return; } reject(new Error('请求超时')); } }); }); } // 确保必要字段存在 async function ensureRequiredFields(baseId, tableId, accessToken) { try { console.log('检查并创建必要字段...'); // 获取现有字段 const existingFields = await getFeishuFields(baseId, tableId, accessToken); const fieldNames = existingFields.map(field => field.field_name); // 定义必要字段 const requiredFields = [ { name: '标题', type: 1 }, // 文本 { name: '内容', type: 1 }, // 文本 { name: '关键词', type: 1 }, // 文本 { name: '助手', type: 1 }, // 文本 { name: '创建时间', type: 1 } // 文本(也可以用1001创建时间类型) ]; // 检查并创建缺失的字段 for (const field of requiredFields) { if (!fieldNames.includes(field.name)) { console.log(`字段"${field.name}"不存在,正在创建...`); try { await createFeishuField(baseId, tableId, accessToken, field.name, field.type); console.log(`字段"${field.name}"创建成功`); } catch (error) { console.warn(`创建字段"${field.name}"失败:`, error.message); // 继续创建其他字段,不中断流程 } } else { console.log(`字段"${field.name}"已存在`); } } console.log('字段检查完成'); } catch (error) { console.error('检查字段时出错:', error); throw error; } } // 添加方法论到飞书知识库 function addToFeishu(title, content, keywords = '') { return new Promise(async (resolve, reject) => { try { const token = await getFeishuAccessToken(); // 确保必要字段存在 try { await ensureRequiredFields(FEISHU_CONFIG.BASE_ID, FEISHU_CONFIG.TABLE_ID, token); } catch (fieldError) { console.warn('字段检查失败,但继续尝试添加数据:', fieldError.message); } const requestData = { records: [{ fields: { '标题': title, '内容': content, '关键词': keywords, '创建时间': new Date().toISOString() } }] }; GM_xmlhttpRequest({ method: 'POST', url: `${FEISHU_CONFIG.API_URL}/bitable/v1/apps/${FEISHU_CONFIG.BASE_ID}/tables/${FEISHU_CONFIG.TABLE_ID}/records/batch_create`, headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, data: JSON.stringify(requestData), onload: function(response) { console.log('飞书添加记录响应状态:', response.status); console.log('飞书添加记录响应内容:', response.responseText); try { const data = JSON.parse(response.responseText); if (data.code === 0) { resolve(data.data); } else { console.error('飞书添加记录API错误:', data); let errorMsg = '添加到飞书失败'; if (data.msg) { errorMsg += ': ' + data.msg; } if (response.status === 403 || data.msg?.includes('Forbidden')) { errorMsg = '飞书应用权限不足,请检查应用权限配置:\n1. 确保应用有"查看、编辑、新增和删除多维表格"权限\n2. 确保应用已发布并获得管理员审批\n3. 检查Base ID和Table ID是否正确'; } reject(new Error(errorMsg)); } } catch (e) { console.error('解析飞书添加记录响应失败:', e, '原始响应:', response.responseText); reject(new Error('响应解析失败: ' + e.message)); } }, onerror: function(error) { console.error('飞书添加记录网络请求失败:', error); reject(new Error('网络请求失败')); } }); } catch (error) { reject(error); } }); } // ==================== AI增强相关 ==================== // 调用AI API增强提示词 function enhancePromptWithAI(userInput, methodologyContext = '') { return new Promise((resolve, reject) => { const systemPrompt = `扮演一名提示词工程师,根据我接下来为你提供的需求、相关方法论和示例,创建一个可以满足需求的提示词。 ## 创作方法 1. 分析需求:理解或挖掘需求的背景和目标,尽可能详细的提供在提示词中,但不要意向编造需求中未描述的信息; 2. 方法论挑选:我会为你提供 0-3个与用户需求相关的方法论,你可以选择其中 1 个或整合多个,放在提示词中。如果接下来的信息中不包含方法论,可以省略。 3. 我为你提供的信息中可能会包含参考示例,从中选择与需求的输出相关的示例,放在提示词中。如果未提供示例,则省略这部分。 ## 提示词框架 在创建提示词时,参考以下框架: # 扮演角色: 为 AI 定义角色,让它由通用的"助理",变成更擅长处理具体工作的定向角色,可以使用职业来描述定义。 ## 做什么: 向 AI 尽可能详细的描述任务的背景信息,可以调用它更多的"知识记忆"。 ## 怎么做: 把你完成这项任务的成熟方法论告诉AI,可以确保 AI 按照预期的方法完成任务,几个tips: 1)如果能给出完成任务的步骤,并要求 AI 输出过程指标,效果会非常棒; 2)可以使用现成的方法论或者理论知识框架,即便你并不能熟练的应用它; 3)如果你不知道这项工作的方法,可以先向 AI 询问,从它推荐的方法中选择你觉得靠谱的。 ## 参考示例: "怎么做"和"结果要求"中你可能列不出全面的信息,让 AI 自己在示例中学习。保留示例前后的代码块分隔符。 ## 结果要求: 为 AI 列出输出的要求,包括格式、结构等。 另一个重要的提示:为了防止 AI 胡编乱造,有些时候可以在要求为 AI 留出路,类似"如果你无法执行这个任务,可以回复XXX"。 ## 输出要求 直接输出优化后的提示词,不要包含任何解释或说明。确保提示词清晰、具体、可执行。`; let userPrompt = `用户需求: <user_query> ${userInput} </user_query>`; if (methodologyContext && methodologyContext.trim()) { userPrompt += ` 可选方法论支持 <methodology> ${methodologyContext} </methodology>`; } const requestData = { model: AI_CONFIG.MODEL_NAME, messages: [ { role: 'system', content: systemPrompt }, { role: 'user', content: userPrompt } ], temperature: 0.7, max_tokens: 2000 }; console.log('发送AI请求到:', `${AI_CONFIG.BASE_URL}/chat/completions`); console.log('AI配置:', { MODEL_NAME: AI_CONFIG.MODEL_NAME, BASE_URL: AI_CONFIG.BASE_URL, API_KEY: AI_CONFIG.API_KEY ? '已配置' : '未配置' }); GM_xmlhttpRequest({ method: 'POST', url: `${AI_CONFIG.BASE_URL}/chat/completions`, headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${AI_CONFIG.API_KEY}` }, data: JSON.stringify(requestData), onload: function(response) { console.log('AI响应状态:', response.status); console.log('AI响应内容:', response.responseText); try { const data = JSON.parse(response.responseText); if (data.choices && data.choices[0] && data.choices[0].message) { resolve(data.choices[0].message.content.trim()); } else { console.error('AI响应格式异常:', data); reject(new Error('AI响应格式异常: ' + JSON.stringify(data))); } } catch (e) { console.error('解析AI响应失败:', e, '原始响应:', response.responseText); reject(new Error('响应解析失败: ' + e.message)); } }, onerror: function(error) { console.error('AI网络请求失败:', error); reject(new Error('AI请求失败,请检查网络连接和API配置')); }, ontimeout: function() { console.error('AI请求超时'); reject(new Error('AI请求超时')); } }); }); } // ==================== 输入处理相关 ==================== // 输入处理器类 class InputHandler { constructor(element, inputType) { this.element = element; this.inputType = inputType; } getInputText() { switch (this.inputType) { case 'textarea': return this.element.value || ''; case 'contenteditable': return this.getContentEditableText(); default: return ''; } } setInputText(text) { try { switch (this.inputType) { case 'textarea': this.setTextareaValue(text); break; case 'contenteditable': this.setContentEditableText(text); break; default: this.element.textContent = text; } this.triggerInput(); } catch (error) { console.error('设置输入文本失败:', error); // 回退方案 try { if (this.element.value !== undefined) { this.element.value = text; } else { this.element.textContent = text; } this.triggerInput(); } catch (fallbackError) { console.error('回退设置也失败:', fallbackError); } } } focus() { try { this.element.focus(); if (this.inputType === 'contenteditable') { this.setCursorToEnd(); } } catch (e) { console.warn('聚焦失败:', e); } } getContentEditableText() { return this.element.textContent || ''; } setTextareaValue(text) { try { const element = this.element; const start = element.selectionStart; element.value = text; const newPos = Math.min(text.length, start + text.length); element.setSelectionRange(newPos, newPos); } catch (e) { console.warn('设置textarea值失败:', e); } } setContentEditableText(text) { try { if (this.isKimiEditor()) { this.setKimiEditorText(text); } else { this.element.textContent = text; } } catch (e) { console.warn('设置contenteditable文本失败:', e); this.element.textContent = text; } } isKimiEditor() { return this.element.classList.contains('chat-input-editor') || this.element.hasAttribute('data-lexical-editor') || this.element.getAttribute('contenteditable') === 'true' || this.element.querySelector('[data-lexical-text]') !== null; } setKimiEditorText(text) { try { // 清空现有内容 this.element.innerHTML = ''; // 创建段落元素 const p = document.createElement('p'); p.setAttribute('dir', 'ltr'); // 处理文本内容 const lines = text.split('\n'); lines.forEach((line, index) => { if (index > 0) { p.appendChild(document.createElement('br')); } if (line.trim()) { const span = document.createElement('span'); span.setAttribute('data-lexical-text', 'true'); span.textContent = line; p.appendChild(span); } }); // 如果没有内容,添加默认span if (p.children.length === 0) { const span = document.createElement('span'); span.setAttribute('data-lexical-text', 'true'); span.textContent = text || ''; p.appendChild(span); } // 添加到编辑器 this.element.appendChild(p); // 触发输入事件 this.triggerInput(); // 延迟设置光标和触发框架事件 setTimeout(() => { this.setCursorToEnd(); this.triggerFrameworkEvents(); }, 100); // 额外的兼容性处理 setTimeout(() => { const inputEvent = new Event('input', { bubbles: true, cancelable: true }); this.element.dispatchEvent(inputEvent); if (this.element._lexicalEditor) { try { this.element._lexicalEditor.update(() => {}); } catch (lexicalError) { console.warn('Lexical编辑器更新失败:', lexicalError); } } }, 200); } catch (e) { console.error('设置Kimi编辑器文本失败:', e); this.element.textContent = text; this.triggerInput(); } } setCursorToEnd() { try { const range = document.createRange(); range.selectNodeContents(this.element); range.collapse(false); const selection = window.getSelection(); if (selection) { selection.removeAllRanges(); selection.addRange(range); } } catch (e) { console.warn('设置光标位置失败:', e); } } triggerInput() { ['input', 'change', 'keyup', 'blur'].forEach(eventType => { try { const event = new Event(eventType, { bubbles: true, cancelable: true }); this.element.dispatchEvent(event); } catch (e) { console.warn(`触发 ${eventType} 事件失败:`, e); } }); this.triggerFrameworkEvents(); } triggerFrameworkEvents() { try { // React事件触发 const reactKeys = Object.keys(this.element).filter(key => key.startsWith('__reactInternalInstance') || key.startsWith('__reactFiber') ); if (reactKeys.length > 0) { const event = new Event('input', { bubbles: true }); event.simulated = true; this.element.dispatchEvent(event); } // Vue事件触发 if (this.element.__vue__) { const event = new CustomEvent('input', { bubbles: true, detail: { value: this.getInputText() } }); this.element.dispatchEvent(event); } } catch (e) { console.warn('触发框架事件失败:', e); } } } // ==================== 按钮注入相关 ==================== // 创建增强按钮 function createEnhanceButton() { const button = document.createElement('div'); button.setAttribute('role', 'button'); button.setAttribute('tabindex', '0'); button.className = 'prompt-enhancer-button'; button.innerHTML = getButtonContent(); button.title = '使用方法论增强提示词'; button.setAttribute('data-testid', 'prompt-enhancer-button'); button.addEventListener('click', handleEnhanceClick); button.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); e.stopPropagation(); handleEnhanceClick(); } }); return button; } // 获取按钮内容 function getButtonContent() { const icon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> <path d="M12 2L13.09 8.26L22 9L13.09 9.74L12 16L10.91 9.74L2 9L10.91 8.26L12 2Z"/> </svg>`; return `${icon} <span>P增强</span>`; } // 注入按钮到页面 async function injectButton() { const siteConfig = SITE_CONFIGS[currentSiteType]; if (!siteConfig) return false; const container = await waitForElement(siteConfig.buttonContainerSelector); if (!container) { console.warn('未找到按钮容器'); return false; } const inputElement = await waitForElement(siteConfig.inputSelector); if (!inputElement) { console.warn('未找到输入元素'); return false; } inputHandler = new InputHandler(inputElement, siteConfig.inputType); enhanceButton = createEnhanceButton(); // 根据不同网站调整按钮样式和位置 switch (currentSiteType) { case SiteType.DEEPSEEK: injectForDeepSeek(container); break; case SiteType.KIMI: injectForKimi(container); break; case SiteType.DOUBAO: injectForDoubao(container); break; default: container.appendChild(enhanceButton); } console.log('增强按钮注入成功'); return true; } // DeepSeek网站特殊处理 function injectForDeepSeek(container) { if (!enhanceButton) return; enhanceButton.style.cssText += ` margin-right: 8px; cursor: pointer; `; container.appendChild(enhanceButton); } // Kimi网站特殊处理 function injectForKimi(container) { if (!enhanceButton) return; const wrapper = document.createElement('div'); wrapper.className = 'researcher-switch-container'; wrapper.style.cssText = ` display: flex; align-items: center; margin-right: 8px; `; enhanceButton.style.cssText += ` display: flex; align-items: center; gap: 6px; padding: 8px 12px; background: rgba(0, 0, 0, 0.05); border: none; border-radius: 16px; color: #666; font-size: 14px; cursor: pointer; transition: background-color 0.2s ease, color 0.2s ease; white-space: nowrap; `; wrapper.appendChild(enhanceButton); container.appendChild(wrapper); } // 豆包网站特殊处理 function injectForDoubao(container) { if (!enhanceButton) return; const deepThinkButton = container.querySelector('[data-testid="deep_think_select_button"]'); if (deepThinkButton && deepThinkButton.parentElement) { deepThinkButton.parentElement.insertBefore(enhanceButton, deepThinkButton.nextSibling); } else { container.appendChild(enhanceButton); } enhanceButton.style.cssText += ` flex-shrink: 0; margin-left: 8px; margin-right: 8px; `; } // ==================== 事件处理 ==================== // 处理增强按钮点击 async function handleEnhanceClick() { if (!inputHandler) { console.error('输入处理器未初始化'); showTooltip('系统未初始化,请刷新页面重试'); return; } try { const userInput = inputHandler.getInputText(); if (!userInput || userInput.trim().length === 0) { showTooltip('请先输入内容'); return; } setButtonState(ButtonState.LOADING); setFloatingEnhancerState('loading'); let knowledgeBaseStatus = '未使用'; // 从飞书知识库检索相关方法论 console.log('正在检索知识库...'); let methodologyContext = ''; let methodologyCount = 0; try { // 检查飞书配置是否完整 const hasFeishuConfig = FEISHU_CONFIG.APP_ID && FEISHU_CONFIG.APP_SECRET && FEISHU_CONFIG.BASE_ID && FEISHU_CONFIG.TABLE_ID; if (hasFeishuConfig) { const methodologies = await retrieveFromFeishu(userInput); if (methodologies && methodologies.length > 0) { methodologyContext = methodologies.map((m, index) => `【相关方法论 ${index + 1}】\n标题: ${m.title}\n助手内容: ${m.assistant}\n关键词: ${m.keywords}` ).join('\n\n'); methodologyCount = methodologies.length; knowledgeBaseStatus = `找到${methodologyCount}个相关方法论`; console.log('找到相关方法论:', methodologyCount, '个'); console.log('方法论上下文内容长度:', methodologyContext.length); console.log('方法论上下文预览:', methodologyContext.substring(0, 300) + '...'); } else { knowledgeBaseStatus = '未找到相关方法论'; console.log('未找到相关方法论,将使用纯AI增强'); } } else { knowledgeBaseStatus = '飞书配置不完整'; console.log('飞书配置不完整,跳过知识库检索,使用纯AI增强'); } } catch (error) { knowledgeBaseStatus = `检索失败: ${error.message}`; console.warn('知识库检索失败:', error.message); console.log('知识库检索失败,将继续使用纯AI增强'); } // 使用AI增强提示词 console.log('正在AI增强...'); console.log('用户输入:', userInput); console.log('是否有方法论上下文:', methodologyContext ? '是' : '否'); try { const enhancedText = await enhancePromptWithAI(userInput, methodologyContext); console.log('AI增强完成,增强后文本长度:', enhancedText.length); console.log('增强后文本预览:', enhancedText.substring(0, 200) + '...'); // 自动复制、清空并粘贴增强后的文本 console.log('正在复制增强后的文本到剪切板...'); try { // 使用现代剪切板API复制文本 if (navigator.clipboard && navigator.clipboard.writeText) { await navigator.clipboard.writeText(enhancedText); console.log('文本已复制到剪切板'); } else { // 回退到传统方法 const textArea = document.createElement('textarea'); textArea.value = enhancedText; textArea.style.position = 'fixed'; textArea.style.left = '-999999px'; textArea.style.top = '-999999px'; document.body.appendChild(textArea); textArea.focus(); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); console.log('文本已复制到剪切板(传统方法)'); } // 立即清空输入框并使用剪切板粘贴 console.log('正在清空输入框并使用剪切板粘贴...'); // 聚焦输入框 inputHandler.focus(); await new Promise(resolve => setTimeout(resolve, 100)); // 全选当前输入框内容 console.log('正在全选输入框内容...'); if (inputHandler.inputType === 'textarea') { inputHandler.element.select(); } else { // 对于contenteditable,使用Selection API全选 const range = document.createRange(); range.selectNodeContents(inputHandler.element); const selection = window.getSelection(); if (selection) { selection.removeAllRanges(); selection.addRange(range); } } await new Promise(resolve => setTimeout(resolve, 100)); // 使用剪切板粘贴替换选中内容 console.log('正在使用剪切板粘贴...'); try { // 尝试使用现代剪切板API粘贴 if (navigator.clipboard && navigator.clipboard.readText) { const clipboardText = await navigator.clipboard.readText(); if (clipboardText === enhancedText) { // 模拟Ctrl+V粘贴 const pasteEvent = new KeyboardEvent('keydown', { key: 'v', code: 'KeyV', ctrlKey: true, bubbles: true, cancelable: true }); inputHandler.element.dispatchEvent(pasteEvent); // 也尝试paste事件 const clipboardData = new DataTransfer(); clipboardData.setData('text/plain', enhancedText); const pasteEventClipboard = new ClipboardEvent('paste', { clipboardData: clipboardData, bubbles: true, cancelable: true }); inputHandler.element.dispatchEvent(pasteEventClipboard); } } // 等待粘贴完成 await new Promise(resolve => setTimeout(resolve, 300)); // 验证粘贴结果 const finalText = inputHandler.getInputText(); if (finalText.includes(enhancedText.substring(0, 100))) { console.log('剪切板粘贴操作成功完成'); } else { console.warn('剪切板粘贴失败,回退到直接设置文本'); // 回退到直接设置文本 inputHandler.setInputText(enhancedText); } } catch (clipboardPasteError) { console.warn('剪切板粘贴失败,回退到直接设置文本:', clipboardPasteError); // 回退到直接设置文本 inputHandler.setInputText(enhancedText); } } catch (error) { console.error('自动复制粘贴失败:', error); // 回退到直接设置文本 inputHandler.setInputText(enhancedText); inputHandler.focus(); } setButtonState(ButtonState.SUCCESS); setFloatingEnhancerState('success'); // 显示增强结果信息 const successMessage = methodologyCount > 0 ? `增强完成!已自动复制并粘贴(使用了${methodologyCount}个方法论)` : '增强完成!已自动复制并粘贴(纯AI增强)'; showTooltip(successMessage); setTimeout(() => { setButtonState(ButtonState.NORMAL); setFloatingEnhancerState('normal'); }, 2000); console.log(`增强完成 - 知识库状态: ${knowledgeBaseStatus}`); } catch (aiError) { console.error('AI增强失败:', aiError); setButtonState(ButtonState.ERROR); setFloatingEnhancerState('error'); // 提供更详细的错误信息 let errorMessage = 'AI增强失败'; if (aiError.message) { if (aiError.message.includes('API')) { errorMessage = 'AI API调用失败,请检查配置'; } else if (aiError.message.includes('网络')) { errorMessage = '网络连接失败,请重试'; } else { errorMessage = `AI增强失败: ${aiError.message}`; } } showTooltip(errorMessage); setTimeout(() => { setButtonState(ButtonState.NORMAL); setFloatingEnhancerState('normal'); }, 3000); throw aiError; // 重新抛出错误以便外层catch处理 } } catch (error) { console.error('增强处理失败:', error); // 如果还没有设置错误状态,设置它 if (!enhanceButton?.classList.contains('pe-error')) { setButtonState(ButtonState.ERROR); setFloatingEnhancerState('error'); showTooltip('增强失败,请重试'); setTimeout(() => { setButtonState(ButtonState.NORMAL); setFloatingEnhancerState('normal'); }, 3000); } } } // 设置按钮状态 function setButtonState(state) { if (!enhanceButton) return; enhanceButton.classList.remove('pe-loading', 'pe-success', 'pe-error'); switch (state) { case ButtonState.LOADING: enhanceButton.classList.add('pe-loading'); enhanceButton.disabled = true; updateButtonContent('增强中...'); break; case ButtonState.SUCCESS: enhanceButton.classList.add('pe-success'); enhanceButton.disabled = false; updateButtonContent('✓ 已增强'); break; case ButtonState.ERROR: enhanceButton.classList.add('pe-error'); enhanceButton.disabled = false; updateButtonContent('× 失败'); break; default: enhanceButton.disabled = false; enhanceButton.innerHTML = getButtonContent(); } } // 更新按钮内容 function updateButtonContent(text) { if (!enhanceButton) return; const icon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> <path d="M12 2L13.09 8.26L22 9L13.09 9.74L12 16L10.91 9.74L2 9L10.91 8.26L12 2Z"/> </svg>`; enhanceButton.innerHTML = `${icon} <span>${text}</span>`; } // 显示提示 function showTooltip(message) { if (!enhanceButton) return; const tooltip = document.createElement('div'); tooltip.className = 'pe-tooltip'; tooltip.textContent = message; enhanceButton.style.position = 'relative'; enhanceButton.appendChild(tooltip); setTimeout(() => { tooltip.remove(); }, 3000); } // ==================== 配置面板 ==================== // 创建配置面板 function createConfigPanel() { // 检查是否已存在配置面板 const existingPanel = document.querySelector('.pe-config-panel'); if (existingPanel) { existingPanel.remove(); } const panel = document.createElement('div'); panel.className = 'pe-config-panel'; // 创建配置面板HTML结构 const headerDiv = document.createElement('div'); headerDiv.className = 'pe-config-header'; headerDiv.innerHTML = '<h2>AI提示词增强器 - 配置</h2><button class="pe-close-btn">×</button>'; const contentDiv = document.createElement('div'); contentDiv.className = 'pe-config-content'; // 飞书配置区域 const feishuSection = document.createElement('div'); feishuSection.className = 'pe-config-section'; feishuSection.innerHTML = ` <h3>飞书多维表格配置</h3> <div class="pe-form-group"> <label>App ID:</label> <input type="text" id="feishu-app-id" placeholder="请输入飞书应用的App ID"> </div> <div class="pe-form-group"> <label>App Secret:</label> <input type="password" id="feishu-app-secret" placeholder="请输入飞书应用的App Secret"> </div> <div class="pe-form-group"> <label>飞书表格URL (可选):</label> <input type="text" id="feishu-table-url" placeholder="输入完整的飞书多维表格URL,将自动解析Base ID和Table ID"> <small style="color: #666; font-size: 12px; display: block; margin-top: 4px;">例如: https://example.feishu.cn/base/T1M4bzmLLarNLhs5jcEcwAcRn8Q?table=tbliBckxa87pskV8</small> </div> <div class="pe-form-group"> <label>Base ID:</label> <input type="text" id="feishu-base-id" placeholder="请输入多维表格的Base ID"> </div> <div class="pe-form-group"> <label>Table ID:</label> <input type="text" id="feishu-table-id" placeholder="请输入数据表的Table ID"> </div> `; // AI配置区域 const aiSection = document.createElement('div'); aiSection.className = 'pe-config-section'; aiSection.innerHTML = ` <h3>AI模型配置</h3> <div class="pe-form-group"> <label>模型名称:</label> <input type="text" id="ai-model-name" placeholder="如: moonshot-v1-8k"> </div> <div class="pe-form-group"> <label>API Base URL:</label> <input type="text" id="ai-base-url" placeholder="如: https://api.moonshot.cn/v1"> </div> <div class="pe-form-group"> <label>API Key:</label> <input type="password" id="ai-api-key" placeholder="请输入AI模型的API Key"> </div> `; // 按钮区域 const buttonSection = document.createElement('div'); buttonSection.className = 'pe-config-section'; buttonSection.innerHTML = ` <button class="pe-button pe-button-primary" id="save-config-btn">保存配置</button> <button class="pe-button pe-button-secondary" id="test-config-btn">测试连接</button> `; // 组装面板 contentDiv.appendChild(feishuSection); contentDiv.appendChild(aiSection); contentDiv.appendChild(buttonSection); panel.appendChild(headerDiv); panel.appendChild(contentDiv); // 设置当前配置值 setTimeout(() => { const appIdInput = document.getElementById('feishu-app-id'); const appSecretInput = document.getElementById('feishu-app-secret'); const tableUrlInput = document.getElementById('feishu-table-url'); const baseIdInput = document.getElementById('feishu-base-id'); const tableIdInput = document.getElementById('feishu-table-id'); const modelNameInput = document.getElementById('ai-model-name'); const baseUrlInput = document.getElementById('ai-base-url'); const apiKeyInput = document.getElementById('ai-api-key'); if (appIdInput) appIdInput.value = FEISHU_CONFIG.APP_ID || ''; if (appSecretInput) appSecretInput.value = FEISHU_CONFIG.APP_SECRET || ''; if (tableUrlInput) tableUrlInput.value = GM_getValue('feishu_table_url', '') || ''; if (baseIdInput) baseIdInput.value = FEISHU_CONFIG.BASE_ID || ''; if (tableIdInput) tableIdInput.value = FEISHU_CONFIG.TABLE_ID || ''; if (modelNameInput) modelNameInput.value = AI_CONFIG.MODEL_NAME || ''; if (baseUrlInput) baseUrlInput.value = AI_CONFIG.BASE_URL || ''; if (apiKeyInput) apiKeyInput.value = AI_CONFIG.API_KEY || ''; // 添加URL输入框的事件监听器 if (tableUrlInput) { tableUrlInput.addEventListener('input', function() { const url = this.value.trim(); if (url) { try { const { baseId, tableId } = parseFeishuUrl(url); if (baseIdInput) baseIdInput.value = baseId; if (tableIdInput) tableIdInput.value = tableId; // 显示成功提示 const small = this.nextElementSibling; if (small) { small.style.color = '#28a745'; small.textContent = `✓ 解析成功: Base ID=${baseId}, Table ID=${tableId}`; setTimeout(() => { small.style.color = '#666'; small.textContent = '例如: https://example.feishu.cn/base/T1M4bzmLLarNLhs5jcEcwAcRn8Q?table=tbliBckxa87pskV8'; }, 3000); } } catch (error) { // 显示错误提示 const small = this.nextElementSibling; if (small) { small.style.color = '#dc3545'; small.textContent = `✗ ${error.message}`; setTimeout(() => { small.style.color = '#666'; small.textContent = '例如: https://example.feishu.cn/base/T1M4bzmLLarNLhs5jcEcwAcRn8Q?table=tbliBckxa87pskV8'; }, 3000); } } } }); } }, 100); document.body.appendChild(panel); // 绑定关闭按钮事件 const closeBtn = panel.querySelector('.pe-close-btn'); closeBtn.addEventListener('click', () => { panel.remove(); }); // 绑定保存配置按钮事件 const saveBtn = panel.querySelector('#save-config-btn'); saveBtn.addEventListener('click', function() { try { const appId = document.getElementById('feishu-app-id').value.trim(); const appSecret = document.getElementById('feishu-app-secret').value.trim(); const tableUrl = document.getElementById('feishu-table-url').value.trim(); const baseId = document.getElementById('feishu-base-id').value.trim(); const tableId = document.getElementById('feishu-table-id').value.trim(); const modelName = document.getElementById('ai-model-name').value.trim(); const baseUrl = document.getElementById('ai-base-url').value.trim(); const apiKey = document.getElementById('ai-api-key').value.trim(); // 如果有URL但没有手动填写Base ID和Table ID,尝试从URL解析 let finalBaseId = baseId; let finalTableId = tableId; if (tableUrl && (!baseId || !tableId)) { try { const parsed = parseFeishuUrl(tableUrl); if (!baseId) finalBaseId = parsed.baseId; if (!tableId) finalTableId = parsed.tableId; } catch (error) { console.warn('URL解析失败:', error.message); } } // 保存到GM存储 GM_setValue('feishu_app_id', appId); GM_setValue('feishu_app_secret', appSecret); GM_setValue('feishu_table_url', tableUrl); GM_setValue('feishu_base_id', finalBaseId); GM_setValue('feishu_table_id', finalTableId); GM_setValue('ai_model_name', modelName); GM_setValue('ai_base_url', baseUrl); GM_setValue('ai_api_key', apiKey); // 清除访问令牌缓存 accessTokenCache = { token: null, expireTime: 0 }; console.log('配置已保存'); // 显示成功提示 if (typeof Swal !== 'undefined') { Swal.fire({ title: '保存成功', text: '配置已保存', icon: 'success', timer: 2000, showConfirmButton: false }); } else { alert('配置保存成功!'); } } catch (error) { console.error('保存配置失败:', error); if (typeof Swal !== 'undefined') { Swal.fire({ title: '保存失败', text: error.message, icon: 'error' }); } else { alert('保存配置失败: ' + error.message); } } }); // 绑定测试配置按钮事件 const testBtn = panel.querySelector('#test-config-btn'); testBtn.addEventListener('click', async function() { try { console.log('开始测试配置...'); // 显示测试中提示 if (typeof Swal !== 'undefined') { Swal.fire({ title: '测试中...', text: '正在测试连接', allowOutsideClick: false, didOpen: () => { Swal.showLoading(); } }); } // 测试飞书连接 const token = await getFeishuAccessToken(); console.log('飞书连接测试成功'); // 测试字段检查和创建 try { await ensureRequiredFields(FEISHU_CONFIG.BASE_ID, FEISHU_CONFIG.TABLE_ID, token); console.log('飞书字段检查成功'); } catch (fieldError) { console.warn('字段检查失败:', fieldError.message); } // 测试AI连接 await enhancePromptWithAI('测试连接', ''); console.log('AI连接测试成功'); // 显示成功提示 if (typeof Swal !== 'undefined') { Swal.fire({ title: '测试成功', text: '所有配置都正常工作', icon: 'success' }); } else { alert('测试成功!所有配置都正常工作'); } } catch (error) { console.error('测试配置失败:', error); if (typeof Swal !== 'undefined') { Swal.fire({ title: '测试失败', text: error.message, icon: 'error' }); } else { alert('测试失败: ' + error.message); } } }); } // ==================== 主初始化逻辑 ==================== // 主初始化函数 async function init() { try { console.log('🚀 开始初始化AI提示词增强器...'); currentSiteType = detectSiteType(); console.log('🎯 当前网站类型:', currentSiteType); if (currentSiteType === SiteType.UNKNOWN) { console.log('❌ 当前网站不受支持,初始化终止'); return; } const chatPageResult = isChatPage(); console.log('📄 聊天页面检查结果:', chatPageResult); if (!chatPageResult) { console.log('⏳ 当前页面不是聊天页面,设置页面变化监听...'); setupPageChangeListener(); return; } console.log(`✅ AI提示词增强器已加载,网站类型: ${currentSiteType}`); console.log('🔧 开始添加样式和初始化组件...'); // 添加样式 addStyles(); // 等待页面加载完成 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initializeEnhancer); } else { await initializeEnhancer(); } } catch (error) { console.error('初始化失败:', error); } } // 初始化增强器 async function initializeEnhancer() { if (isInitialized) return; try { console.log('等待页面元素加载...'); const success = await injectButton(); if (success) { isInitialized = true; console.log('AI提示词增强器初始化完成'); // 设置页面变化监听 setupMutationObserver(); // 添加快捷键支持 setupKeyboardShortcuts(); // 检查配置完整性 checkConfigCompleteness(); } else { console.warn('按钮注入失败,将在页面变化时重试'); setupRetryMechanism(); } } catch (error) { console.error('增强器初始化失败:', error); setupRetryMechanism(); } } // 设置页面变化监听 function setupPageChangeListener() { let currentUrl = window.location.href; const checkUrlChange = () => { if (window.location.href !== currentUrl) { currentUrl = window.location.href; if (isChatPage()) { console.log('检测到跳转到聊天页面,开始初始化...'); isInitialized = false; setTimeout(() => initializeEnhancer(), 1000); } } }; setInterval(checkUrlChange, 1000); // 监听popstate事件 window.addEventListener('popstate', () => { setTimeout(checkUrlChange, 100); }); } // 设置DOM变化监听 function setupMutationObserver() { const observer = new MutationObserver((mutations) => { let shouldReinject = false; mutations.forEach((mutation) => { if (mutation.type === 'childList') { // 检查按钮是否还在页面中 if (enhanceButton && !document.contains(enhanceButton)) { shouldReinject = true; } } }); if (shouldReinject) { console.log('检测到页面变化,重新注入按钮...'); setTimeout(() => { isInitialized = false; initializeEnhancer(); }, 500); } }); observer.observe(document.body, { childList: true, subtree: true }); } // 设置重试机制 function setupRetryMechanism() { setTimeout(() => { if (!isInitialized) { console.log('重试初始化...'); initializeEnhancer(); } }, 3000); } // 设置键盘快捷键 function setupKeyboardShortcuts() { document.addEventListener('keydown', (e) => { // Ctrl/Cmd + Shift + P 打开配置面板 if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'P') { e.preventDefault(); createConfigPanel(); } // Ctrl/Cmd + Enter 触发增强 if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { const activeElement = document.activeElement; const siteConfig = SITE_CONFIGS[currentSiteType]; if (siteConfig && activeElement && activeElement.matches(siteConfig.inputSelector)) { e.preventDefault(); handleEnhanceClick(); } } }); } // 检查配置完整性 function checkConfigCompleteness() { const hasFeishuConfig = FEISHU_CONFIG.APP_ID && FEISHU_CONFIG.APP_SECRET && FEISHU_CONFIG.BASE_ID && FEISHU_CONFIG.TABLE_ID; const hasAIConfig = AI_CONFIG.API_KEY && AI_CONFIG.BASE_URL && AI_CONFIG.MODEL_NAME; if (!hasFeishuConfig || !hasAIConfig) { setTimeout(() => { if (typeof Swal !== 'undefined') { Swal.fire({ title: '配置不完整', text: '请先配置飞书多维表格和AI模型信息。按 Ctrl+Shift+P 打开配置面板。', icon: 'warning', confirmButtonText: '打开配置', showCancelButton: true, cancelButtonText: '稍后配置' }).then((result) => { if (result.isConfirmed) { createConfigPanel(); } }); } }, 2000); } } // ==================== 文章采集功能 ==================== // 采集当前页面内容到飞书知识库 async function collectCurrentPage() { try { // 检查飞书配置 const hasFeishuConfig = FEISHU_CONFIG.APP_ID && FEISHU_CONFIG.APP_SECRET && FEISHU_CONFIG.BASE_ID && FEISHU_CONFIG.TABLE_ID; if (!hasFeishuConfig) { if (typeof Swal !== 'undefined') { Swal.fire({ title: '配置不完整', text: '请先配置飞书多维表格信息', icon: 'warning', confirmButtonText: '打开配置' }).then((result) => { if (result.isConfirmed) { createConfigPanel(); } }); } else { alert('请先配置飞书多维表格信息'); } return; } // 显示采集中提示 if (typeof Swal !== 'undefined') { Swal.fire({ title: '采集中...', text: '正在采集页面内容到飞书知识库', allowOutsideClick: false, didOpen: () => { Swal.showLoading(); } }); } // 获取页面信息 const title = document.title || '未知标题'; const url = window.location.href; const content = extractPageContent(); const keywords = extractKeywords(content); // 构建要保存的内容 const fullContent = `来源:${url}\n\n${content}`; // 保存到飞书 await addToFeishu(title, fullContent, keywords); // 显示成功提示 if (typeof Swal !== 'undefined') { Swal.fire({ title: '采集成功', text: `已将「${title}」采集到飞书知识库`, icon: 'success', timer: 3000, showConfirmButton: false }); } else { alert(`采集成功!已将「${title}」保存到飞书知识库`); } } catch (error) { console.error('采集页面失败:', error); if (typeof Swal !== 'undefined') { Swal.fire({ title: '采集失败', text: error.message, icon: 'error' }); } else { alert('采集失败: ' + error.message); } } } // 提取页面主要内容 function extractPageContent() { // 尝试获取文章主体内容 const selectors = [ 'article', '[role="main"]', '.content', '.article-content', '.post-content', '.entry-content', 'main', '#content', '.markdown-body' ]; let content = ''; for (const selector of selectors) { const element = document.querySelector(selector); if (element) { content = element.innerText || element.textContent || ''; if (content.trim().length > 100) { break; } } } // 如果没有找到合适的内容,使用body内容但过滤掉导航等 if (!content || content.trim().length < 100) { const body = document.body.cloneNode(true); // 移除不需要的元素 const removeSelectors = [ 'nav', 'header', 'footer', 'aside', '.nav', '.header', '.footer', '.sidebar', '.menu', '.navigation', '.ads', '.advertisement' ]; removeSelectors.forEach(selector => { const elements = body.querySelectorAll(selector); elements.forEach(el => el.remove()); }); content = body.innerText || body.textContent || ''; } // 清理内容 content = content .replace(/\s+/g, ' ') // 多个空白字符替换为单个空格 .replace(/\n\s*\n/g, '\n') // 多个换行替换为单个换行 .trim(); // 限制长度 if (content.length > 5000) { content = content.substring(0, 5000) + '...'; } return content; } // 提取关键词 function extractKeywords(content) { // 简单的关键词提取 const title = document.title || ''; const url = window.location.href; let keywords = []; // 从标题提取 if (title) { keywords.push(title.split(/[\s\-_|]+/).filter(word => word.length > 1).slice(0, 3)); } // 从URL提取 const domain = new URL(url).hostname.replace('www.', ''); keywords.push(domain); // 从内容提取(简单实现) const contentWords = content .replace(/[^\u4e00-\u9fffA-Za-z0-9\s]/g, ' ') .split(/\s+/) .filter(word => word.length > 2) .slice(0, 5); keywords.push(...contentWords); return keywords.flat().filter(Boolean).join(', '); } // 创建浮动采集按钮 function createFloatingCollectButton() { // 检查是否已存在按钮 if (document.getElementById('pe-floating-collect-btn')) { return; } const button = document.createElement('div'); button.id = 'pe-floating-collect-btn'; button.innerHTML = ` <div class="pe-floating-btn-content"> 📚 采集到飞书 </div> `; // 添加样式 button.style.cssText = ` position: fixed; top: 50%; right: 20px; transform: translateY(-50%); z-index: 10000; background: #007bff; color: white; padding: 12px 16px; border-radius: 25px; cursor: pointer; font-size: 14px; font-weight: 500; box-shadow: 0 4px 12px rgba(0, 123, 255, 0.3); transition: all 0.3s ease; user-select: none; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; `; // 悬停效果 button.addEventListener('mouseenter', () => { button.style.transform = 'translateY(-50%) scale(1.05)'; button.style.boxShadow = '0 6px 16px rgba(0, 123, 255, 0.4)'; }); button.addEventListener('mouseleave', () => { button.style.transform = 'translateY(-50%) scale(1)'; button.style.boxShadow = '0 4px 12px rgba(0, 123, 255, 0.3)'; }); // 点击事件 button.addEventListener('click', collectCurrentPage); // 右键显示配置 button.addEventListener('contextmenu', (e) => { e.preventDefault(); createConfigPanel(); }); document.body.appendChild(button); // 添加提示 setTimeout(() => { if (button.parentElement) { const tooltip = document.createElement('div'); tooltip.style.cssText = ` position: fixed; top: 50%; right: 200px; transform: translateY(-50%); background: rgba(0, 0, 0, 0.8); color: white; padding: 8px 12px; border-radius: 6px; font-size: 12px; z-index: 10001; pointer-events: none; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; `; tooltip.textContent = '右键打开配置'; document.body.appendChild(tooltip); setTimeout(() => { if (tooltip.parentElement) { tooltip.remove(); } }, 3000); } }, 1000); } // 注册菜单命令 function registerMenuCommands() { try { // 注册采集页面命令 GM_registerMenuCommand('📚 采集当前页面到飞书知识库', collectCurrentPage); // 注册配置面板命令 GM_registerMenuCommand('⚙️ 打开配置面板', createConfigPanel); console.log('菜单命令注册成功'); } catch (error) { console.warn('菜单命令注册失败:', error); } } // ==================== 启动脚本 ==================== // 创建右侧悬浮增强窗(专门用于DeepSeek等AI网站) function createFloatingEnhancer() { // 重新检测网站类型,确保准确 const siteType = detectSiteType(); console.log('悬浮窗创建时检测到的网站类型:', siteType); // 只在支持的AI网站上显示 if (siteType === SiteType.UNKNOWN) { console.log('未识别的网站类型,不创建悬浮窗'); return; } // 检查是否已存在 if (document.getElementById('pe-floating-enhancer')) { return; } const floatingEnhancer = document.createElement('div'); floatingEnhancer.id = 'pe-floating-enhancer'; floatingEnhancer.innerHTML = ` <div class="pe-floating-btn-content"> ✨ 增强提示词 </div> `; // 添加样式(参考采集按钮但使用不同颜色和位置) floatingEnhancer.style.cssText = ` position: fixed; top: calc(50% - 70px); right: 20px; transform: translateY(-50%); z-index: 10001; background: #28a745; color: white; padding: 12px 16px; border-radius: 25px; cursor: pointer; font-size: 14px; font-weight: 500; box-shadow: 0 4px 12px rgba(40, 167, 69, 0.3); transition: all 0.3s ease; user-select: none; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; `; // 悬停效果 floatingEnhancer.addEventListener('mouseenter', () => { floatingEnhancer.style.transform = 'translateY(-50%) scale(1.05)'; floatingEnhancer.style.boxShadow = '0 6px 16px rgba(40, 167, 69, 0.4)'; }); floatingEnhancer.addEventListener('mouseleave', () => { floatingEnhancer.style.transform = 'translateY(-50%) scale(1)'; floatingEnhancer.style.boxShadow = '0 4px 12px rgba(40, 167, 69, 0.3)'; }); // 点击事件 floatingEnhancer.addEventListener('click', async () => { // 如果没有初始化,尝试初始化 if (!isInitialized || !inputHandler) { console.log('尝试初始化增强器...'); await initializeEnhancer(); // 如果仍然没有初始化成功,显示错误 if (!inputHandler) { setFloatingEnhancerState('error'); setTimeout(() => { setFloatingEnhancerState('normal'); }, 2000); return; } } // 调用增强处理函数 handleEnhanceClick(); }); // 右键显示配置 floatingEnhancer.addEventListener('contextmenu', (e) => { e.preventDefault(); createConfigPanel(); }); document.body.appendChild(floatingEnhancer); console.log('✅ 悬浮增强窗创建成功!'); console.log('悬浮窗元素ID:', floatingEnhancer.id); console.log('悬浮窗已添加到页面'); // 添加提示 setTimeout(() => { if (floatingEnhancer.parentElement) { const tooltip = document.createElement('div'); tooltip.style.cssText = ` position: fixed; top: 50%; right: 90px; transform: translateY(-50%); background: rgba(0, 0, 0, 0.8); color: white; padding: 8px 12px; border-radius: 6px; font-size: 12px; z-index: 10001; pointer-events: none; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; `; tooltip.textContent = '右键打开配置'; document.body.appendChild(tooltip); setTimeout(() => { if (tooltip.parentElement) { tooltip.remove(); } }, 3000); } }, 1000); } // 设置悬浮增强窗状态 function setFloatingEnhancerState(state) { const floatingEnhancer = document.getElementById('pe-floating-enhancer'); if (!floatingEnhancer) return; switch (state) { case 'loading': floatingEnhancer.style.background = '#ffc107'; floatingEnhancer.style.boxShadow = '0 4px 12px rgba(255, 193, 7, 0.3)'; floatingEnhancer.innerHTML = ` <div class="pe-floating-btn-content"> ⏳ 增强中... </div> `; break; case 'success': floatingEnhancer.style.background = '#28a745'; floatingEnhancer.style.boxShadow = '0 4px 12px rgba(40, 167, 69, 0.3)'; floatingEnhancer.innerHTML = ` <div class="pe-floating-btn-content"> ✅ 增强完成 </div> `; break; case 'error': floatingEnhancer.style.background = '#dc3545'; floatingEnhancer.style.boxShadow = '0 4px 12px rgba(220, 53, 69, 0.3)'; floatingEnhancer.innerHTML = ` <div class="pe-floating-btn-content"> ❌ 增强失败 </div> `; break; default: floatingEnhancer.style.background = '#28a745'; floatingEnhancer.style.boxShadow = '0 4px 12px rgba(40, 167, 69, 0.3)'; floatingEnhancer.innerHTML = ` <div class="pe-floating-btn-content"> ✨ 增强提示词 </div> `; } } // 启动脚本 init(); // 注册菜单命令(在所有网站都可用) registerMenuCommands(); // 创建右侧悬浮增强窗(在AI网站上显示) function initFloatingEnhancer() { console.log('🎨 开始初始化悬浮增强窗...'); console.log('🌐 当前网站:', window.location.hostname); console.log('📍 当前路径:', window.location.pathname); console.log('🔍 当前网站类型:', currentSiteType); console.log('⏰ 页面加载状态:', document.readyState); // 延迟创建,确保页面完全加载 setTimeout(() => { console.log('⏳ 2秒延迟后开始创建悬浮窗...'); createFloatingEnhancer(); }, 2000); // 增加延迟时间到2秒 } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initFloatingEnhancer); } else { initFloatingEnhancer(); } // 创建浮动采集按钮(确保在所有网站都能看到) if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', createFloatingCollectButton); } else { createFloatingCollectButton(); } // 添加全局快捷键支持(在所有网站都可用) document.addEventListener('keydown', (e) => { // Ctrl/Cmd + Shift + P 打开配置面板 if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'P') { e.preventDefault(); createConfigPanel(); } // Ctrl/Cmd + Shift + C 采集当前页面 if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'C') { e.preventDefault(); collectCurrentPage(); } }); console.log('🚀 AI提示词增强器 - 飞书知识库版 v1.2.9 已加载'); console.log('📋 功能说明:'); console.log(' • 在支持的AI网站上增强提示词'); console.log(' • 使用飞书多维表格作为知识库'); console.log(' • 支持页面内容采集到知识库'); console.log('⌨️ 快捷键:'); console.log(' • Ctrl+Shift+P: 打开配置面板'); console.log(' • Ctrl+Shift+C: 采集当前页面'); console.log(' • Ctrl+Enter: 在输入框中触发增强'); console.log('🔧 改进内容 (v1.2.9):'); console.log(' • 🧹 简化代码逻辑:移除复杂的文本获取和验证机制'); console.log(' • 🚀 优化性能:减少不必要的调试日志和验证步骤'); console.log(' • 📋 简化粘贴流程:直接复制→全选→删除→粘贴'); console.log(' • ✨提升用户体验:减少控制台噪音,专注核心功能'); console.log(' • 🎯 保持核心功能:确保文本设置和增强功能正常工作'); // 检查浏览器兼容性 if (typeof GM_xmlhttpRequest === 'undefined') { console.warn('⚠️ 警告: GM_xmlhttpRequest 不可用,请确保使用支持的用户脚本管理器'); } if (typeof GM_setValue === 'undefined') { console.warn('⚠️ 警告: GM_setValue 不可用,配置保存功能可能无法正常工作'); } })();