您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Zotero GPT Pro, support ChatGPT Gemini Poe Kimi Coze Chatglm Yiyan Tongyi Claude Mytan ChanlderAi DeepSeek Doubao AIStudio
当前为
// ==UserScript== // @name Zotero GPT Connector // @description Zotero GPT Pro, support ChatGPT Gemini Poe Kimi Coze Chatglm Yiyan Tongyi Claude Mytan ChanlderAi DeepSeek Doubao AIStudio // @namespace http://tampermonkey.net/ // @icon https://github.com/MuiseDestiny/zotero-gpt/blob/bootstrap/addon/chrome/content/icons/favicon.png?raw=true // @version 3.1.4 // @author Polygon // @match https://chatgpt.com/* // @match https://gemini.google.com/app* // @match https://poe.com/* // @match https://www.coze.com/* // @match https://kimi.moonshot.cn/* // @match https://chatglm.cn/* // @match https://chatglm.cn/* // @match https://yiyan.baidu.com/* // @match https://tongyi.aliyun.com/* // @match https://qianwen.aliyun.com/* // @match https://claude.ai/* // @match https://mytan.maiseed.com.cn/* // @match https://mychandler.bet/* // @match https://chat.deepseek.com/* // @match https://www.doubao.com/chat/* // @match https://*.chatshare.biz/* // @match https://chat.kelaode.ai/* // @match https://chat.rawchat.cn/* // @match https://chat.sharedchat.top/* // @match https://node.dawuai.buzz/* // @match https://aistudio.google.com/* // @connect * // @grant GM_xmlhttpRequest // @grant GM_registerMenuCommand // @grant GM_cookie // @grant unsafeWindow // @run-at document-start // ==/UserScript== (async function () { 'use strict'; let isRunning = true let AI = "ChatGPT" const host = location.host if (host == 'chatgpt.com') { AI = "ChatGPT" } else if (host == 'gemini.google.com') { AI = "Gemini" } else if (host == 'poe.com') { AI = "Poe" } else if (host == 'kimi.moonshot.cn') { AI = "Kimi" } else if (host == 'www.coze.com') { AI = "Coze" } else if (host == "chatglm.cn") { localStorage.conversation_id = "" AI = "Chatglm" } else if (host == 'yiyan.baidu.com') { AI = "Yiyan" } else if (host == 'tongyi.aliyun.com' || host == 'qianwen.aliyun.com') { AI = "Tongyi" } else if (host == "claude.ai" || host == 'chat.kelaode.ai') { AI = "Claude" } else if (host == 'mytan.maiseed.com.cn') { AI = "MyTan" } else if (host == 'mychandler.bet') { localStorage.conversation_id = "" AI = "ChanlderAi" } else if (host == 'chat.deepseek.com') { AI = "DeepSeek" } else if (host == "www.doubao.com") { AI = "Doubao" } else if (host == 'aistudio.google.com') { AI = "AIStudio" } // 在Zotero中执行代码 async function execInZotero(code) { try { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "POST", url: "http://127.0.0.1:23119/zoterogpt", headers: { "Content-Type": "application/json", }, responseType: "json", data: JSON.stringify({ code }), onload: function (response) { if (response.status >= 200 && response.status < 300) { resolve(response.response.result); } else { reject(new Error(`Request failed with status: ${response.status}`)); } }, onerror: function (error) { reject(new Error('Network error')); } }); }); } catch (e) { window.alert("execInZotero error: " + code); return "" } } // 设定ChatGPT输入框文本并发送 const setText = async (text) => { const dispatchInput = (selector) => { // 获取 input 输入框的dom对象 var inputNode = document.querySelector(selector); // 修改input的值 inputNode.value = text; // plus inputNode.innerText = text; // 设置输入框的 input 事件 var event = new InputEvent('input', { 'bubbles': true, 'cancelable': true, }); inputNode.dispatchEvent(event); } const originalText = text if (AI == "ChatGPT") { dispatchInput('#prompt-textarea') await sleep(100) await send("article", () => { const button = document.querySelector('[data-testid="send-button"]'); button.click() }) } else if (AI == "Gemini") { // 获取 input 输入框的dom对象 const element_input = window.document.querySelector('rich-textarea .textarea'); // 修改input的值 element_input.textContent = text; document.querySelector(".send-button").click(); setTimeout(() => { document.querySelector(".send-button").click() }, 100) } else if (AI == "Poe") { dispatchInput('textarea[class*=GrowingTextArea_textArea]') document.querySelector("button[class*=ChatMessageSendButton_sendButton]").click(); setTimeout(() => { document.querySelector("button[class*=ChatMessageSendButton_sendButton]").click() }, 100) } else if (AI == "Kimi") { const node = document.querySelector("[class^=inputInner]") await node[Object.keys(node)[1]].children[1][1].ref.current.insertText(text) await send("[class^=segmentItem]", () => { const button = document.querySelector('[data-testid=msh-chatinput-send-button]'); button.click() }) } else if (AI == "Coze") { const node = document.querySelector(".b5gKALp6yXERRDn8TV4r") node[Object.keys(node)[0]].pendingProps.children[0].props.onSendMessage({ text, mentionList: [] }) } else if (AI == "Chatglm") { const token = document.cookie.match(/chatglm_token=([^;]+);/)[1]; requestStream({ api: `https://chatglm.cn/chatglm/backend-api/assistant/stream`, token, data: { "assistant_id": "65940acff94777010aa6b796", "conversation_id": localStorage.conversation_id || "", "meta_data": { "mention_conversation_id": "", "is_test": false, "input_question_type": "xxxx", "channel": "", "draft_id": "" }, "messages": [ { "role": "user", "content": [{ "type": "text", "text": text }] }] }, lineRegex: /event:message\ndata: .+/g, getContent: (data) => data.parts[0].content[0].type == "text" ? data.parts[0].content[0].text : "", midFunction: (data) => { if (!localStorage.conversation_id || localStorage.conversation_id.length == 0) { localStorage.conversation_id = data.conversation_id } }, isNotDelta: true }) } else if (AI == "Yiyan") { const node = document.querySelector("#eb_model_footer") node[Object.keys(node)[1]].children[3].props.children[2].props.children[0].props.setText(text); document.querySelector(".VAtmtpqL").click() } else if (AI == "Tongyi") { const node = document.querySelector(".chatInput--eJzBH8LP") await node[Object.keys(node)[1]].children[0].props.setText(text); await send("[class^=questionItem]", () => { const node2 = document.querySelector(".operateBtn--zFx6rSR0"); node2[Object.keys(node2)[1]].onClick() }) } else if (AI == "Claude") { const node = document.querySelector("fieldset") const props = node[Object.keys(node)[1]].children[0].props.children[0].props.children[0].props; props.setInput(text); document.querySelector("button[aria-label='Send Message']").click(); } else if (AI == "MyTan") { const conversation_id = location.href.split("chat/")?.[1] const data = { "content": [ { "type": "text", "text": text } ], "stream": true, } if (conversation_id) { data.conversation_id = conversation_id } else { data.conversation = { title: "新对话", model: JSON.parse(localStorage["chosen-model-obj"]).model } } requestStream({ api: `https://mytan.maiseed.com.cn/api/v2/messages`, token: JSON.parse(localStorage["chat-tan-token"]).token, data, lineRegex: /data: .+/g, getContent: (data) => data.choices[0].delta.content, }) } else if (AI == "ChanlderAi") { // 更新id if (!localStorage.conversation_id || localStorage.conversation_id == "") { async function readStream(stream) { const reader = stream.getReader(); let result = ''; const decoder = new TextDecoder(); while (true) { const { done, value } = await reader.read(); if (done) break; result += decoder.decode(value, { stream: true }); } return result } const res = await fetch("https://api.chandler.bet/api/chat/chatHistory", { method: 'POST', headers: { 'Content-Type': 'application/json', "Authorization": `Bearer ${localStorage.token}`, "accept": "application/json, text/plain, */*" }, body: JSON.stringify({ "keywords": "", "model_names": [], "page_size": 10, "page_num": 1 }), }) const data = JSON.parse(await readStream(res.body)).data[0] localStorage.conversation_id = data.conversation_id localStorage.model_name = data.model_name localStorage.parent_message_id = data.parent_message_id } const appData = JSON.parse(localStorage.appLocalStorage) requestStream({ api: `https://api.chandler.bet/api/chat/Chat`, token: localStorage.token, data: { "uid": appData.email, "prompt": text, "model_name": localStorage.model_name, "request_timeout": 30, "global_timeout": 100, "max_retries": 1, "attachment_list": [], "parent_message_id": localStorage.parent_message_id, "conversation_id": localStorage.conversation_id, "answer_again": false, "aireply": "", "timestamp": (new Date()).getTime(), "status": "", "app_name": "", "web_url": "https://api.chandler.bet" }, lineRegex: /event:message\ndata:.+/g, getContent: (data) => data.delta }) } else if (AI == "DeepSeek") { requestStream({ api: `https://chat.deepseek.com/api/v0/chat/completions`, token: JSON.parse(localStorage.userToken).value, data: { "message": text, "stream": true, "model_preference": null, "model_class": "deepseek_chat", "temperature": 0 }, lineRegex: /data: .+/g, getContent: (data) => data.choices[0].delta.content || "" }) } else if (AI == "Doubao") { const node = document.querySelector("[class^=footer]") await node[Object.keys(node)[1]].children.ref.current.autoTransValue(text); await sleep(1e3) document.querySelector("button#flow-end-msg-send").click(); } else if (AI == "AIStudio") { // 获取 input 输入框的dom对象 const element_input = document.querySelector(".input-wrapper textarea") // 修改input的值 element_input.value = text; // plus element_input.innerText = text; await sleep(100) // 设置输入框的 input 事件 const event = new InputEvent('input', { 'bubbles': true, 'cancelable': true, }); element_input.dispatchEvent(event); await sleep(100) await send("ms-chat-turn", () => { const button = document.querySelector('run-button button'); button.click() }) await sleep(1000) } } // 连续发送 const send = async (selector, callback) => { const oldNumber = document.querySelectorAll(selector).length; callback(); while (document.querySelectorAll(selector).length == oldNumber) { callback(); await sleep(100); } } const uploadFile = async (base64String, fileName) => { try { let type if (fileName.endsWith("pdf")) { type = "application/pdf" } else if (fileName.endsWith("png")) { type = "image/png" } function base64ToArrayBuffer(base64) { const binaryString = atob(base64); const len = binaryString.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { bytes[i] = binaryString.charCodeAt(i); } return bytes.buffer; } if (AI == "AIStudio") { const button = document.querySelector("ms-add-chunk-menu button") button && button.click() } // 创建一个虚拟的 PDF 文件对象 const fileContent = base64ToArrayBuffer(base64String); const file = new File([fileContent], fileName, { type }); // 创建一个DataTransfer对象 const dataTransfer = new DataTransfer(); dataTransfer.items.add(file); const fileInput = document.querySelector("input[type=file]") fileInput.files = dataTransfer.files fileInput.dispatchEvent(new Event('change', { bubbles: true })); // 需要特殊处理的 if (AI == "AIStudio") { const button = document.querySelector("ms-add-chunk-menu button") button && button.click() } } catch (e) { // window.alert(e) } } /** * {api, token, data, lineRegex, getContent, errorFunction, midFunction} * @param {*} data */ const requestStream = async (params) => { fetch(params.api, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${params.token}` }, body: JSON.stringify(params.data) }) .then(response => { if (response.status == 200) { return response.body.getReader() } else if (response.status == 400) { throw new Error('频率过高'); } { throw new Error('授权失败'); } }) .then(reader => { let text = "" const decoder = new TextDecoder(); window.setTimeout(async () => { while (true) { const { done, value } = await reader.read(); if (done) { await execInZotero(` let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1] task.responseText = ${JSON.stringify(text)}; task.type = "done"; task.responseType = "markdown" `) break } try { const newLines = decoder.decode(value, { stream: true }) for (let line of newLines.match(params.lineRegex)) { try { const data = JSON.parse(line.split("data:")[1].trim()) params.midFunction && params.midFunction(data) text = params.isNotDelta ? params.getContent(data) : (text + params.getContent(data)); } catch (e) { if (String(e).includes("Stop")) { return } } execInZotero(` let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1] task.responseText = ${JSON.stringify(text)}; task.type = "pending"; task.responseType = "markdown" `) } } catch (e) { console.log(e) } } }, 0) }) .catch(e => { params.errorFunction && params.errorFunction() }) } // 阻塞 function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } GM_registerMenuCommand('🔗 运行', () => { isRunning = true window.alert("🔗 已运行") }); GM_registerMenuCommand('🎊 断开', () => { isRunning = false window.alert("🎊 断开") }); const waitSend = async (selector) => { let getUserQuestionNum = () => document.querySelectorAll(selector).length const questionNum = getUserQuestionNum() while (getUserQuestionNum() == questionNum) { await sleep(100) } } // 通信 await sleep(3000) while (true) { if (!isRunning) { await execInZotero(` window.Meet.Connector.time = 0; `) await sleep(1000) continue; } try { const tasks = (await execInZotero(` if (!window.Meet.Connector){ window.Meet.Connector = ${JSON.stringify({ AI, time: Date.now() / 1e3, tasks: [] })}; } else { window.Meet.Connector.time = ${Date.now() / 1e3}; window.Meet.Connector.AI = "${AI}"; } window.Meet.Connector `)).tasks if (!tasks || tasks.length == 0) { await sleep(500) continue } const task = tasks.slice(-1)[0] if (task.type == "pending") { if (task.file) { // document.querySelector("[data-testid='create-new-chat-button']").click(); // await sleep(1e3) await uploadFile(task.file.base64String, task.file.name) await execInZotero(` let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1] task.type = "done" `) } else if (task.requestText) { await setText(task.requestText) // 操作浏览器提问 if (AI == "Gemini") { // await waitSend("user-query") while (document.querySelectorAll('model-response').length != getUserQuestionNum()) { await sleep(100) } } await execInZotero(` let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1] task.requestText = ""; task.responseText = "<p>Waiting ${AI}...</p>"; `) } else { let isDone = false, text = "", type = "html" const setZoteroText = async () => { if (typeof (text) !== "string") { return } await execInZotero(` let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1] task.responseText = ${JSON.stringify(text)}; task.type = ${isDone} ? "done" : "pending"; task.responseType = "${type}" `) if (isDone) { await sleep(1000) await execInZotero(` let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1] task.responseText = ${JSON.stringify(text)}; `) } } if (AI == "ChatGPT") { type = "markdown" const outputEle = [...document.querySelectorAll('[data-testid^=conversation-turn]')].slice(-1)[0]; const node = outputEle.querySelector("[data-message-author-role=assistant]") const message = node[Object.keys(node)[0]].alternate.alternate.pendingProps.children[2].props.messages[0].message isDone = message.status == "finished_successfully" text = message.content.parts[0]; await setZoteroText() } else if (AI == "Gemini") { const outputEle = [...document.querySelectorAll('model-response')].slice(-1)[0]; const contentEle = outputEle.querySelector(".response-content message-content") isDone = Boolean(outputEle.querySelector(".complete")) text = contentEle.querySelector(".markdown").innerHTML await setZoteroText() } else if (AI == "Poe") { type = "markdown" const lastNode = [...document.querySelectorAll("[class^=ChatMessagesView_messagePair]")].slice(-1)[0] const props = lastNode[Object.keys(lastNode)[0]].child.memoizedProps text = props.pairedMessage.text isDone = props.pairedMessage.state == "complete" await setZoteroText() } else if (AI == "Kimi") { type = "markdown" const lastNode = [...document.querySelectorAll(".kimi-default-segment-component")].slice(-1)[0] const data = lastNode[Object.keys(lastNode)[0]].child.pendingProps.data const sections = data.contents.zones[0].sections text = sections.find(i=>i.cmpl).cmpl isDone = Boolean(data.status) await setZoteroText() } else if (AI == "Coze") { const outputEle = document.querySelector(".message-group-wrapper"); const contentEle = outputEle.querySelector("[data-testid='bot.ide.chat_area.message_box'] .flow-markdown-body") isDone = Boolean(outputEle.querySelector(".chat-uikit-message-box-container__message__message-box__footer").childNodes.length != 0) text = contentEle.innerHTML.replace(/<br .+?>/g, "").replace(/<hr .+?>/g, "<hr/>") await setZoteroText() } else if (AI == "Yiyan") { const outputEle = document.querySelector(".ErXhAgf5 .RmHagX8t"); const contentEle = outputEle.querySelector(".custom-html.md-stream-desktop") isDone = Boolean(outputEle.querySelector(".fXxD0Rtx")) text = contentEle.innerHTML.replace(/<br .+?>/g, "").replace(/<hr .+?>/g, "<hr/>") await setZoteroText() } else if (AI == "Tongyi") { const lastAnwser = [...document.querySelectorAll("[class^=answerItem]")].slice(-1)[0] type = "markdown" const message = lastAnwser[Object.keys(lastAnwser)[0]].memoizedProps.children.find(i => { try { return i.props.children[2].props.message } catch { } }).props.children[2].props.message isDone = message.contents[message.contents.length - 1].status == "finished" text = message.contents[message.contents.length - 1].content await setZoteroText() } else if (AI == "Claude") { const node = [...document.querySelectorAll("div[data-test-render-count]")].slice(-1)[0].querySelector("[data-is-streaming]"); type = "markdown" text = node[Object.keys(node)[1]].children[0].props.children[0].props.text isDone = node.getAttribute("data-is-streaming") == "false"; await setZoteroText() } else if (AI == "Doubao") { const nodes = [...document.querySelectorAll("[class^=message-box-content-wrapper]")] const node = nodes.slice(-1)[0] const data = node[Object.keys(node)[0]].child.child.child.child.memoizedState.memoizedState.current.value type = "markdown" text = data.content_obj.text; isDone = data.ext.is_finish == "1"; await setZoteroText() } else if (AI == "AIStudio") { type = "html" const msgNodes = document.querySelectorAll('ms-text-chunk.ng-star-inserted') const msgNode = msgNodes[msgNodes.length - 1] isDone = !Boolean(msgNode.closest("ms-chat-turn").querySelector(".loading-indicator")); if (isDone) { // 消息会有延迟,务必等待 let n = 10 isDone = false while (n > 0) { await sleep(100) text = msgNode.innerHTML; await setZoteroText() n -= 1 } isDone = true } text = msgNode.innerHTML; await setZoteroText() } } } } catch (e) { console.log(e) await sleep(1000) } await sleep(10) } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址