chatgpt.js:专为ChatGPT网页版打造的JavaScript自动化工具库
1. 项目概述一个为ChatGPT网页版而生的JavaScript瑞士军刀如果你和我一样是个重度ChatGPT网页版用户同时又是个喜欢折腾的前端开发者那你肯定有过这样的念头要是能写个脚本自动帮我整理对话、一键导出聊天记录或者给那个输入框加点快捷键该多好。但当你真正打开浏览器开发者工具面对ChatGPT那复杂且频繁变动的DOM结构时一股无力感就会油然而生。每次OpenAI更新界面你辛辛苦苦写的选择器就全失效了维护成本高得吓人。这就是chatgpt.js诞生的背景。它不是另一个AI模型而是一个纯粹的前端JavaScript库。你可以把它理解成专门为ChatGPT网页版以及Poe等类似平台定制的“jQuery”。它的核心使命只有一个屏蔽底层DOM的复杂性为你提供一套稳定、易用的API让你能轻松地以编程方式与ChatGPT的界面进行交互。无论是想开发用户脚本UserScript来增强功能还是构建浏览器扩展甚至是做一些自动化测试chatgpt.js都试图成为那个可靠的基石。它把从页面中提取聊天内容、模拟用户点击发送、监听响应状态这些脏活累活都封装好了你只需要关心你的业务逻辑。我最初接触它是因为想做一个自动保存聊天记录到本地的工具手动操作了几次后深感效率低下直到发现了这个库才真正把想法落地。2. 核心设计哲学为什么是“瑞士军刀”2.1 面向变化的防御性编程ChatGPT的网页界面并非一成不变的公共API它没有提供官方的JavaScript SDK。这意味着任何基于其DOM结构的自动化操作都极其脆弱。今天按钮的类名是.btn明天可能就变成了.button-primary。chatgpt.js 最核心的设计思想就是应对这种不确定性。它内部实现了一套健壮的DOM查询和状态监听机制。举个例子获取最新的AI回复库不会只依赖一个固定的CSS选择器比如div[data-message-author-role\assistant\]:last-child而是会结合多个特征如角色标识、消息容器结构、甚至时序进行综合判断并在主选择器失效时尝试备用方案。这种设计使得你的代码在ChatGPT前端小幅更新时有很大概率无需修改就能继续运行库本身会通过更新版本来适应大的DOM变更。2.2 极致灵活与“猜测即正确”的API风格翻阅chatgpt.js的文档或源码你会发现一个有趣的现象完成同一个任务往往有多种方法。比如获取最后一条回复你可以用chatgpt.getLastResponse()也可以用chatgpt.response.getLast()甚至chatgpt.get(reply, last)也能工作。这不是设计冗余而是一种刻意的“宽容性”设计。这种设计降低了开发者的心智负担。你不需要死记硬背某个特定的方法名只要你的意图足够清晰“获取”、“最后”、“回复”库有很大概率能理解并执行。这背后通常是基于一个统一的、支持自然语言关键词映射的内部路由机制。对于快速原型开发和脚本编写来说这种灵活性非常友好。2.3 轻量级与无依赖作为一个需要被注入到第三方网页中运行的库体积和纯净度至关重要。chatgpt.js 被刻意设计为不依赖任何其他大型框架如React、Vue、jQuery。它使用纯粹的Vanilla JS原生JavaScript实现压缩后的体积控制在几十KB级别确保加载迅速且不会与目标页面原有的JavaScript环境发生冲突。这种“零依赖”特性也意味着极高的兼容性。无论是通过UserScript管理器如Tampermonkey、浏览器扩展内容脚本还是直接粘贴到浏览器控制台它都能即插即用。3. 环境准备与多种导入方式详解chatgpt.js 的使用场景非常灵活因此也提供了多种导入方式。选择哪一种完全取决于你的项目形态和运行环境。3.1 场景一用户脚本Greasemonkey/Tampermonkey/Violentmonkey这是chatgpt.js最典型、最便捷的使用场景。用户脚本管理器允许你为特定网站注入自定义的JavaScript代码。操作步骤安装一个用户脚本管理器扩展如TampermonkeyChrome/Edge/Firefox等主流浏览器均支持。点击管理器图标选择“创建新脚本”。在脚本的元信息部分UserScript和/UserScript之间使用require指令直接引入chatgpt.js的CDN地址。// UserScript // name 我的ChatGPT增强工具 // namespace http://tampermonkey.net/ // version 1.0 // description 尝试使用chatgpt.js增强ChatGPT // author You // match https://chatgpt.com/* // grant none // require https://cdn.jsdelivr.net/npm/kudoai/chatgpt.jslatest/dist/chatgpt.min.js // /UserScript (function() { use strict; // 你的代码写在这里。库已经加载完成可以直接使用 chatgpt 对象。 console.log(chatgpt.js 已加载); // 例如获取当前对话的标题 const chatTitle chatgpt.getChatTitle(); console.log(当前对话标题, chatTitle); })();注意在生产环境中建议将latest替换为具体的版本号如3.9.0以避免因库的更新导致你的脚本出现意外行为。开发时可以使用latest来获取最新特性。优势部署极其简单用户安装脚本即可使用无需关心库文件的存放。脚本管理器会负责库的缓存和更新检查。3.2 场景二浏览器扩展Chrome Extension, Firefox Add-on等开发浏览器扩展时由于安全策略内容安全策略CSP限制通常无法直接通过require加载远程CDN资源。这时需要将库文件本地化。操作步骤以Chrome Extension Manifest V3为例下载库文件在项目根目录下创建一个lib文件夹然后将chatgpt.js的源码文件保存于此。你可以直接从GitHub仓库的dist目录下载chatgpt.min.js或者通过npm安装后从node_modules中复制。配置manifest.json声明该库文件为“web_accessible_resources”允许内容脚本访问。{ manifest_version: 3, name: My ChatGPT Extension, version: 1.0, // ... 其他配置 web_accessible_resources: [{ resources: [lib/chatgpt.min.js], matches: [https://chatgpt.com/*] }], content_scripts: [{ js: [content.js], matches: [https://chatgpt.com/*] }] }在内容脚本中动态导入在你的扩展内容脚本如content.js中使用运行时API获取本地资源的URL并导入。// content.js (async () { try { // 获取扩展内资源的绝对URL const scriptUrl chrome.runtime.getURL(lib/chatgpt.min.js); // 动态导入模块 await import(scriptUrl); console.log(chatgpt.js 加载成功); // 现在可以使用 chatgpt 对象了 const lastMessage chatgpt.getLastResponse(); // ... 你的业务逻辑 } catch (error) { console.error(加载 chatgpt.js 失败, error); } })();优势功能更强大可以结合扩展的后台脚本、弹出页面、选项页面等实现更复杂的功能集成和用户交互。分发渠道正规Chrome Web Store。3.3 场景三直接嵌入网页或控制台调试对于快速测试、一次性任务或教学演示你可以直接在浏览器控制台中运行代码。方法A现代浏览器支持ES模块// 在ChatGPT网页的控制台中粘贴并执行 (async () { await import(https://cdn.jsdelivr.net/npm/kudoai/chatgpt.jslatest/dist/chatgpt.min.js); // 库加载完毕后执行你的代码 const allMessages chatgpt.getChatData(); console.log(JSON.stringify(allMessages, null, 2)); // 漂亮地打印出所有聊天数据 })();方法B兼容旧环境// 使用XMLHttpRequest加载脚本内容 var xhr new XMLHttpRequest(); xhr.open(GET, https://cdn.jsdelivr.net/npm/kudoai/chatgpt.jslatest/dist/chatgpt.min.js); xhr.onload function() { if (xhr.status 200) { var script document.createElement(script); script.textContent xhr.responseText; document.head.appendChild(script); // 脚本已加载可以调用你的初始化函数 myInitFunction(); } }; xhr.send(); function myInitFunction() { // 这里可以安全地使用 chatgpt 对象 console.log(当前是否有AI正在思考, chatgpt.isThinking()); }优势无需任何安装和配置最适合临时性的数据提取、调试或探索性编程。3.4 场景四通过NPM安装进行本地开发如果你是在构建一个更复杂的项目或者需要对chatgpt.js进行二次开发或定制可以通过NPM将其作为依赖项引入。npm install kudoai/chatgpt.js安装后你可以在你的Node.js项目注意chatgpt.js本身是前端库但可以在Node环境下进行代码分析或构建或使用打包工具如Webpack, Vite的前端项目中导入它。源码位于node_modules/kudoai/chatgpt.js/目录下。// 在你的项目文件中 // 注意这通常用于构建阶段运行时仍需在浏览器环境中执行。 import * as chatgpt from kudoai/chatgpt.js; // 或者使用 require // const chatgpt require(kudoai/chatgpt.js);优势便于版本管理、集成到现代前端工作流、进行代码分析和自定义构建。4. 核心API解析与实战应用chatgpt.js 的API大致可以分为几个核心类别数据获取、界面交互、状态监听和实用工具。下面我们结合具体代码示例深入看看如何用它们解决实际问题。4.1 数据获取从页面中提取结构化信息这是最基础也是最常用的功能。ChatGPT的对话内容深藏在层层嵌套的DOM元素中手动解析非常麻烦。获取当前对话的所有消息// 获取完整的聊天数据通常返回一个消息对象数组 const chatData chatgpt.getChatData(); console.log(本次对话共有 ${chatData.length} 条消息); chatData.forEach((msg, index) { console.log([${index}] ${msg.author}: ${msg.text.substring(0, 50)}...); });getChatData()方法会遍历整个对话窗口识别出用户和AI的每一条消息并将其转化为结构化的对象包含作者、内容、可能的唯一ID等信息。这对于需要分析对话、导出记录或进行持久化存储的场景至关重要。获取上一条或最后一条回复// 方法1最直观的别名 const lastReply chatgpt.getLastResponse(); // 方法2面向对象的调用方式 const lastReply2 chatgpt.response.getLast(); // 方法3灵活的通用get方法 const lastReply3 chatgpt.get(reply, last); if (lastReply) { console.log(AI的最后回复是, lastReply.text); // 你可以在这里处理回复例如翻译、总结、发送到别处等。 } else { console.log(似乎还没有AI的回复。); }这种“一词多义”的API设计让你可以用自己最舒服的方式去调用。获取特定的消息或元素// 获取聊天输入框的DOM元素 const inputBox chatgpt.getChatInput(); // 获取“发送”按钮 const sendButton chatgpt.getSendButton(); // 获取“重新生成响应”按钮如果存在 const regenerateButton chatgpt.getRegenerateButton(); // 获取整个对话容器的DOM元素 const conversationContainer chatgpt.getConversationContainer(); // 有了这些元素你就可以进行更精细的操作例如 inputBox.value 请用Python写一个快速排序算法; // 自动填充问题 // sendButton.click(); // 自动点击发送需谨慎最好在用户知情下进行4.2 界面交互模拟用户操作除了读取chatgpt.js 还能帮助你“操作”界面。发送消息// 方法1直接设置输入框内容并点击发送按钮模拟用户操作 function sendMessageSimulate(text) { const input chatgpt.getChatInput(); if (!input) { console.error(找不到输入框); return false; } input.value text; input.dispatchEvent(new Event(input, { bubbles: true })); // 触发输入事件确保UI更新 const sendBtn chatgpt.getSendButton(); if (sendBtn !sendBtn.disabled) { sendBtn.click(); return true; } return false; } // 方法2使用库可能提供的高级方法如果存在请查阅最新版本文档 // 例如chatgpt.sendMessage(你的问题);与对话控件交互// 停止AI的生成过程 const stopButton chatgpt.getStopButton(); if (stopButton) { stopButton.click(); console.log(已停止生成。); } // 切换到“默认”或“自定义GPT”模式如果页面有相关UI // 这需要根据ChatGPT具体的UI结构来定位元素chatgpt.js可能提供了便捷方法也可能需要你自己用DOM操作补充。4.3 状态监听感知页面变化一个健壮的自动化脚本需要知道页面当前处于什么状态。监听AI是否正在生成回复// 轮询检查简单但有效 function waitForThinkingToStop(interval 1000) { return new Promise((resolve) { const checkInterval setInterval(() { if (!chatgpt.isThinking()) { clearInterval(checkInterval); resolve(); // AI停止思考Promise完成 } }, interval); }); } // 使用示例 async function getResponseAfterAsking(question) { // 1. 输入并发送问题 sendMessageSimulate(question); // 2. 等待AI开始并结束思考 console.log(等待AI回复...); await waitForThinkingToStop(500); // 每500毫秒检查一次 // 3. 获取回复 const response chatgpt.getLastResponse(); console.log(收到回复, response.text); return response; }isThinking()方法内部通常通过检测“停止生成”按钮是否存在、光标是否在闪烁等视觉状态来判断。监听新消息到达更高级的用法是监听DOM的变化MutationObserver。虽然chatgpt.js可能没有直接封装一个“onNewMessage”事件但它提供了稳定的元素选择器让你可以轻松地自己实现监听。// 利用 chatgpt.js 获取到的容器设置 MutationObserver const conversation chatgpt.getConversationContainer(); if (conversation) { const observer new MutationObserver((mutations) { // 检查是否有子节点添加这可能意味着新消息 for (const mutation of mutations) { if (mutation.type childList mutation.addedNodes.length 0) { console.log(对话容器可能出现了新内容。); // 可以在这里调用 getLastResponse() 获取最新内容 const latest chatgpt.getLastResponse(); // 处理最新消息... } } }); observer.observe(conversation, { childList: true, subtree: true }); }4.4 实用工具函数库还可能包含一些周边工具让开发更便捷。处理对话历史// 获取当前对话的标题通常是第一条用户消息的摘要 const title chatgpt.getChatTitle(); console.log(对话标题, title); // 获取所有对话的链接或标识用于侧边栏历史记录导航 // 这需要深入侧边栏DOMchatgpt.js可能提供了相应方法如 chatgpt.getChatList()5. 实战案例构建一个自动保存聊天记录的脚本理论讲得再多不如动手做一个实际的东西。我们来用chatgpt.js构建一个用户脚本它能在每次AI回复后自动将对话包括新回复追加保存到浏览器的本地存储LocalStorage中。目标用户无需任何操作对话历史自动在本地备份。步骤分解在ChatGPT页面加载chatgpt.js库。设置一个监听器检测何时有新的AI回复生成。当检测到新回复时获取完整的当前对话数据。将对话数据序列化后保存到LocalStorage。考虑去重和存储空间管理。完整代码示例Tampermonkey脚本// UserScript // name ChatGPT 对话自动保存器 // namespace https://your-namespace.com // version 1.0 // description 使用chatgpt.js自动保存ChatGPT对话到本地 // author You // match https://chatgpt.com/* // grant none // require https://cdn.jsdelivr.net/npm/kudoai/chatgpt.js3.9.0/dist/chatgpt.min.js // /UserScript (function() { use strict; // 配置LocalStorage中使用的键名 const STORAGE_KEY chatgpt_auto_backup; // 配置防抖延迟毫秒避免短时间内频繁保存 const SAVE_DELAY_MS 2000; let saveTimeout null; let lastSavedDataHash ; // 用于简单去重避免保存完全相同的数据 /** * 获取当前对话数据的哈希值简单实现 */ function getChatDataHash(chatData) { // 将最后几条消息的内容连接起来做哈希足够用于检测显著变化 const recentText chatData.slice(-3).map(m m.text).join(|); let hash 0; for (let i 0; i recentText.length; i) { const char recentText.charCodeAt(i); hash ((hash 5) - hash) char; hash hash hash; // Convert to 32bit integer } return hash.toString(36); } /** * 保存对话数据到LocalStorage */ function saveConversation() { try { const chatData chatgpt.getChatData(); if (!chatData || chatData.length 0) { console.log([自动保存] 无对话数据跳过保存。); return; } const currentHash getChatDataHash(chatData); if (currentHash lastSavedDataHash) { console.log([自动保存] 对话数据未变化跳过保存。); return; } // 构建保存对象包含时间戳和完整数据 const saveObject { timestamp: new Date().toISOString(), url: window.location.href, title: document.title || chatgpt.getChatTitle() || 未命名对话, data: chatData }; // 从LocalStorage读取历史记录 let history JSON.parse(localStorage.getItem(STORAGE_KEY) || []); // 将新记录添加到数组末尾 history.push(saveObject); // 可选限制保存的记录数量例如只保留最近的50次对话 const MAX_HISTORY 50; if (history.length MAX_HISTORY) { history history.slice(-MAX_HISTORY); } // 保存回LocalStorage localStorage.setItem(STORAGE_KEY, JSON.stringify(history)); lastSavedDataHash currentHash; console.log([自动保存] 对话已保存。历史记录数${history.length}); } catch (error) { console.error([自动保存] 保存过程中发生错误, error); } } /** * 防抖保存函数 */ function debouncedSave() { clearTimeout(saveTimeout); saveTimeout setTimeout(saveConversation, SAVE_DELAY_MS); } /** * 初始化监听 */ function initAutoSave() { console.log([自动保存] 脚本初始化...); // 方法1轮询检查 isThinking 状态变化简单可靠 let wasThinking chatgpt.isThinking(); const thinkingCheckInterval setInterval(() { const isThinkingNow chatgpt.isThinking(); if (wasThinking !isThinkingNow) { // AI从“思考中”变为“思考完成”意味着可能有了新回复 console.log([自动保存] 检测到AI回复完成触发保存。); debouncedSave(); } wasThinking isThinkingNow; }, 1000); // 每秒检查一次 // 方法2补充监听对话容器的DOM变化更及时但可能更复杂 const conversationContainer chatgpt.getConversationContainer(); if (conversationContainer) { const observer new MutationObserver((mutations) { // 粗略判断是否有新增的、可能是AI回复的节点 let hasPotentialNewMessage false; for (const mutation of mutations) { if (mutation.type childList mutation.addedNodes.length 0) { hasPotentialNewMessage true; break; } } if (hasPotentialNewMessage) { // 稍微延迟一下确保DOM完全渲染 setTimeout(debouncedSave, 500); } }); observer.observe(conversationContainer, { childList: true, subtree: true }); console.log([自动保存] DOM变化监听器已启动。); } // 页面关闭前尝试保存一次 window.addEventListener(beforeunload, () { clearInterval(thinkingCheckInterval); saveConversation(); // 直接保存不防抖 }); console.log([自动保存] 初始化完成开始监控对话。); } // 等待页面和chatgpt.js完全就绪 window.addEventListener(load, () { // 稍加延迟确保ChatGPT的UI完全加载 setTimeout(initAutoSave, 3000); }); })();代码解读与注意事项防抖DebouncedebouncedSave函数确保不会在AI快速连续输出多个字符时流式响应触发多次保存而是在停止变化后2秒才保存提升性能。双重监听策略结合了轮询isThinking()和MutationObserver。轮询作为保底机制稳定可靠DOM监听作为快速响应机制。两者结合提高了可靠性。简单去重getChatDataHash函数生成一个简易哈希避免在对话内容毫无变化时重复保存。存储管理限制了LocalStorage中保存的历史记录条数50条防止存储空间被无限占用。错误处理使用try...catch包裹核心保存逻辑避免脚本因意外错误而完全停止工作。页面卸载保存监听beforeunload事件在用户关闭页面或导航离开前进行最后一次保存。这个脚本是一个功能完整的基础示例。你可以在此基础上扩展比如将数据保存到IndexedDB以支持更大容量、添加导出JSON文件的功能、或者将数据同步到你自己的服务器。6. 进阶技巧与避坑指南在实际使用chatgpt.js开发更复杂的功能时我总结了一些经验和需要注意的坑。6.1 处理ChatGPT的流式响应ChatGPT在回复时采用的是流式传输Streaming即一个字一个字地显示出来。这给“检测回复完成”带来了挑战。不要依赖“文本内容是否完整”在流式输出过程中getLastResponse()获取到的文本是逐渐变长的。如果你在文本中间就判断会得到不完整的回复。可靠的方法正如上面例子所示结合isThinking()状态和MutationObserver是更可靠的方式。当isThinking()从true变为false且DOM停止更新一段时间后可以认为回复基本完成。追加模式如果你需要实时处理每一个“词块”可以监听DOM的细微变化但处理逻辑会复杂很多。对于大多数备份、分析场景等待回复完成再处理是完全足够的。6.2 应对DOM结构更新这是使用任何基于DOM自动化库的最大风险。OpenAI随时可能更新前端。关注库的版本更新chatgpt.js 的维护者会持续跟进ChatGPT的界面变化并更新库。及时将你的脚本或扩展中引用的库版本升级到最新稳定版。编写防御性代码不要假设某个元素一定存在。在使用chatgpt.getSendButton()等返回DOM元素的方法后先进行空值判断。const sendBtn chatgpt.getSendButton(); if (!sendBtn) { console.warn(发送按钮未找到界面可能已更新或不在聊天页面。); // 这里可以尝试备用方案或者优雅地降级处理 return; } // 安全地操作 sendBtn准备降级方案对于核心功能思考如果某个API暂时失效是否有替代方案或能否向用户给出友好提示。6.3 性能与用户体验避免阻塞主线程像上面例子中每秒一次的轮询对性能影响微乎其微。但如果你进行复杂的DOM查询或数据处理务必考虑使用setTimeout、requestIdleCallback或将任务放入Web Worker。尊重用户自动发送消息、大量操作DOM等行为可能会干扰用户正常使用甚至违反ChatGPT的使用条款。你的脚本应该以“增强”为主例如提供便捷按钮、保存数据、修改样式等而非替代用户进行自动化对话。明确告知用户脚本的功能并谨慎使用自动发送等功能。6.4 调试技巧利用控制台chatgpt.js 通常会在全局暴露一个chatgpt对象。直接在浏览器控制台输入chatgpt.然后按Tab键可以查看所有可用方法。查看源码如果某个方法的行为不符合预期可以去GitHub仓库查看该方法的源码实现理解其选择器逻辑这有助于你调试或编写兼容代码。隔离测试在一个干净的浏览器页面或匿名窗口中测试你的脚本避免与其他扩展或脚本冲突。7. 生态与社区看看别人用它做了什么chatgpt.js 的活力很大程度上来自于其社区。项目README中展示的“Made with chatgpt.js”部分就是最好的证明。这些项目为你提供了绝佳的学习范式和灵感来源。AmazonGPT / BraveGPT / DuckDuckGPT这些项目将AI聊天功能“嫁接”到了电商或搜索引擎页面。其核心思路就是利用chatgpt.js获取当前页面的关键信息如产品标题、搜索关键词然后通过ChatGPT的接口或类似API生成摘要、评论或答案再动态插入到原页面中。这展示了chatgpt.js在“页面内容提取”和“UI集成”方面的强大能力。Autoclear ChatGPT History自动清除聊天历史以保护隐私。这需要精确地找到并点击ChatGPT界面上的“删除对话”或“清除所有”按钮。chatgpt.js提供了定位这些交互元素的能力。ChatGPT Auto-Continue / Auto-Refresh处理ChatGPT常见的网络中断或响应截断问题。这类脚本需要监听“继续生成”按钮的出现并自动点击或者定时检测页面错误状态并刷新。它们重度依赖isThinking()、getRegenerateButton()等状态和元素查询API。ThunderAI这是一个将ChatGPT集成到Thunderbird邮件客户端的扩展。它证明了chatgpt.js的核心逻辑DOM交互经过适配可以应用到基于Web技术的桌面应用中。研究这些开源项目的源码是快速提升chatgpt.js使用水平的最佳途径。你可以看到他们如何处理边界情况、如何组织代码结构、如何提供用户配置。8. 贡献与问题排查如果你在使用中发现bug或者ChatGPT更新导致库的某些功能失效最有效的做法是去GitHub仓库提交Issue。提交时请尽量提供详细信息chatgpt.js 版本号你使用的是哪个版本运行环境浏览器类型和版本、UserScript管理器版本、ChatGPT的界面版本如果有。复现步骤清晰描述如何操作能触发这个问题。预期行为你期望发生什么实际行为实际上发生了什么最好有错误信息或截图。可能的线索你自己排查时发现了什么例如失效的CSS选择器是什么如果你有能力修复问题更欢迎直接提交Pull Request。库的维护者通常很乐意接受社区贡献。最后一点个人体会chatgpt.js 这类工具的本质是在官方未提供API的领域通过逆向工程和自动化技术创造可能性。它非常强大但也伴随着一定的脆弱性。用它来开发提升个人效率的小工具、进行有趣的前端实验是绝佳的。但在构建指望长期稳定运行、面向大量用户的服务时需要对其依赖的“非官方接口”的稳定性有清醒的认识并设计好降级和容错机制。把它当作一个快速原型工具和效率倍增器你会收获很多乐趣。