当前位置: 首页 > news >正文

基于chatgpt.js的油猴脚本开发:快速构建浏览器AI助手

1. 项目概述:一个浏览器脚本的快速启动器

如果你经常在浏览器里折腾,想给网页加点儿自己的功能,或者自动化一些重复操作,那你肯定听说过油猴脚本。油猴脚本,或者说用户脚本,就像给浏览器装的一个个“外挂”,能让你在访问任何网站时,按你的想法改变它的样子或行为。但说实话,从零开始写一个油猴脚本,尤其是想让它稳定、好用、还能方便地调用像ChatGPT这样的AI能力,对很多人来说门槛不低。

这就是“KudoAI/chatgpt.js-greasemonkey-starter”这个项目出现的原因。它不是一个成品脚本,而是一个“启动器”或者说“脚手架”。你可以把它理解为一个已经搭好骨架、装好核心发动机的赛车底盘。你拿到手之后,不需要再去操心怎么造轮子、怎么连接发动机和变速箱,只需要专注于设计你的车身(也就是具体的脚本功能),然后开上赛道就行了。

这个启动器的核心价值,在于它把两个强大的东西优雅地结合在了一起:一个是油猴脚本的通用能力,另一个是chatgpt.js这个专门用于与ChatGPT网页版交互的JavaScript库。这意味着,你可以用非常少的代码,就写出一个能在任意网页上调用ChatGPT进行对话、总结、翻译、改写等操作的智能脚本。无论是想给论坛加个一键总结长帖的功能,还是给文档网站加个实时翻译侧边栏,这个启动器都为你铺平了道路。

它适合谁呢?首先,是那些有一定JavaScript基础,想快速入门油猴脚本开发的开发者。其次,是那些对AI应用感兴趣,想亲手打造一些浏览器小工具的产品经理或爱好者。最后,它也适合那些已经写过一些简单脚本,但苦于项目结构混乱、维护困难,想学习更工程化开发方式的“野生”开发者。接下来,我们就深入拆解这个启动器,看看它到底是怎么工作的,以及如何用它来打造你自己的智能脚本。

2. 核心架构与设计思路拆解

要理解这个启动器的妙处,我们得先把它拆开,看看里面到底有哪些“预制件”。它的设计思路非常清晰:标准化、模块化、开箱即用。这避免了每个开发者都从零开始重复搭建基础框架的窘境。

2.1 技术栈选型:为什么是它们?

这个启动器主要基于三个核心技术:

  1. Greasemonkey/Tampermonkey API:这是油猴脚本的运行时环境标准。选择它意味着你的脚本能兼容几乎所有主流浏览器的用户脚本管理器(如Tampermonkey、Violentmonkey),确保了最大的用户覆盖范围。
  2. chatgpt.js 库:这是一个非官方的、逆向工程得出的JavaScript库,它封装了与chatgpt.com网页版交互的复杂逻辑。使用它,你就不需要自己去研究OpenAI的网页接口是如何进行认证、发送消息、接收流式响应的,大大降低了开发难度。这是整个启动器“智能”能力的源泉。
  3. ES6+ JavaScript 与现代构建工具:项目使用现代JavaScript语法,并通常搭配如ViteWebpack这样的构建工具。这允许开发者使用模块化开发、npm包管理等现代前端工程实践,让脚本开发也能享受大型项目的开发体验,代码更易维护和扩展。

为什么这样选型?

  • 降低门槛:直接使用chatgpt.js,避免了从零开始研究ChatGPT网页端通信协议的巨大成本。这个协议可能涉及WebSocket、特定的请求头、反爬机制等,非常复杂且不稳定(因为网页端可能随时更新)。chatgpt.js社区会持续维护以适配官方变化,为开发者屏蔽了底层变动。
  • 提升可靠性:一个结构良好的启动器模板,内置了错误处理、日志记录、配置管理等最佳实践。这比开发者自己随手写的、结构松散的脚本要稳定得多,减少了脚本崩溃或行为异常的概率。
  • 便于协作与分享:标准化的项目结构、清晰的依赖声明(package.json)和构建流程,使得你的脚本项目可以轻松地在GitHub等平台分享,其他人也能一键安装依赖、进行构建和贡献代码。

2.2 项目结构解析:骨架长什么样?

