Zotero GPT Connector

Zotero GPT Pro, support ChatGPT Gemini Poe Kimi Coze Chatglm Yiyan Tongyi Claude Mytan ChanlderAi DeepSeek Doubao AIStudio

目前为 2024-10-16 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Zotero GPT Connector
  3. // @description Zotero GPT Pro, support ChatGPT Gemini Poe Kimi Coze Chatglm Yiyan Tongyi Claude Mytan ChanlderAi DeepSeek Doubao AIStudio
  4. // @namespace http://tampermonkey.net/
  5. // @icon https://github.com/MuiseDestiny/zotero-gpt/blob/bootstrap/addon/chrome/content/icons/favicon.png?raw=true
  6. // @version 3.1.6
  7. // @author Polygon
  8. // @match https://chatgpt.com/*
  9. // @match https://gemini.google.com/app*
  10. // @match https://poe.com/*
  11. // @match https://www.coze.com/*
  12. // @match https://kimi.moonshot.cn/*
  13. // @match https://chatglm.cn/*
  14. // @match https://yiyan.baidu.com/*
  15. // @match https://tongyi.aliyun.com/*
  16. // @match https://qianwen.aliyun.com/*
  17. // @match https://claude.ai/*
  18. // @match https://mytan.maiseed.com.cn/*
  19. // @match https://mychandler.bet/*
  20. // @match https://chat.deepseek.com/*
  21. // @match https://www.doubao.com/chat/*
  22. // @match https://*.chatshare.biz/*
  23. // @match https://chat.kelaode.ai/*
  24. // @match https://chat.rawchat.cn/*
  25. // @match https://chat.sharedchat.top/*
  26. // @match https://node.dawuai.buzz/*
  27. // @match https://aistudio.google.com/*
  28. // @match https://claude.ai0.cn/*
  29. // @connect *
  30. // @grant GM_xmlhttpRequest
  31. // @grant GM_registerMenuCommand
  32. // @grant GM_cookie
  33. // @grant unsafeWindow
  34. // @run-at document-start
  35. // ==/UserScript==
  36.  
  37. (async function () {
  38. 'use strict';
  39. let isRunning = true
  40. let AI = "ChatGPT"
  41. const host = location.host
  42. if (host == 'chatgpt.com') {
  43. AI = "ChatGPT"
  44. } else if (host == 'gemini.google.com') {
  45. AI = "Gemini"
  46. } else if (host == 'poe.com') {
  47. AI = "Poe"
  48. } else if (host == 'kimi.moonshot.cn') {
  49. AI = "Kimi"
  50. } else if (host == 'www.coze.com') {
  51. AI = "Coze"
  52. } else if (host == "chatglm.cn") {
  53. localStorage.conversation_id = ""
  54. AI = "Chatglm"
  55. } else if (host == 'yiyan.baidu.com') {
  56. AI = "Yiyan"
  57. } else if (host == 'tongyi.aliyun.com' || host == 'qianwen.aliyun.com') {
  58. AI = "Tongyi"
  59. } else if (host == "claude.ai" || host == 'chat.kelaode.ai' || host.includes("claude")) {
  60. AI = "Claude"
  61. } else if (host == 'mytan.maiseed.com.cn') {
  62. AI = "MyTan"
  63. } else if (host == 'mychandler.bet') {
  64. localStorage.conversation_id = ""
  65. AI = "ChanlderAi"
  66. } else if (host == 'chat.deepseek.com') {
  67. AI = "DeepSeek"
  68. } else if (host == "www.doubao.com") {
  69. AI = "Doubao"
  70. } else if (host == 'aistudio.google.com') {
  71. AI = "AIStudio"
  72. }
  73. const requestPatchArr = [
  74. {
  75. AI: "Kimi",
  76. regex: /https:\/\/kimi.moonshot.cn\/api\/chat\/.+\/completion\/stream/,
  77. extract: (text) => {
  78. let deltaText = ""
  79. for (let line of text.split("\n")) {
  80. if (line.startsWith("data")) {
  81. const data = JSON.parse(line.split("data: ")[1])
  82. if (data.event == "cmpl") {
  83. deltaText += data.text
  84. }
  85. }
  86. }
  87. return deltaText
  88. },
  89. text: ""
  90. },
  91. {
  92. AI: "AIStudio",
  93. regex: /GenerateContent$/,
  94. extract: (text) => {
  95. while (true) {
  96. try {
  97. JSON.parse(text)
  98. break
  99. } catch {
  100. text += "]"
  101. }
  102. }
  103. const data = JSON.parse(text)
  104. return data[0].map(i => i[0][0][0][0][0][1]).join("")
  105. },
  106. text: ""
  107. },
  108. {
  109. AI: "ChatGPT",
  110. regex: /backend-api\/conversation$/,
  111. extract: (text) => {
  112. let deltaText = ""
  113. for (let line of text.split("\n")) {
  114. if (line.startsWith("data: {")) {
  115. try { JSON.parse(line.split("data: ")[1]) } catch { continue }
  116. const data = JSON.parse(line.split("data: ")[1])
  117. if (data.p && data.p.length > 0) { deltaText+="\n"}
  118. if (typeof(data.v) == "string") {
  119. deltaText += data.v
  120. } else if (data.v instanceof Array) {
  121. if (data.v[0].o == "append") {
  122. deltaText += data.v[0].v
  123. }
  124. }
  125. }
  126. }
  127. return deltaText
  128. },
  129. text: ""
  130. },
  131. {
  132. AI: "Claude",
  133. regex: /chat_conversations\/.+\/completion/,
  134. extract: (text) => {
  135. console.log(text)
  136. let deltaText = ""
  137. for (let line of text.split("\n")) {
  138. if (line.startsWith("data: {")) {
  139. try { JSON.parse(line.split("data: ")[1]) } catch { continue }
  140. const data = JSON.parse(line.split("data: ")[1])
  141. if (data.type && data.type == "completion") {
  142. deltaText += data.completion
  143. }
  144. }
  145. }
  146. return deltaText
  147. },
  148. text: ""
  149. }
  150. ]
  151.  
  152. // 数据拦截,部分网站需要
  153.  
  154. const originalXhrOpen = XMLHttpRequest.prototype.open;
  155. XMLHttpRequest.prototype.open = function (method, url, async) {
  156. this.addEventListener('readystatechange', async function () {
  157. let requestPatch
  158. if ((requestPatch = requestPatchArr.find(i => i.regex.test(url)))) {
  159. if (this.readyState === 3) {
  160. requestPatch.text = ""
  161. // 请求返回数据的流式部分
  162. try {
  163. requestPatch.text = requestPatch.extract(this.responseText)
  164. } catch {
  165. console.log(this.responseText)
  166. }
  167. await execInZotero(`
  168. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  169. task.responseText = ${JSON.stringify(requestPatch.text)};
  170. task.type = "pending";
  171. task.responseType = "markdown"
  172. `)
  173. } else if (this.readyState == 0) {
  174. await execInZotero(`
  175. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  176. task.responseText = ${JSON.stringify(requestPatch.text)};
  177. task.type = "done";
  178. task.responseType = "markdown"
  179. `)
  180. }
  181. }
  182. });
  183. originalXhrOpen.apply(this, arguments);
  184. };
  185.  
  186.  
  187. const originalFetch = window.fetch;
  188. unsafeWindow.fetch = function () {
  189. return originalFetch.apply(this, arguments)
  190. .then(response => {
  191. const url = response.url
  192. const requestPatch = requestPatchArr.find(i => i.regex.test(url))
  193. if (requestPatch) {
  194. const clonedResponse = response.clone();
  195. console.log("requestPatch", requestPatch)
  196. requestPatch.text = ""
  197. console.log(clonedResponse)
  198. const reader = clonedResponse.body.getReader();
  199. const decoder = new TextDecoder()
  200. function processStream() {
  201. reader.read().then(({ done, value }) => {
  202. if (done) {
  203. execInZotero(`
  204. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length - 1];
  205. task.responseText = ${JSON.stringify(requestPatch.text)};
  206. task.type = "done";
  207. task.responseType = "markdown";
  208. `);
  209. return;
  210. }
  211.  
  212. // 将 Uint8Array 转为字符串
  213. const text = decoder.decode(value, {stream: true});
  214. requestPatch.text += (requestPatch.extract(text) || "");
  215.  
  216. execInZotero(`
  217. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length - 1];
  218. task.responseText = ${JSON.stringify(requestPatch.text)};
  219. task.type = "pending";
  220. task.responseType = "markdown";
  221. `);
  222.  
  223. // 递归调用,继续读取流数据
  224. processStream();
  225. }).catch(error => {
  226. // 捕获所有错误,包括 AbortError
  227. console.log("Error when Patch", error)
  228. execInZotero(`
  229. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length - 1];
  230. task.responseText = ${JSON.stringify(requestPatch.text)};
  231. task.type = "done";
  232. task.responseType = "markdown";
  233. `);
  234. });
  235. }
  236.  
  237. // 开始处理流
  238. processStream();
  239. // try {
  240. // if (done) {
  241. // execInZotero(`
  242. // let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  243. // task.responseText = ${JSON.stringify(requestPatch.text)};
  244. // task.type = "done";
  245. // task.responseType = "markdown"
  246. // `)
  247. // return
  248. // };
  249. // // 将 Uint8Array 转为字符串
  250. // const text = new TextDecoder().decode(value);
  251. // requestPatch.text += (requestPatch.extract(text) || "")
  252. // execInZotero(`
  253. // let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  254. // task.responseText = ${JSON.stringify(requestPatch.text)};
  255. // task.type = "pending";
  256. // task.responseType = "markdown"
  257. // `)
  258. // // 递归读取剩余流数据
  259. // reader.read().then(processStream);
  260. // } catch {
  261. // execInZotero(`
  262. // let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  263. // task.responseText = ${JSON.stringify(requestPatch.text)};
  264. // task.type = "done";
  265. // task.responseType = "markdown"
  266. // `)
  267. // }
  268. // });
  269. }
  270. return response;
  271. });
  272. };
  273.  
  274.  
  275. // 在Zotero中执行代码
  276. async function execInZotero(code) {
  277. try {
  278. return new Promise((resolve, reject) => {
  279. GM_xmlhttpRequest({
  280. method: "POST",
  281. url: "http://127.0.0.1:23119/zoterogpt",
  282. headers: {
  283. "Content-Type": "application/json",
  284. },
  285. responseType: "json",
  286. data: JSON.stringify({ code }),
  287. onload: function (response) {
  288. if (response.status >= 200 && response.status < 300) {
  289. resolve(response.response.result);
  290. } else {
  291. reject(new Error(`Request failed with status: ${response.status}`));
  292. }
  293. },
  294. onerror: function (error) {
  295. reject(new Error('Network error'));
  296. }
  297. });
  298. });
  299. } catch (e) {
  300. window.alert("execInZotero error: " + code);
  301. return ""
  302. }
  303. }
  304.  
  305. // 设定ChatGPT输入框文本并发送
  306. const setText = async (text) => {
  307. const dispatchInput = (selector) => {
  308. // 获取 input 输入框的dom对象
  309. var inputNode = document.querySelector(selector);
  310. if (!inputNode) { return }
  311. // 修改input的值
  312. inputNode.value = text;
  313. // plus
  314. inputNode.innerText = text;
  315. // 设置输入框的 input 事件
  316. var event = new InputEvent('input', {
  317. 'bubbles': true,
  318. 'cancelable': true,
  319. });
  320. inputNode.dispatchEvent(event);
  321. }
  322. const originalText = text
  323. if (AI == "ChatGPT") {
  324. dispatchInput('#prompt-textarea')
  325. await sleep(100)
  326. await send("article", () => {
  327. const button = document.querySelector('[data-testid="send-button"]');
  328. button.click()
  329. })
  330. } else if (AI == "Gemini") {
  331. // 获取 input 输入框的dom对象
  332. const element_input = window.document.querySelector('rich-textarea .textarea');
  333. // 修改input的值
  334. element_input.textContent = text;
  335. await send(".conversation-container", () => {
  336. const button = document.querySelector('.send-button');
  337. button.click()
  338. })
  339. } else if (AI == "Poe") {
  340. dispatchInput('textarea[class*=GrowingTextArea_textArea]')
  341. document.querySelector("button[class*=ChatMessageSendButton_sendButton]").click();
  342. setTimeout(() => {
  343. document.querySelector("button[class*=ChatMessageSendButton_sendButton]").click()
  344. }, 100)
  345. } else if (AI == "Kimi") {
  346. const node = document.querySelector("[class^=inputInner]")
  347. await node[Object.keys(node)[1]].children[1][1].ref.current.insertText(text)
  348.  
  349. await send("[class^=segmentItem]", () => {
  350. const button = document.querySelector('[data-testid=msh-chatinput-send-button]');
  351. button.click()
  352. })
  353.  
  354. } else if (AI == "Coze") {
  355. const node = document.querySelector(".b5gKALp6yXERRDn8TV4r")
  356. node[Object.keys(node)[0]].pendingProps.children[0].props.onSendMessage({ text, mentionList: [] })
  357. } else if (AI == "Chatglm") {
  358. const token = document.cookie.match(/chatglm_token=([^;]+);/)[1];
  359. requestStream({
  360. api: `https://chatglm.cn/chatglm/backend-api/assistant/stream`,
  361. token,
  362. data: {
  363. "assistant_id": "65940acff94777010aa6b796",
  364. "conversation_id": localStorage.conversation_id || "",
  365. "meta_data": {
  366. "mention_conversation_id": "",
  367. "is_test": false,
  368. "input_question_type": "xxxx",
  369. "channel": "",
  370. "draft_id": ""
  371. }, "messages": [
  372. { "role": "user", "content": [{ "type": "text", "text": text }] }]
  373. },
  374. lineRegex: /event:message\ndata: .+/g,
  375. getContent: (data) => data.parts[0].content[0].type == "text" ? data.parts[0].content[0].text : "",
  376. midFunction: (data) => {
  377. if (!localStorage.conversation_id || localStorage.conversation_id.length == 0) {
  378. localStorage.conversation_id = data.conversation_id
  379. }
  380. },
  381. isNotDelta: true
  382. })
  383. } else if (AI == "Yiyan") {
  384. const node = document.querySelector("#eb_model_footer")
  385. node[Object.keys(node)[1]].children[3].props.children[2].props.children[0].props.setText(text);
  386. document.querySelector(".VAtmtpqL").click()
  387. } else if (AI == "Tongyi") {
  388. const node = document.querySelector(".chatInput--eJzBH8LP")
  389. await node[Object.keys(node)[1]].children[0].props.setText(text);
  390.  
  391. await send("[class^=questionItem]", () => {
  392. const node2 = document.querySelector(".operateBtn--zFx6rSR0");
  393. node2[Object.keys(node2)[1]].onClick()
  394. })
  395. } else if (AI == "Claude") {
  396. const node = document.querySelector("fieldset")
  397. const props = node[Object.keys(node)[1]].children[0].props.children[0].props.children[0].props;
  398. props.setInput(text);
  399. document.querySelector("button[aria-label='Send Message']").click();
  400. } else if (AI == "MyTan") {
  401. const conversation_id = location.href.split("chat/")?.[1]
  402. const data = {
  403. "content": [
  404. { "type": "text", "text": text }
  405. ],
  406. "stream": true,
  407. }
  408. if (conversation_id) {
  409. data.conversation_id = conversation_id
  410. } else {
  411. data.conversation = { title: "新对话", model: JSON.parse(localStorage["chosen-model-obj"]).model }
  412. }
  413. requestStream({
  414. api: `https://mytan.maiseed.com.cn/api/v2/messages`,
  415. token: JSON.parse(localStorage["chat-tan-token"]).token,
  416. data,
  417. lineRegex: /data: .+/g,
  418. getContent: (data) => data.choices[0].delta.content,
  419. })
  420. } else if (AI == "ChanlderAi") {
  421. // 更新id
  422. if (!localStorage.conversation_id || localStorage.conversation_id == "") {
  423. async function readStream(stream) {
  424. const reader = stream.getReader();
  425. let result = '';
  426. const decoder = new TextDecoder();
  427. while (true) {
  428. const { done, value } = await reader.read();
  429. if (done) break;
  430.  
  431. result += decoder.decode(value, { stream: true });
  432. }
  433. return result
  434. }
  435. const res = await fetch("https://api.chandler.bet/api/chat/chatHistory", {
  436. method: 'POST',
  437. headers: {
  438. 'Content-Type': 'application/json',
  439. "Authorization": `Bearer ${localStorage.token}`,
  440. "accept": "application/json, text/plain, */*"
  441. },
  442. body: JSON.stringify({ "keywords": "", "model_names": [], "page_size": 10, "page_num": 1 }),
  443. })
  444. const data = JSON.parse(await readStream(res.body)).data[0]
  445. localStorage.conversation_id = data.conversation_id
  446. localStorage.model_name = data.model_name
  447. localStorage.parent_message_id = data.parent_message_id
  448. }
  449. const appData = JSON.parse(localStorage.appLocalStorage)
  450. requestStream({
  451. api: `https://api.chandler.bet/api/chat/Chat`,
  452. token: localStorage.token,
  453. data: {
  454. "uid": appData.email,
  455. "prompt": text,
  456. "model_name": localStorage.model_name,
  457. "request_timeout": 30,
  458. "global_timeout": 100,
  459. "max_retries": 1,
  460. "attachment_list": [],
  461. "parent_message_id": localStorage.parent_message_id,
  462. "conversation_id": localStorage.conversation_id,
  463. "answer_again": false,
  464. "aireply": "",
  465. "timestamp": (new Date()).getTime(),
  466. "status": "",
  467. "app_name": "",
  468. "web_url": "https://api.chandler.bet"
  469. },
  470. lineRegex: /event:message\ndata:.+/g,
  471. getContent: (data) => data.delta
  472. })
  473. } else if (AI == "DeepSeek") {
  474. requestStream({
  475. api: `https://chat.deepseek.com/api/v0/chat/completions`,
  476. token: JSON.parse(localStorage.userToken).value,
  477. data: {
  478. "message": text,
  479. "stream": true,
  480. "model_preference": null,
  481. "model_class":
  482. "deepseek_chat",
  483. "temperature": 0
  484. },
  485. lineRegex: /data: .+/g,
  486. getContent: (data) => data.choices[0].delta.content || ""
  487. })
  488. } else if (AI == "Doubao") {
  489. const node = document.querySelector("[class^=footer]")
  490. await node[Object.keys(node)[1]].children.ref.current.autoTransValue(text);
  491. await sleep(1e3)
  492. document.querySelector("button#flow-end-msg-send").click();
  493. } else if (AI == "AIStudio") {
  494. dispatchInput(".input-wrapper textarea")
  495. await sleep(100)
  496. await send("ms-chat-turn", () => {
  497. const button = document.querySelector('run-button button');
  498. button.click()
  499. })
  500. }
  501. }
  502.  
  503. // 连续发送
  504. const send = async (selector, callback) => {
  505. const oldNumber = document.querySelectorAll(selector).length;
  506. callback();
  507. await sleep(100);
  508. while (document.querySelectorAll(selector).length == oldNumber) {
  509. callback();
  510. await sleep(100);
  511. }
  512. }
  513.  
  514. const uploadFile = async (base64String, fileName) => {
  515. try {
  516. let type
  517. if (fileName.endsWith("pdf")) {
  518. type = "application/pdf"
  519. } else if (fileName.endsWith("png")) {
  520. type = "image/png"
  521. }
  522. function base64ToArrayBuffer(base64) {
  523. const binaryString = atob(base64);
  524. const len = binaryString.length;
  525. const bytes = new Uint8Array(len);
  526. for (let i = 0; i < len; i++) {
  527. bytes[i] = binaryString.charCodeAt(i);
  528. }
  529. return bytes.buffer;
  530. }
  531. if (AI == "AIStudio") {
  532. const button = document.querySelector("ms-add-chunk-menu button")
  533. button && button.click()
  534. }
  535. // 创建一个虚拟的 PDF 文件对象
  536. const fileContent = base64ToArrayBuffer(base64String);
  537. const file = new File([fileContent], fileName, { type });
  538.  
  539. // 创建一个DataTransfer对象
  540. const dataTransfer = new DataTransfer();
  541. dataTransfer.items.add(file);
  542. const fileInput = document.querySelector("input[type=file]") ||
  543. document.querySelector('[data-testid="file-upload"]') // Claude
  544.  
  545. fileInput.files = dataTransfer.files
  546. fileInput.dispatchEvent(new Event('change', { bubbles: true }));
  547. // 需要特殊处理的
  548. if (AI == "AIStudio") {
  549. const button = document.querySelector("ms-add-chunk-menu button")
  550. button && button.click()
  551. }
  552. } catch (e) {
  553. // window.alert(e)
  554. }
  555. }
  556.  
  557. /**
  558. * {api, token, data, lineRegex, getContent, errorFunction, midFunction}
  559. * @param {*} data
  560. */
  561. const requestStream = async (params) => {
  562. fetch(params.api, {
  563. method: 'POST',
  564. headers: {
  565. 'Content-Type': 'application/json',
  566. 'Authorization': `Bearer ${params.token}`
  567. },
  568. body: JSON.stringify(params.data)
  569. })
  570. .then(response => {
  571. if (response.status == 200) {
  572. return response.body.getReader()
  573. } else if (response.status == 400) {
  574. throw new Error('频率过高');
  575. } {
  576. throw new Error('授权失败');
  577. }
  578. })
  579. .then(reader => {
  580. let text = ""
  581. const decoder = new TextDecoder();
  582. window.setTimeout(async () => {
  583. while (true) {
  584. const { done, value } = await reader.read();
  585. if (done) {
  586. await execInZotero(`
  587. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  588. task.responseText = ${JSON.stringify(text)};
  589. task.type = "done";
  590. task.responseType = "markdown"
  591. `)
  592. break
  593. }
  594. try {
  595. const newLines = decoder.decode(value, { stream: true })
  596. for (let line of newLines.match(params.lineRegex)) {
  597. try {
  598. const data = JSON.parse(line.split("data:")[1].trim())
  599. params.midFunction && params.midFunction(data)
  600. text = params.isNotDelta ? params.getContent(data) : (text + params.getContent(data));
  601. } catch (e) {
  602. if (String(e).includes("Stop")) { return }
  603. }
  604. execInZotero(`
  605. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  606. task.responseText = ${JSON.stringify(text)};
  607. task.type = "pending";
  608. task.responseType = "markdown"
  609. `)
  610. }
  611. } catch (e) {
  612. console.log(e)
  613. }
  614. }
  615. }, 0)
  616. })
  617. .catch(e => {
  618. params.errorFunction && params.errorFunction()
  619. })
  620. }
  621. // 阻塞
  622. function sleep(ms) {
  623. return new Promise(resolve => setTimeout(resolve, ms));
  624. }
  625.  
  626. GM_registerMenuCommand('🔗 运行', () => {
  627. isRunning = true
  628. window.alert("🔗 已运行")
  629. });
  630. GM_registerMenuCommand('🎊 断开', () => {
  631. isRunning = false
  632. window.alert("🎊 断开")
  633. });
  634.  
  635. const waitSend = async (selector) => {
  636. let getUserQuestionNum = () => document.querySelectorAll(selector).length
  637. const questionNum = getUserQuestionNum()
  638. while (getUserQuestionNum() == questionNum) {
  639. await sleep(100)
  640. }
  641. }
  642.  
  643. // 通信
  644. await sleep(3000)
  645. while (true) {
  646. if (!isRunning) {
  647. await execInZotero(`
  648. window.Meet.Connector.time = 0;
  649. `)
  650. await sleep(1000)
  651. continue;
  652. }
  653. try {
  654. const tasks = (await execInZotero(`
  655. if (!window.Meet.Connector){
  656. window.Meet.Connector = ${JSON.stringify({
  657. AI, time: Date.now() / 1e3, tasks: []
  658. })};
  659. } else {
  660. window.Meet.Connector.time = ${Date.now() / 1e3};
  661. window.Meet.Connector.AI = "${AI}";
  662. }
  663. window.Meet.Connector
  664. `)).tasks
  665. if (!tasks || tasks.length == 0) {
  666. await sleep(500)
  667. continue
  668. }
  669. const task = tasks.slice(-1)[0]
  670. if (task.type == "pending") {
  671. if (task.file) {
  672. // document.querySelector("[data-testid='create-new-chat-button']").click();
  673. // await sleep(1e3)
  674. await uploadFile(task.file.base64String, task.file.name)
  675. await execInZotero(`
  676. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  677. task.type = "done"
  678. `)
  679. } else if (task.requestText) {
  680. await setText(task.requestText)
  681. // 操作浏览器提问
  682. await execInZotero(`
  683. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  684. task.requestText = "";
  685. task.responseText = "<p>Waiting ${AI}...</p>";
  686. `)
  687. } else {
  688. let isDone = false, text = "", type = "html"
  689. const setZoteroText = async () => {
  690. if (typeof (text) !== "string") { return }
  691. await execInZotero(`
  692. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  693. task.responseText = ${JSON.stringify(text)};
  694. task.type = ${isDone} ? "done" : "pending";
  695. task.responseType = "${type}"
  696. `)
  697. if (isDone) {
  698. await sleep(1000)
  699. await execInZotero(`
  700. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  701. task.responseText = ${JSON.stringify(text)};
  702. `)
  703. }
  704. }
  705. if (AI == "Gemini") {
  706. const outputEle = [...document.querySelectorAll('.conversation-container')].slice(-1)[0];
  707. const contentEle = outputEle.querySelector("model-response .response-content message-content")
  708. if (contentEle) {
  709. isDone = Boolean(outputEle.querySelector(".complete"))
  710. text = contentEle.querySelector(".markdown").innerHTML
  711. await setZoteroText()
  712. }
  713. } else if (AI == "Poe") {
  714. type = "markdown"
  715. const lastNode = [...document.querySelectorAll("[class^=ChatMessagesView_messagePair]")].slice(-1)[0]
  716. const props = lastNode[Object.keys(lastNode)[0]].child.memoizedProps
  717. text = props.pairedMessage.text
  718. isDone = props.pairedMessage.state == "complete"
  719. await setZoteroText()
  720. } else if (AI == "Coze") {
  721. const outputEle = document.querySelector(".message-group-wrapper");
  722. const contentEle = outputEle.querySelector("[data-testid='bot.ide.chat_area.message_box'] .flow-markdown-body")
  723. isDone = Boolean(outputEle.querySelector(".chat-uikit-message-box-container__message__message-box__footer").childNodes.length != 0)
  724. text = contentEle.innerHTML.replace(/<br .+?>/g, "").replace(/<hr .+?>/g, "<hr/>")
  725. await setZoteroText()
  726. } else if (AI == "Yiyan") {
  727. const outputEle = document.querySelector(".ErXhAgf5 .RmHagX8t");
  728. const contentEle = outputEle.querySelector(".custom-html.md-stream-desktop")
  729. isDone = Boolean(outputEle.querySelector(".fXxD0Rtx"))
  730. text = contentEle.innerHTML.replace(/<br .+?>/g, "").replace(/<hr .+?>/g, "<hr/>")
  731. await setZoteroText()
  732. } else if (AI == "Tongyi") {
  733. const lastAnwser = [...document.querySelectorAll("[class^=answerItem]")].slice(-1)[0]
  734. type = "markdown"
  735. 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
  736. isDone = message.contents[message.contents.length - 1].status == "finished"
  737. text = message.contents[message.contents.length - 1].content
  738. await setZoteroText()
  739. } else if (AI == "Doubao") {
  740. const nodes = [...document.querySelectorAll("[class^=message-box-content-wrapper]")]
  741. const node = nodes.slice(-1)[0]
  742. const data = node[Object.keys(node)[0]].child.child.child.child.memoizedState.memoizedState.current.value
  743. type = "markdown"
  744. text = data.content_obj.text;
  745. isDone = data.ext.is_finish == "1";
  746. await setZoteroText()
  747. }
  748. }
  749. }
  750. } catch (e) {
  751. console.log(e)
  752. await sleep(1000)
  753. }
  754. await sleep(10)
  755. }
  756. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址