Zotero GPT Connector

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

目前為 2024-10-30 提交的版本,檢視 最新版本

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

QingJ © 2025

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