一个典型的基于此启动器的项目目录结构可能如下所示(根据具体fork或克隆的版本略有差异):

your-script-project/ ├── src/ │ ├── main.js # 脚本的主入口文件,油猴元数据声明和初始化逻辑 │ ├── core/ # 核心功能模块 │ │ ├── chatgpt.js # 封装或直接引入的chatgpt.js客户端 │ │ └── utils.js # 通用工具函数,如DOM操作、消息格式化等 │ ├── ui/ # 用户界面相关模块 │ │ ├── panel.js # 侧边栏或浮动面板的UI逻辑 │ │ └── styles.css # 脚本的样式文件 │ └── config.js # 脚本的配置文件,如API端点、默认提示词等 ├── dist/ # 构建输出目录,最终的用户脚本文件(.user.js)在这里 ├── package.json # 项目依赖和脚本定义 ├── vite.config.js # (如果使用Vite)构建配置文件 └── README.md # 项目说明文档

关键文件解读:

  • src/main.js:这是心脏。它的开头部分必须包含油猴脚本的元数据块,用==UserScript==注释包裹。这里面定义了脚本的名称、命名空间、版本、描述、作者、匹配的网站URL(@match)、所需的权限(@grant)等。启动器已经为你写好了这些元数据的模板,你只需要填空即可。
  • dist/目录:现代前端开发很少直接写一个巨大的、未压缩的.user.js文件。我们通常在src/目录下用模块化的方式开发,然后通过构建工具(如Vite)将代码打包、压缩、并注入油猴元数据,最终生成一个适合分发的、单一的xxx.user.js文件放在dist/目录。用户安装的就是这个文件。
  • package.json:它声明了项目依赖(如chatgpt.js),以及构建和开发的命令(如npm run buildnpm run dev)。这让项目管理变得非常规范。

注意chatgpt.js本身可能通过npm安装,也可能需要你手动将某个版本的库文件放入项目。具体方式需要查看你使用的启动器版本的README。有些启动器可能已经将其作为子模块(git submodule)引入。

2.3 核心工作流程:从启动到交互

理解了这个骨架,我们再来看看脚本从安装到运行的“生命流程”:

  1. 用户安装:用户将dist/目录下生成的.user.js文件链接添加到Tampermonkey中。
  2. 脚本加载:当用户访问匹配@match规则的网站时,Tampermonkey会自动加载并执行这个脚本。
  3. 初始化:脚本的立即执行函数(IIFE)开始运行。首先,它会检查当前页面是否已经加载完成(监听DOMContentLoaded或直接操作),然后初始化chatgpt.js客户端。这个初始化过程可能包括检查用户是否已在chatgpt.com登录(通过读取本地存储的令牌),或准备一个后台的通信通道。
  4. UI注入:脚本通常会向页面注入自定义的UI元素,比如一个浮动按钮、一个侧边栏或者一个集成到网站原有UI中的输入框。这是脚本与用户交互的界面。
  5. 事件监听:脚本会监听用户事件,比如点击浮动按钮、在选中的文本上右键选择自定义菜单项、或者在注入的输入框中按下回车。
  6. 调用AI:当触发事件时,脚本会收集上下文信息(如当前选中的文本、整个网页的正文、所在的URL等),然后通过初始化好的chatgpt.js客户端,向ChatGPT发送一个构造好的请求。请求中包含了提示词(Prompt)和上下文。
  7. 处理与展示:脚本接收ChatGPT的流式或非流式响应,并实时或最终地将结果展示在之前注入的UI元素中,比如写入一个弹出的对话框,或者直接替换页面上的某段文字。

这个流程的巧妙之处在于,开发者只需要关心第5、6步:即“在什么场景下触发”以及“给ChatGPT发送什么指令”。底层的页面注入、通信协议、响应处理,启动器和chatgpt.js库都已经帮你搞定了。

3. 关键模块深度解析与实操要点

现在,我们深入到几个最关键的内部模块,看看它们具体是如何实现的,以及在开发时需要注意什么。

3.1 油猴脚本元数据(Metadata)的精细配置

元数据块是脚本的“身份证”和“说明书”,它告诉脚本管理器如何管理你的脚本。启动器模板会提供一个基础版本,但你需要根据自己脚本的特性进行精细调整。

// ==UserScript== // @name 我的智能网页助手 // @namespace https://github.com/yourname/your-repo // @version 1.0.0 // @description 利用ChatGPT增强网页浏览体验,提供总结、翻译等功能。 // @author YourName // @match https://*.example.com/* // @match https://news.ycombinator.com/* // @icon https://www.google.com/s2/favicons?domain=openai.com // @grant GM_notification // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @connect chatgpt.com // @require https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@latest/dist/chatgpt.min.js // @license MIT // ==/UserScript==

关键配置解析与避坑指南:

  • @match/@include/@exclude:这是控制脚本在哪些网站运行的核心规则。

    • @match最常用,语法更精确。例如https://*.example.com/*匹配所有example.com的子域名下的所有页面。
    • @include更宽松,支持简单的通配符。
    • @exclude用于排除特定页面。
    • 避坑点:规则过于宽泛(如*://*/*)可能导致脚本在不需要的网站上运行,消耗资源并可能引发冲突。务必精确匹配。同时,注意某些网站(如单页应用SPA)在导航时URL的hash(#)部分变化而页面不刷新,此时需要脚本具备监听路由变化的能力。
  • @grant:声明脚本需要使用的特殊Tampermonkey API权限。这是安全沙箱机制的一部分。

    • GM_notification:发送桌面通知。
    • GM_setValue/GM_getValue:在本地存储键值对数据,用于保存用户设置、历史记录等。
    • GM_addStyle:向页面动态添加CSS样式,这是注入UI样式的标准方式。
    • 避坑点:只声明你确实需要的权限。未声明的API将无法使用。GM_*API是异步的,使用时需要注意回调或使用async/await(如果环境支持Promise版本)。
  • @connect:这是一个极其重要且易错的配置。它声明脚本需要与之通信的外部域名。由于chatgpt.js需要与chatgpt.com交互,必须在此处添加@connect chatgpt.com。否则,脚本管理器会阻止向该域名发起的请求(如fetchXMLHttpRequest),导致AI功能完全失效。

    • 避坑点:如果你还调用了其他外部API(比如你自己的后端服务),也需要在这里添加相应的域名。
  • @require:用于引入外部库。这里可以直接引入chatgpt.js的CDN版本。但更常见的做法是在项目中通过npm安装,然后在构建过程中打包进最终的脚本。使用构建打包的方式能更好地管理版本依赖,避免因CDN不稳定或库更新导致脚本出错。

3.2 chatgpt.js 客户端的初始化与使用

chatgpt.js是这个启动器的灵魂。它的初始化通常不是简单的new ChatGPT(),因为需要处理认证状态。

典型的初始化模式:

// 在 main.js 或专门的 core/chatgpt.js 模块中 async function initChatGPTClient() { // 1. 检查当前标签页是否就是chatgpt.com // 这是一种常见策略:脚本依赖一个后台的、已登录的ChatGPT页面来通信。 const chatgptTabs = await browser.runtime.sendMessage({ type: 'GET_CHATGPT_TAB' }); // 这里假设你有一个后台脚本(background page/service worker)来管理这个“后台页” if (!chatgptTabs || chatgptTabs.length === 0) { // 2. 如果没有找到,可以引导用户打开一个,或者尝试使用其他认证方式(如cookie,但更复杂且不稳定) showNotification('请先打开并登录 chatgpt.com', 'error'); return null; } // 3. 连接到该标签页,建立通信 // chatgpt.js 库可能提供了自己的连接工厂函数 const client = new ChatGPT.Client({ tabId: chatgptTabs[0].id, // 其他配置,如模型选择、超时时间等 }); // 4. 测试连接 try { await client.sendMessage('Hello'); // 发送一个测试消息 console.log('ChatGPT client initialized successfully.'); return client; } catch (error) { console.error('Failed to initialize ChatGPT client:', error); showNotification('连接ChatGPT失败,请确保页面已登录', 'error'); return null; } }

实操心得与注意事项:

  1. 后台页依赖:许多基于chatgpt.js的方案都依赖于一个始终打开并保持登录状态的chatgpt.com标签页作为“后台代理”。你的脚本通过浏览器扩展API(如chrome.runtime)与这个后台页通信,再由后台页上的chatgpt.js实例与OpenAI服务器交互。这意味着用户需要先手动打开并登录ChatGPT网页。
  2. 认证状态维持:ChatGPT的登录会话(session)可能会过期。你的脚本需要处理“未登录”或“会话失效”的情况,并友好地提示用户重新登录。一种策略是定期发送一个轻量级的测试请求来检查连接状态。
  3. 速率限制与错误处理:OpenAI对未付费账户有严格的请求速率限制。你的脚本必须健壮地处理429 Too Many Requests等错误,实现请求队列、退避重试机制(如指数退避),并给用户清晰的等待提示,而不是让脚本卡死或无限报错。
  4. 模型上下文管理chatgpt.js通常默认使用ChatGPT网页端当前选择的模型(如GPT-3.5, GPT-4)。如果你的功能对模型有特定要求,需要在代码中明确,并考虑如何让用户知晓或选择。

3.3 用户界面(UI)的构建与样式隔离

油猴脚本的UI需要无缝地融入宿主页面,但又不能被宿主页面的样式污染,也不能污染宿主页面。这是UI构建的核心挑战。

构建策略:

  1. 使用Shadow DOM(推荐):这是实现样式隔离的现代Web标准。你可以创建一个Shadow Root,将你的UI元素挂载进去。这样,你的CSS样式就只在这个Shadow Tree内生效,与主页面完全隔离。

    function createFloatingButton() { const container = document.createElement('div'); container.id = 'my-script-container'; const shadow = container.attachShadow({ mode: 'open' }); // 创建Shadow DOM // 添加样式 const style = document.createElement('style'); style.textContent = ` button { background: #007bff; color: white; border: none; padding: 10px; border-radius: 50%; } /* 这些样式不会影响页面其他部分 */ `; shadow.appendChild(style); // 添加按钮 const button = document.createElement('button'); button.textContent = 'AI'; shadow.appendChild(button); document.body.appendChild(container); return button; }
  2. 使用GM_addStyle并提高CSS特异性:如果因为兼容性等原因不使用Shadow DOM,可以通过GM_addStyle注入样式,并为你所有的UI元素类名加上一个独特的前缀,然后使用非常高的CSS选择器特异性来覆盖页面样式。

    GM_addStyle(` .my-script-unique-btn { all: initial !important; /* 尝试重置所有属性 */ background: #007bff !important; color: white !important; /* ... */ } `);

    注意:滥用!importantall: initial可能带来不可预料的副作用,且无法完全保证隔离。Shadow DOM是更优解。

  3. UI组件库的选择:对于复杂的UI,可以考虑使用轻量级的UI库,如Preact+Material-Web-Components,或者自己编写简单的组件。启动器模板可能会包含一个基础的UI框架。

实操心得:

  • 定位策略:浮动元素通常使用position: fixed并指定z-index为一个很大的值(如999999)以确保在最上层。但要小心某些网站自己的模态框可能有更高的z-index
  • 响应式考虑:你的UI在小屏幕(如手机浏览器)上可能也需要可用。使用相对单位(rem,vw)和媒体查询。
  • 性能:避免在滚动等高频事件中直接操作DOM。使用防抖(debounce)或节流(throttle)技术。

4. 从零开始打造你的第一个智能脚本:实操流程

理论说了这么多,我们来动手实现一个具体的功能:“网页文本智能总结”脚本。这个脚本的功能是:用户在网页上选中一段文字,右键菜单会出现一个“使用ChatGPT总结”的选项,点击后脚本会将选中的文本发送给ChatGPT,并将返回的总结显示在一个优雅的浮动面板中。

4.1 环境准备与项目初始化

假设你已经Fork或克隆了KudoAI/chatgpt.js-greasemonkey-starter的一个模板仓库。

  1. 安装Node.js和npm:确保你的开发环境已安装Node.js(建议LTS版本)和npm。
  2. 获取项目代码
    git clone https://github.com/your-fork/chatgpt.js-greasemonkey-starter.git my-smart-summarizer cd my-smart-summarizer
  3. 安装依赖
    npm install
    这个命令会根据package.json安装所有依赖,包括chatgpt.js(如果已配置在依赖中)以及构建工具如Vite。
  4. 探索项目结构:打开项目,熟悉src/目录下的文件。找到main.js,这是我们的主战场。

4.2 修改元数据与主框架

打开src/main.js,首先更新元数据块,使其符合我们的新脚本。

// ==UserScript== // @name 网页智能总结助手 (Smart Page Summarizer) // @namespace https://github.com/yourname/my-smart-summarizer // @version 0.1.0 // @description 选中网页文本,一键调用ChatGPT生成简洁摘要。 // @author YourName // @match https://*/* // @icon https://www.google.com/s2/favicons?domain=openai.com // @grant GM_registerMenuCommand // @grant GM_notification // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @connect chatgpt.com // @require https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@latest/dist/chatgpt.min.js // @license MIT // ==/UserScript== (function() { 'use strict'; // 主逻辑将在这里编写 console.log('智能总结助手脚本加载成功!'); })();

修改点说明:

  • @match:我们暂时设置为https://*/*以在所有HTTPS网站测试,后期可以收窄。
  • @grant:新增了GM_registerMenuCommand,用于注册油猴脚本管理器自身的菜单命令(作为备用方案)。我们主要用自定义右键菜单,但多一个入口无妨。
  • @connect:务必保留,这是与ChatGPT通信的关键。

4.3 实现核心逻辑:右键菜单与AI调用

接下来,我们在IIFE内部编写核心逻辑。我们将分步骤实现:

步骤1:初始化与状态管理

// 在IIFE内部 let chatGPTClient = null; let isProcessing = false; // 防止重复请求 // 初始化函数 async function init() { console.log('初始化脚本...'); // 初始化UI样式 injectStyles(); // 初始化ChatGPT客户端(这里简化,实际需按3.2节实现) chatGPTClient = await initChatGPTClient(); if (chatGPTClient) { // 注册右键菜单 setupContextMenu(); console.log('脚本初始化完成,已注册右键菜单。'); } else { console.warn('ChatGPT客户端初始化失败,部分功能不可用。'); } } // 调用初始化 init();

步骤2:注入样式 (injectStyles)

function injectStyles() { const css = ` #smart-summarizer-panel { position: fixed; top: 20px; right: 20px; width: 350px; max-height: 500px; background: white; border: 1px solid #ccc; 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: none; /* 默认隐藏 */ overflow: hidden; } .summarizer-header { padding: 12px 16px; background: #f8f9fa; border-bottom: 1px solid #dee2e6; display: flex; justify-content: space-between; align-items: center; font-weight: bold; } .summarizer-close { background: none; border: none; font-size: 1.5rem; cursor: pointer; color: #6c757d; } .summarizer-content { padding: 16px; max-height: 400px; overflow-y: auto; } .summarizer-loading { text-align: center; color: #6c757d; padding: 20px; } .summarizer-error { color: #dc3545; padding: 16px; } /* 自定义右键菜单项样式 */ .smart-summarize-option { color: #007bff !important; font-weight: bold !important; } `; GM_addStyle(css); }

步骤3:设置右键菜单 (setupContextMenu)我们需要覆盖浏览器的默认右键菜单行为,这需要监听contextmenu事件,并在合适的时候注入我们的菜单项。一个更稳健的做法是直接修改文档的contextmenu事件,在菜单显示前动态添加项。

function setupContextMenu() { document.addEventListener('contextmenu', function(event) { // 获取选中的文本 const selectedText = window.getSelection().toString().trim(); if (!selectedText || selectedText.length < 10) { // 太短的文本不提供总结 return; } // 延迟执行,以确保原生的上下文菜单已经准备弹出 setTimeout(() => { // 查找页面中可能已经存在的自定义菜单项容器 let customMenu = document.getElementById('smart-summarizer-custom-menu'); if (!customMenu) { customMenu = document.createElement('div'); customMenu.id = 'smart-summarizer-custom-menu'; customMenu.style.cssText = 'position: absolute; z-index: 10001; background: white; border: 1px solid #ddd; box-shadow: 2px 2px 5px rgba(0,0,0,0.2); display: none;'; document.body.appendChild(customMenu); } // 清空并添加新的菜单项 customMenu.innerHTML = ''; const menuItem = document.createElement('div'); menuItem.textContent = '📝 使用AI总结'; menuItem.className = 'smart-summarize-option'; menuItem.style.cssText = 'padding: 8px 16px; cursor: pointer;'; menuItem.onclick = function(e) { e.preventDefault(); e.stopPropagation(); customMenu.style.display = 'none'; summarizeText(selectedText, event); // event用于定位,可能用于显示面板 }; customMenu.appendChild(menuItem); // 定位菜单到鼠标位置 customMenu.style.left = `${event.pageX}px`; customMenu.style.top = `${event.pageY}px`; customMenu.style.display = 'block'; // 点击页面其他地方隐藏自定义菜单 const hideMenu = function(e) { if (customMenu && !customMenu.contains(e.target)) { customMenu.style.display = 'none'; document.removeEventListener('click', hideMenu); } }; setTimeout(() => document.addEventListener('click', hideMenu), 10); }, 10); // 一个小的延迟确保执行顺序 }, true); // 使用捕获阶段 }

注意:这种方式修改右键菜单相对复杂且可能与其他脚本冲突。更高级的做法是使用浏览器的Context Menus API,但这需要脚本运行在扩展的background page上下文中,对于纯油猴脚本限制较多。上述方法是一个在内容脚本中可行的“Hack”。

步骤4:实现总结函数 (summarizeText) 与UI面板

async function summarizeText(text, originalEvent) { if (isProcessing) { GM_notification({ text: '正在处理上一个请求,请稍候...', title: '提示' }); return; } if (!chatGPTClient) { GM_notification({ text: 'ChatGPT未就绪,请确保已打开chatgpt.com并登录。', title: '错误' }); return; } isProcessing = true; // 显示或创建结果面板 let panel = document.getElementById('smart-summarizer-panel'); if (!panel) { panel = createResultPanel(); document.body.appendChild(panel); } showPanel(panel, originalEvent); // 更新面板内容为“加载中” const contentArea = panel.querySelector('.summarizer-content'); contentArea.innerHTML = `<div class="summarizer-loading">正在生成总结,请稍候...</div>`; try { // 构造一个优化的提示词(Prompt) const prompt = `请用中文对以下文本进行总结,要求简洁明了,抓住核心要点,总结长度控制在原文的30%以内:\n\n${text}`; // 调用chatgpt.js客户端发送请求 // 注意:这里需要根据你实际使用的chatgpt.js API进行调整 const response = await chatGPTClient.sendMessage({ prompt: prompt, // 可能还有其他参数,如 model, stream 等 }); // 假设response是一个包含文本的对象 const summary = response.text || '未收到有效总结。'; // 更新面板显示结果 contentArea.innerHTML = `<p>${summary.replace(/\n/g, '<br>')}</p>`; // 可选:将结果保存到本地(GM_setValue) const history = JSON.parse(GM_getValue('summary_history', '[]')); history.unshift({ text, summary, time: new Date().toISOString() }); if (history.length > 50) history.pop(); // 只保留最近50条 GM_setValue('summary_history', JSON.stringify(history)); } catch (error) { console.error('总结请求失败:', error); contentArea.innerHTML = `<div class="summarizer-error">总结失败: ${error.message || '未知错误'}</div>`; } finally { isProcessing = false; } } function createResultPanel() { const panel = document.createElement('div'); panel.id = 'smart-summarizer-panel'; panel.innerHTML = ` <div class="summarizer-header"> <span>AI总结结果</span> <button class="summarizer-close" aria-label="关闭">×</button> </div> <div class="summarizer-content"></div> `; const closeBtn = panel.querySelector('.summarizer-close'); closeBtn.addEventListener('click', () => { panel.style.display = 'none'; }); return panel; } function showPanel(panel, event) { panel.style.display = 'block'; // 可以做一些简单的定位,比如放在鼠标右下方,避免遮挡 panel.style.top = `${event.pageY + 10}px`; panel.style.left = `${Math.min(event.pageX + 10, window.innerWidth - 350)}px`; // 确保不超出屏幕 }

4.4 构建与测试

  1. 开发模式:许多启动器模板配置了开发服务器(如npm run dev)。它会启动一个服务,并监听src/目录下文件的变化,自动重新构建。你可以在浏览器中安装一个指向本地开发服务器的脚本链接(Tampermonkey支持安装来自URL的脚本),实现实时调试。
  2. 构建生产版本:当你完成开发后,运行构建命令(通常是npm run build)。这个过程会:
    • 打包和压缩你的JavaScript代码。
    • src/main.js顶部的元数据块提取出来,添加到打包后文件的开头。
    • 将最终生成的.user.js文件输出到dist/目录。
  3. 安装测试:将dist/目录下的.user.js文件拖到浏览器的Tampermonkey管理页面进行安装。然后访问一个匹配@match规则的网站(如一篇新闻博客),选中一段长文字,右键看看是否出现了你的“使用AI总结”菜单项,并测试整个流程。

5. 进阶优化与问题排查实录

一个基础功能跑通后,我们可以让它变得更强大、更稳定。以下是几个常见的进阶方向和踩坑记录。

5.1 功能增强:从总结到多功能工具箱

我们的脚本可以轻松扩展为一个小型工具箱:

  • 多语言翻译:修改提示词为“将以下文本翻译成[目标语言]:”。
  • 语气转换:例如“将以下文字改写得更加正式/口语化:”。
  • 代码解释:针对选中的代码片段,提示“解释以下代码的功能:”。
  • 自定义指令模板:在脚本设置中允许用户保存和选择自己常用的提示词模板。

实现上,可以在右键菜单中增加子菜单,或者在浮动面板上增加一个功能选择下拉框。状态(如当前选择的模板)可以使用GM_setValue/GM_getValue持久化。

5.2 性能与稳定性优化

  1. 请求队列与限流

    class RequestQueue { constructor(maxConcurrent = 1) { this.queue = []; this.maxConcurrent = maxConcurrent; this.activeCount = 0; } async add(requestFn) { return new Promise((resolve, reject) => { const task = async () => { this.activeCount++; try { const result = await requestFn(); resolve(result); } catch (error) { reject(error); } finally { this.activeCount--; this.next(); } }; this.queue.push(task); this.next(); }); } next() { if (this.activeCount < this.maxConcurrent && this.queue.length > 0) { const task = this.queue.shift(); task(); } } } // 全局使用一个队列实例 const gptQueue = new RequestQueue(1); // 串行执行,避免触发速率限制 // 在 summarizeText 中 const response = await gptQueue.add(() => chatGPTClient.sendMessage({ prompt }));
  2. 错误重试与回退:网络请求可能失败。实现一个简单的重试逻辑。

    async function sendWithRetry(requestFn, maxRetries = 2) { let lastError; for (let i = 0; i < maxRetries; i++) { try { return await requestFn(); } catch (error) { lastError = error; console.warn(`请求失败,第${i+1}次重试...`, error); if (i < maxRetries - 1) { await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i))); // 指数退避 } } } throw lastError; // 所有重试都失败后抛出错误 }
  3. 上下文管理:对于长文本,ChatGPT有令牌限制。需要在发送前检查文本长度,如果过长,可以自动截断或分段总结。

    function truncateText(text, maxTokens = 3000) { // 简单估算:1个token约等于0.75个英文单词或0.4个中文字符。 // 这是一个非常粗略的估算,实际应使用更精确的tokenizer。 const approxTokens = text.length * 0.4; // 对中文的粗略估计 if (approxTokens > maxTokens) { const ratio = maxTokens / approxTokens; const truncatedLength = Math.floor(text.length * ratio); return text.substring(0, truncatedLength) + '...【文本过长,已截断】'; } return text; }

5.3 常见问题排查(FAQ)

Q1: 脚本安装后完全不工作,控制台也没有错误。

  • 检查1:Tampermonkey脚本是否启用?图标是亮的吗?
  • 检查2:当前网站URL是否匹配@match规则?去Tampermonkey仪表盘查看脚本详情。
  • 检查3@connect指令是否包含了所有必要的域名(特别是chatgpt.com)?
  • 检查4:是否有其他脚本冲突?尝试在无痕模式下禁用其他脚本测试。

Q2: 右键菜单没有出现“使用AI总结”选项。

  • 检查1:选中的文本长度是否太短?代码中设置了selectedText.length < 10的阈值。
  • 检查2:网站是否禁用了默认的右键菜单或使用了复杂的框架(如Google Docs)?这些网站可能完全重写了右键事件,我们的简单监听器会失效。对于这类网站,可能需要更复杂的注入策略,或者考虑使用快捷键(如Ctrl+Shift+S)来触发。
  • 检查3:控制台是否有JavaScript错误?打开开发者工具查看。

Q3: 点击菜单后,提示“ChatGPT未就绪”或请求失败。

  • 检查1:是否已经打开并登录了chatgpt.com?在一个标签页中手动访问并登录。
  • 检查2chatgpt.js客户端初始化是否成功?检查initChatGPTClient函数的逻辑和错误信息。
  • 检查3:ChatGPT网页端会话是否已过期?尝试在chatgpt.com标签页中刷新一下。
  • 检查4:是否触发了OpenAI的速率限制?免费用户请求频率不宜过高,加入队列和延迟。

Q4: 生成的总结面板样式被网站CSS覆盖了,布局错乱。

  • 检查1:是否使用了Shadow DOM?这是最有效的隔离方案。如果用了GM_addStyle,确保你的CSS选择器特异性足够高,并谨慎使用!important
  • 检查2:你的UI元素是否被网站的高z-index元素覆盖?尝试增加面板的z-index值(如100000)。

Q5: 在GitHub/Gmail等复杂页面上脚本运行缓慢。

  • 优化1:确保你的DOM查询和事件监听是高效的。避免在滚动等高频事件中执行复杂操作。
  • 优化2:检查是否有内存泄漏。长时间停留在SPA页面时,旧的监听器是否被正确移除?
  • 优化3:考虑使用MutationObserver来延迟初始化,直到页面主体内容加载完成,而不是在DOMContentLoaded时就执行所有操作。

开发油猴脚本,尤其是涉及与复杂网页交互和外部API调用的脚本,是一个不断调试和适配的过程。每个网站都可能是一个独特的“战场”。这个启动器为你提供了强大的武器和稳固的基地,但如何打好每一场仗,还需要你根据实际情况灵活运用这些工具和策略。

http://www.jsqmd.com/news/821460/

相关文章:

  • 无锡亨得利官方手表养护有哪些项目?2026年5月最全项目清单+价格参考+服务流程详解(附全国官方网点地址) - 亨得利腕表维修中心
  • Pydantic与Logfire集成:数据验证事件化与可观测性实践
  • 怎样免费去掉图片水印?2026年免费去水印工具推荐|在线vs软件对比
  • Blender动画GIF终极指南:用Bligify插件轻松制作专业级动态图像
  • 多行业极端工况下机封定制的选型与实测复盘 - 奔跑123
  • 六边形网格地图生成与路径规划避坑指南:奇偶行坐标转换的三种方法对比
  • AUTOSAR网络管理实战:从报文解析到状态机调试,一个CANoe Trace的完整分析案例
  • Git 热修复 hotfix 分支怎么合并回 master 和 develop 才规范
  • Temu 批量报活动效率提升 10 倍:凌风工具箱如何终结手动申报痛点
  • DeTikZify:基于深度学习的LaTeX公式与图表逆向解析技术详解
  • Taotoken用量看板如何让我们清晰掌握各模型消耗与团队使用习惯
  • Arm RD-V3-R1 FVP虚拟开发平台核心技术与应用实践
  • NsEmuTools:简化NS模拟器管理的三步解决方案
  • 实战指南:四款开源弱口令审计工具的场景化应用与效能对比
  • 携程任我行卡用不完别浪费!三种回收方法,哪种最适合你? - 可可收
  • 工业 DC-DC 性能深度对比解析|钡特电源 DF1-05D15LS 与 E0515S-1WR3 封装互通
  • 大连全域黄金变现大盘点——六大正规品牌实力解读与区域服务地图 - 奢侈品回收测评
  • Windows热键侦探:3分钟快速找出占用快捷键的程序
  • 智慧养老系统入住老人请假管理模块:规范流程·精准管控,守护老人外出安全
  • 从执行到主导:开发者如何构建技术领导力与高效工程体系
  • extra字段超长截断-码点陷阱
  • TPAMI 投稿微信群成立!
  • 云主机/虚拟机迁移后必看:避开dracut紧急模式,搞定grub2和initramfs引导修复
  • AI系统提示词与模型仓库:提升大模型输出质量的关键
  • BilibiliDown终极指南:免费跨平台B站视频下载器完全教程
  • Node.js 命令行工具开发实战:从日期计算到终端可视化
  • AI开发者必备:开源资源导航站ai-hub的设计哲学与高效使用指南
  • 2026 温州黄金回收哪家靠谱?8 家实体门店全名单 + 实时报价 + 避坑指南 - 润富黄金珠宝行
  • 用74LS161和555芯片搭个复古数字钟:我的课程设计避坑实录(附完整电路图)
  • Kubernetic:提升Kubernetes管理效率的桌面客户端工具