Zotero GPT Connector

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

目前為 2025-04-02 提交的版本,檢視 最新版本

  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 MicrosoftCopilot Wenxiaobai Grok
  4. // @namespace http://tampermonkey.net/
  5. // @icon https://github.com/MuiseDestiny/zotero-gpt/blob/bootstrap/addon/chrome/content/icons/favicon.png?raw=true
  6. // @noframes
  7. // @version 4.1.4
  8. // @author Polygon
  9. // @match https://chatgpt.com/*
  10. // @match https://gemini.google.com/*
  11. // @match https://poe.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.*/*
  26. // @match https://node.dawuai.buzz/*
  27. // @match https://aistudio.google.com/*
  28. // @match https://claude.ai0.cn/*
  29. // @match https://grok.com/*
  30. // @include /.+rawchat.+/
  31. // @include /.+chatgpt.+/
  32. // @include /.+claude.+/
  33. // @include /.+qwen.+/
  34. // @include /.+coze.+/
  35. // @include /.+grok.+/
  36. // @match https://www.zaiwen.top/chat/*
  37. // @match https://chat.aite.lol/*
  38. // @match https://yuanbao.tencent.com/chat/*
  39. // @match https://chatgptup.com/*
  40. // @match https://kelaode.kelaodeshare.com/*
  41. // @match https://ihe5u7.aitianhu2.top/*
  42. // @match https://cc01.plusai.io/*
  43. // @match https://arc.aizex.me/*
  44. // @match https://*.chatopens.net/*
  45. // @match https://www.chatwb.com/*
  46. // @match https://www.xixichat.top/*
  47. // @match https://zchat.tech/*
  48. // @match https://*.sorryios.chat/*
  49. // @match https://monica.im/*
  50. // @match https://copilot.microsoft.com/*
  51. // @match https://gptsdd.com/*
  52. // @match https://max.bpjgpt.top/*
  53. // @match https://nbai.tech/
  54. // @match https://x.liaobots.work/*
  55. // @match https://x.liaox.ai/*
  56. // @match https://chat.qwenlm.ai/*
  57. // @match https://lke.cloud.tencent.com/*
  58. // @match https://dazi.co/*
  59. // @match https://www.wenxiaobai.com/*
  60. // @match https://www.techopens.com/*
  61. // @match https://xiaoyi.huawei.com/*
  62. // @match https://chat.baidu.com/*
  63. // @match https://qrms.com/*
  64. // @match https://www.perplexity.ai/*
  65. // @match https://sider.ai/*
  66. // @match https://saas.ai1.bar/*
  67. // @match https://sx.xiaoai.shop/*
  68. // @match https://oai.liuliangbang.vip/*
  69. // @match https://pro.yrai.cc/*
  70. // @match https://cv24-soai.dftianyi.com/*
  71. // @match https://cv24-soai.dftianyi.com/*
  72. // @connect *
  73. // @connect https://kimi.moonshot.cn/*
  74. // @connect https://chatglm.cn/*
  75. // @connect https://chat.deepseek.com/*
  76. // @connect https://chatgpt.com/*
  77. // @connect http://127.0.0.1:23119/zoterogpt
  78. // @grant GM_xmlhttpRequest
  79. // @grant GM_registerMenuCommand
  80. // @grant GM_setValue
  81. // @grant GM_getValue
  82. // @grant unsafeWindow
  83. // @run-at document-start
  84. // ==/UserScript==
  85.  
  86. (async function () {
  87. 'use strict';
  88. let isRunning = true
  89. let AI = "ChatGPT"
  90. const host = location.host
  91. if (host == 'chatgpt.com') {
  92. AI = "ChatGPT"
  93. } else if (host == 'gemini.google.com') {
  94. AI = "Gemini"
  95. } else if (host == 'poe.com') {
  96. AI = "Poe"
  97. } else if (host == 'kimi.moonshot.cn') {
  98. AI = "Kimi"
  99. } else if (host.includes('coze')) {
  100. AI = "Coze"
  101. } else if (host == "chatglm.cn") {
  102. AI = "Chatglm"
  103. } else if (host == 'yiyan.baidu.com') {
  104. AI = "Yiyan"
  105. } else if (host == 'tongyi.aliyun.com' || host == 'qianwen.aliyun.com') {
  106. AI = "Tongyi"
  107. } else if (host.includes("claude") || host.includes("kelaodeshare") || host.includes("yrai")) {
  108. AI = "Claude"
  109. } else if (host == 'mytan.maiseed.com.cn') {
  110. AI = "MyTan"
  111. } else if (host == 'mychandler.bet') {
  112. AI = "ChanlderAi"
  113. } else if (host == 'chat.deepseek.com') {
  114. AI = "DeepSeek"
  115. } else if (host == "www.doubao.com") {
  116. AI = "Doubao"
  117. } else if (host == 'aistudio.google.com') {
  118. AI = "AIStudio"
  119. } else if (host == "www.zaiwen.top") {
  120. AI = "Zaiwen"
  121. } else if (host == 'yuanbao.tencent.com') {
  122. AI = "Yuanbao"
  123. } else if (host == "www.tiangong.cn") {
  124. AI == "Tiangong"
  125. } else if (host == 'monica.im') {
  126. AI = "Monica"
  127. } else if (host == 'copilot.microsoft.com') {
  128. AI = "Copilot"
  129. } else if (location.host.includes('qwen')) {
  130. AI = "Qwen"
  131. } else if (location.host == 'lke.cloud.tencent.com') {
  132. AI = "TencentDeepSeek"
  133. } else if (location.host == 'dazi.co') {
  134. AI = "AskManyAI"
  135. } else if (location.host == 'www.wenxiaobai.com') {
  136. AI = "Wenxiaobai"
  137. } else if (location.host.includes('grok')) {
  138. AI = "Grok"
  139. } else if (location.host == 'xiaoyi.huawei.com') {
  140. AI = "Xiaoyi"
  141. } else if (location.host == 'chat.baidu.com') {
  142. AI = "Baidu"
  143. } else if (location.host == 'www.perplexity.ai') {
  144. AI = "Perplexity"
  145. } else if (location.host == 'sider.ai') {
  146. AI = "Sider"
  147. }
  148.  
  149. const getOutputText = (resp = "", think= "") => {
  150. let text = ""
  151. if (think) {
  152. text += ("<think>" + think)
  153. if (resp) { text += "</think>"}
  154. }
  155. text += resp
  156. return text
  157. }
  158. const requestPatchArr = [
  159. {
  160. AI: "Kimi",
  161. regex: /https:\/\/kimi.moonshot.cn\/api\/chat\/.+\/completion\/stream/,
  162. extract: function (text, allText) {
  163. let resp = "", think = ""
  164. for (let line of allText.split("\n")) {
  165. if (line.startsWith("data")) {
  166. try { JSON.parse(line.split("data: ")[1]) } catch { continue }
  167. const data = JSON.parse(line.split("data: ")[1])
  168. if (data.event == "cmpl") {
  169. resp += data.text || ""
  170. } else if (data.event.match(/^k.+/)) {
  171. think += data.text || ""
  172. }
  173. }
  174. }
  175. if (resp == "") {
  176. if (think != "") {
  177. this.text = ">" + think.replace(/\n/g, "\n>")
  178. }
  179. } else {
  180. this.text = resp
  181. }
  182. },
  183. text: ""
  184. },
  185. {
  186. AI: "AIStudio",
  187. regex: /GenerateContent$/,
  188. extract: function (text) {
  189. let data
  190. while (!data) {
  191. try {
  192. data = JSON.parse(text)
  193. }catch {
  194. text += "]"
  195. }
  196. }
  197. console.log(data)
  198. // this.text = data[0].map(i => (i[0][0][0][0][0][12] ? ">" : "") + i[0][0][0][0][0][1]).join("")
  199. let think = "", resp = ""
  200. for (let i of data[0]) {
  201. let s = i[0][0][0][0][0][1]
  202. if (i[0][0][0][0][0][12]) {
  203. think += s
  204. } else {
  205. resp += s
  206. }
  207. }
  208. this.text = getOutputText(resp, think)
  209. },
  210. text: "",
  211. },
  212. {
  213. AI: "ChatGPT",
  214. regex: /backend-api\/conversation$/,
  215. extract: function (text) {
  216. for (let line of text.split("\n")) {
  217. if (line.startsWith('data: {"message')) {
  218. try { JSON.parse(line.split("data: ")[1]) } catch { continue }
  219. const data = JSON.parse(line.split("data: ")[1])
  220. if (data.message.content.content_type == "text") {
  221. this.text = data.message.content.parts[0]
  222. }
  223. } else if (line.startsWith("data: {")) {
  224. try { JSON.parse(line.split("data: ")[1]) } catch { continue }
  225. const data = JSON.parse(line.split("data: ")[1])
  226. const streamPath = "/message/content/parts/0"
  227. if (Object.keys(data).length == 1 && typeof (data.v) == "string" && this.p == streamPath) {
  228. this.text += data.v
  229. } else if ((this.p == streamPath || data.p == streamPath)) {
  230. this.p = streamPath
  231. if (data.o && data.o == "add") {
  232. this.text = ""
  233. }
  234. if (typeof (data.v) == "string") {
  235. this.text += data.v
  236. } else if (Array.isArray(data.v)) {
  237. const d = data.v.find(i => i.p == streamPath)
  238. if (d && typeof (d.v) == "string") {
  239. this.text += d.v
  240. }
  241. }
  242. } else {
  243. this.p = ""
  244. }
  245. } else if (line.startsWith("data: [")) {
  246. try {
  247. const delta = JSON.parse(line.replace(/^data:\s/, "")).slice(-1)[0]
  248. if (typeof (delta) == "string" ) {
  249. this.text += delta
  250. }
  251. } catch {}
  252. }
  253. }
  254. },
  255. p: "",
  256. text: ""
  257. },
  258. {
  259. AI: "Claude",
  260. regex: /chat_conversations\/.+\/completion/,
  261. extract: function (text) {
  262. for (let line of text.split("\n")) {
  263. if (line.startsWith("data: {")) {
  264. try { JSON.parse(line.split("data: ")[1]) } catch { continue }
  265. const data = JSON.parse(line.split("data: ")[1])
  266. if (data.type && data.type == "completion") {
  267. this.text += data.completion || ""
  268. } else if (data.type && data.type == "content_block_delta") {
  269. this.text += data.delta.text || ""
  270. }
  271. }
  272. }
  273. },
  274. text: ""
  275. },
  276. {
  277. AI: "Chatglm",
  278. regex: /stream/,
  279. extract: function (text) {
  280. for (let line of text.split("\n")) {
  281. if (line.startsWith("data:")) {
  282. try { JSON.parse(line.split("data:")[1]) } catch { continue }
  283. const data = JSON.parse(line.split("data:")[1])
  284. try {
  285. if (data.parts && data.parts[0] && data.parts[0].content[0].type == "text") {
  286. this.text = data.parts[0].content[0].text
  287. }
  288. } catch (e) { console.log("extract", e) }
  289. }
  290. }
  291. },
  292. text: ""
  293. },
  294. {
  295. AI: "Zaiwen",
  296. regex: /admin\/chatbot$/,
  297. extract: function (text) {
  298. this.text = text
  299.  
  300. },
  301. text: ""
  302. },
  303. {
  304. AI: "Yuanbao",
  305. regex: /api\/chat\/.+/,
  306. extract: function (allText) {
  307. let think = "", resp = ""
  308. for (let line of allText.split("\n")) {
  309. if (line.startsWith("data: {")) {
  310. try { JSON.parse(line.split("data: ")[1]) } catch { continue }
  311. const data = JSON.parse(line.split("data: ")[1])
  312. try {
  313. if (data.type == "text") {
  314. resp += (data.msg || "")
  315. } else if (data.type == "think") {
  316. think += (data.content || "")
  317. } else if (data.type == "replace") {
  318. resp += `![](${data.replace.multimedias[0].url})\n${data.replace.multimedias[0].desc}`
  319. }
  320. } catch (e) { console.log("extract", e) }
  321. }
  322. }
  323. this.text = getOutputText(resp, think)
  324. },
  325. text: ""
  326. },
  327. {
  328. AI: "DeepSeek",
  329. regex: /completion$/,
  330. extract: function (text) {
  331. let resp = "", think = ""
  332. for (let line of text.split("\n")) {
  333. if (line.startsWith("data: {")) {
  334. try { JSON.parse(line.split("data: ")[1]) } catch { continue }
  335. const data = JSON.parse(line.split("data: ")[1])
  336. try {
  337. if (data.choices) {
  338. if (data.choices[0].delta.type == "thinking") {
  339. think += (data.choices[0].delta.content || "")
  340. } else if (data.choices[0].delta.type == "text") {
  341. resp += (data.choices[0].delta.content || "")
  342. }
  343. } else {
  344. if (typeof (data.v) == "string") {
  345. resp += data.v || ""
  346. }
  347. }
  348. } catch (e) { console.log("extract", e) }
  349. }
  350. }
  351. this.text = getOutputText(resp, think)
  352. },
  353. text: "",
  354. },
  355. {
  356. AI: "ChanlderAi",
  357. regex: /api\/chat\/Chat$/,
  358. extract: function (text) {
  359. for (let line of text.split("\n")) {
  360. console.log("line", line)
  361. if (line.startsWith("data:{")) {
  362. try { JSON.parse(line.split("data:")[1]) } catch { continue }
  363. const data = JSON.parse(line.split("data:")[1])
  364. try {
  365. this.text += data.delta
  366. } catch (e) { console.log("extract", e) }
  367. }
  368. }
  369.  
  370. },
  371. text: ""
  372. },
  373. {
  374. AI: "Yiyan",
  375. regex: /chat\/conversation\/v2$/,
  376. extract: function (text, allText) {
  377. let delta = ""
  378. for (let line of allText.split("\n\nevent:message\n")) {
  379. if (line.startsWith("data:{")) {
  380. try { JSON.parse(line.split("data:")[1]) } catch { continue }
  381. const data = JSON.parse(line.split("data:")[1])
  382. try {
  383. delta += data.data.text || ""
  384. } catch (e) { console.log("extract", e) }
  385. }
  386. }
  387. this.text = delta
  388. },
  389. text: "",
  390. },
  391. {
  392. AI: "Doubao",
  393. regex: /samantha\/chat\/completion/,
  394. extract: function (text) {
  395. for (let line of text.split("\n")) {
  396. if (line.startsWith("data:")) {
  397. try {
  398. const data = JSON.parse(JSON.parse(line.slice(5)).event_data)
  399. if (JSON.parse(data.message.content).type != 1) {
  400. this.text += JSON.parse(data.message.content).text || ""
  401. }
  402. } catch {}
  403. }
  404. }
  405. },
  406. text: "",
  407. },
  408. {
  409. AI: "Monica",
  410. regex: /api.monica.im\/api\/custom_bot\/chat/,
  411. extract: function (text, allText) {
  412. let think = "", resp = ""
  413. for (let line of allText.split("\n")) {
  414. if (line.startsWith("data:")) {
  415. const data = JSON.parse(line.slice(5))
  416. if (Boolean(data.agent_status) && Boolean(data.agent_status.type == "thinking_detail_stream")) {
  417. think += (data.agent_status.metadata.reasoning_detail || "")
  418. }
  419. resp += data.text
  420. }
  421. }
  422. this.text = getOutputText(resp, think)
  423. },
  424. text: "",
  425. },
  426. {
  427. AI: "Qwen",
  428. regex: /chat\/completions$/,
  429. extract: function (text, allText) {
  430. for (let line of text.split("\n")) {
  431. if (line.startsWith("data: {")) {
  432. try { JSON.parse(line.split("data:")[1]) } catch { continue }
  433. const data = JSON.parse(line.split("data:")[1])
  434. try {
  435. this.text += data.choices[0].delta.content
  436. } catch (e) { console.log("extract", e) }
  437. }
  438. }
  439. },
  440. text: "",
  441. },
  442. {
  443. AI: "AskManyAI",
  444. regex: /engine\/sseQuery/,
  445. extract: function (text, allText) {
  446. let think = "", resp = ""
  447. for (let line of allText.split("\n")) {
  448. if (line.startsWith("data: {")) {
  449. try { JSON.parse(line.split("data:")[1]) } catch { continue }
  450. const data = JSON.parse(line.split("data:")[1])
  451. try {
  452. if (data.content.startsWith("[HIT-REF]")) { continue}
  453. if (data.event == "thinking") {
  454. think += data.content
  455. } else if (data.event == "resp") {
  456. resp += data.content
  457. }
  458. } catch (e) { console.log("extract", e) }
  459. }
  460. }
  461. this.text = getOutputText(resp, think)
  462.  
  463. },
  464. text: "",
  465. },
  466. {
  467. AI: "Wenxiaobai",
  468. regex: /conversation\/chat\/v1$/,
  469. extract: function (_, allText) {
  470. if (!allText) { return }
  471. let resp = ""
  472. for (let line of allText.replace(/event:message\ndata/g, "message").split("\n")) {
  473. if (line.startsWith("message:{")) {
  474. try { JSON.parse(line.split("message:")[1]) } catch { continue }
  475. const data = JSON.parse(line.split("message:")[1])
  476. try {
  477. resp += data.content || ""
  478. } catch (e) { console.log("extract", e) }
  479. }
  480. }
  481. console.log(resp)
  482. resp = resp.replace(/^```ys_think[\s\S]+?\n\n```\n/, "").replace(/[\s\S]+?```ys_think/, "```ys_think")
  483. if (resp.includes("```ys_think")) {
  484. resp = ">"+resp.split("\n").slice(3).join("\n>")
  485. }
  486. this.text = resp
  487. },
  488. text: "",
  489. },
  490. {
  491. AI: "Coze",
  492. regex: /conversation\/chat/,
  493. extract: function (text, allText) {
  494. for (let line of text.split("\n")) {
  495. if (line.startsWith("data:{")) {
  496. try { JSON.parse(line.split("data:")[1]) } catch { continue }
  497. const data = JSON.parse(line.split("data:")[1])
  498. try {
  499. if (data.message.type == "answer") {
  500.  
  501. this.text += data.message.content || ""
  502. }
  503. } catch (e) { console.log("extract", e) }
  504. }
  505. }
  506. },
  507. text: "",
  508. },
  509. {
  510. AI: "Grok",
  511. regex: /(conversations\/new|responses)$/,
  512. extract: function (text, allText) {
  513. console.log(text)
  514. let resp="", think=""
  515. for (let t of allText.split("\n") ) {
  516. let data
  517. try {
  518. data = JSON.parse(t).result
  519. if (data.response) {data = data.response}
  520. if (data.isThinking) {
  521. think += data.token || ""
  522. } else{
  523. resp += data.token || ""
  524. }
  525. } catch {continue}
  526. }
  527. this.text = getOutputText(resp, think)
  528.  
  529. },
  530. text: "",
  531. },
  532. {
  533. AI: "Baidu",
  534. regex: /conversation$/,
  535. extract: function (text, allText) {
  536. let resp = "", think = ""
  537. for (let t of allText.split("\n")) {
  538. if (!t.startsWith("data:")) {continue}
  539. let data
  540. console.log(t.slice(5))
  541. try {
  542. data = JSON.parse(t.slice(5)).data
  543. } catch { continue }
  544. console.log(data)
  545. if (!data) { continue}
  546. if (data.message.metaData.state == "generating-resp") {
  547. if (data.message.content.generator.component == "reasoningContent") {
  548. think += data.message.content.generator.data.value || ""
  549. } else if (data.message.content.generator.component == "markdown-yiyan"){
  550. resp += data.message.content.generator.data.value || ""
  551. }
  552. }
  553. }
  554. this.text = getOutputText(resp, think)
  555.  
  556. },
  557. text: "",
  558. },
  559. {
  560. AI: "MyTan",
  561. regex: /messages$/,
  562. extract: function (text, allText) {
  563. for (let line of text.split("\n")) {
  564. if (line.startsWith("data:")) {
  565. try { JSON.parse(line.split("data:")[1]) } catch { continue }
  566. const data = JSON.parse(line.split("data:")[1])
  567. try {
  568. this.text += data.choices[0].delta.content
  569. } catch (e) { console.log("extract", e) }
  570. }
  571. }
  572. },
  573. text: "",
  574. },
  575. {
  576. AI: "Perplexity",
  577. regex: /perplexity_ask$/,
  578. extract: function (text, allText) {
  579. let think = "", resp = ""
  580. for (let line of allText.split("\n")) {
  581. if (line.startsWith("data:")) {
  582. try { JSON.parse(line.split("data:")[1]) } catch { continue }
  583. const data = JSON.parse(line.split("data:")[1])
  584. try {
  585. if (data.blocks) {
  586. for (let block of data.blocks) {
  587. if (block.intended_usage == "ask_text") {
  588. resp += block.markdown_block.answer || ""
  589. } else {
  590. // 找到key
  591. const k = Object.keys(block).find(i=>i.endsWith("block"))
  592. think += block[k].goals.map(i=>i.description).join("") || ""
  593. }
  594. }
  595. }
  596.  
  597. } catch (e) { console.log("extract", e) }
  598. }
  599. }
  600. this.text = getOutputText(resp, think)
  601.  
  602. },
  603. text: "",
  604. },
  605. {
  606. AI: "Sider",
  607. regex: /completions$/,
  608. extract: function (text, allText) {
  609. let think = "", resp = ""
  610. for (let line of allText.split("\n")) {
  611. if (line.startsWith("data:")) {
  612. try { JSON.parse(line.split("data:")[1]) } catch { continue }
  613. const data = JSON.parse(line.split("data:")[1])
  614. try {
  615. if (data.data.type == "reasoning_content") {
  616. think += data.data.reasoning_content.text || ""
  617. } else if (data.data.type == "text") {
  618. resp += data.data.text || ""
  619. }
  620.  
  621. } catch (e) { console.log("extract", e) }
  622. }
  623. }
  624. this.text = getOutputText(resp, think)
  625.  
  626. },
  627. text: "",
  628. },
  629. {
  630. AI: "Gemini",
  631. regex: /BardFrontendService\/StreamGenerate/,
  632. extract: function (text) {
  633. let think = "", resp = ""
  634. for (let line of text.split(/\n\d+\n/)) {
  635. try {
  636. const data = JSON.parse(line)
  637. if (data[0][0] == "wrb.fr") {
  638. const data1 = JSON.parse(data[0][2])[4][0]
  639. resp = data1[1][0]
  640. think = data1[37][0][0]
  641. }
  642. } catch {}
  643. }
  644. this.text = getOutputText(resp, think)
  645.  
  646. },
  647. text: "",
  648. },
  649. ]
  650.  
  651. // 数据拦截,部分网站需要
  652.  
  653. const originalXhrOpen = XMLHttpRequest.prototype.open;
  654. XMLHttpRequest.prototype.open = function (method, url, async) {
  655. this.addEventListener('readystatechange', async function () {
  656. let requestPatch
  657. if ((requestPatch = requestPatchArr.find(i => i.AI == AI && i.regex.test(url)))) {
  658. execInZotero(`
  659. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length - 1];
  660. task.responseText = ${JSON.stringify(requestPatch.text || "")};
  661. task.responseType = "markdown";
  662. `);
  663. if (this.readyState === 3) {
  664. try {
  665. requestPatch.extract(this.responseText)
  666. } catch(e) {
  667. console.log("error extract", e, this.responseText)
  668. }
  669. await execInZotero(`
  670. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  671. task.responseText = ${JSON.stringify(requestPatch.text || "")};
  672. task.type = "pending";
  673. task.responseType = "markdown"
  674. `)
  675. } else if ([0, 4].includes(this.readyState)) {
  676. await execInZotero(`
  677. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  678. task.responseText = ${JSON.stringify(requestPatch.text || "")};
  679. task.type = "done";
  680. task.responseType = "markdown"
  681. `)
  682. requestPatch.text = ""
  683. }
  684. }
  685. });
  686. originalXhrOpen.apply(this, arguments);
  687. };
  688.  
  689.  
  690. const originalFetch = window.fetch;
  691. unsafeWindow.fetch = function () {
  692. return originalFetch.apply(this, arguments)
  693. .then(response => {
  694. console.log("fetch")
  695. const url = response.url
  696. const requestPatch = requestPatchArr.find(i => i.AI == AI && i.regex.test(url))
  697. if (requestPatch) {
  698. requestPatch.text = ""
  699. execInZotero(`
  700. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length - 1];
  701. task.responseText = ${JSON.stringify(requestPatch.text)};
  702. task.responseType = "markdown";
  703. `);
  704. const clonedResponse = response.clone();
  705. console.log("requestPatch", requestPatch)
  706. console.log(clonedResponse)
  707. const reader = clonedResponse.body.getReader();
  708. const decoder = new TextDecoder()
  709. let allText = ""
  710. function processStream() {
  711. reader.read().then(({ done, value }) => {
  712. if (done) {
  713. console.log(allText)
  714. execInZotero(`
  715. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length - 1];
  716. task.responseText = ${JSON.stringify(requestPatch.text || "")};
  717. task.type = "done";
  718. task.responseType = "markdown";
  719. `);
  720. requestPatch.text = ""
  721. return;
  722. }
  723.  
  724. // 将 Uint8Array 转为字符串
  725. const text = decoder.decode(value, { stream: true });
  726. allText += text
  727. try {
  728. requestPatch.extract(text, allText)
  729. } catch (e) { console.log("requestPatch.extract(text)", e) }
  730. execInZotero(`
  731. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length - 1];
  732. task.responseText = ${JSON.stringify(requestPatch.text || "")};
  733. task.responseType = "markdown";
  734. `);
  735.  
  736. // 递归调用,继续读取流数据
  737. processStream();
  738. }).catch(error => {
  739. // 捕获所有错误,包括 AbortError
  740. console.log("Error when Patch", error)
  741. execInZotero(`
  742. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length - 1];
  743. task.responseText = ${JSON.stringify(requestPatch.text || "")};
  744. task.type = "done";
  745. task.responseType = "markdown";
  746. `);
  747. requestPatch.text = ""
  748. });
  749. }
  750.  
  751. // 开始处理流
  752. window.setTimeout(() => {
  753. processStream();
  754. })
  755. }
  756. return response;
  757. });
  758. };
  759.  
  760.  
  761. // 在Zotero中执行代码
  762. async function execInZotero(code) {
  763. code = `
  764. if (!window.Meet.Connector){
  765. window.Meet.Connector = ${JSON.stringify({
  766. AI, time: Date.now() / 1e3, tasks: []
  767. })};
  768. } else {
  769. window.Meet.Connector.time = ${Date.now() / 1e3};
  770. window.Meet.Connector.AI = "${AI}";
  771. }
  772. ${code}`
  773. try {
  774. return new Promise((resolve, reject) => {
  775. GM_xmlhttpRequest({
  776. method: "POST",
  777. url: "http://127.0.0.1:23119/zoterogpt",
  778. headers: {
  779. "Content-Type": "application/json",
  780. },
  781. responseType: "json",
  782. data: JSON.stringify({ code }),
  783. onload: function (response) {
  784. if (response.status >= 200 && response.status < 300) {
  785. resolve(response.response.result);
  786. } else {
  787. reject(new Error(`Request failed with status: ${response.status}`));
  788. }
  789. },
  790. onerror: function (error) {
  791. reject(new Error('Network error'));
  792. }
  793. });
  794. });
  795. } catch (e) {
  796. window.alert("execInZotero error: " + code);
  797. return ""
  798. }
  799. }
  800.  
  801. // 设定ChatGPT输入框文本并发送
  802. const setText = async (text) => {
  803. const dispatchInput = (selector) => {
  804. // 获取 input 输入框的dom对象
  805. var inputNode = document.querySelector(selector);
  806. if (!inputNode) { return }
  807. // 修改input的值
  808.  
  809. inputNode.value = text;
  810. // plus
  811. try {
  812. inputNode.innerHTML = text.split("\n").map(i => `<p>${i}</p>`).join("\n");
  813. } catch { }
  814. // 设置输入框的 input 事件
  815. var event = new InputEvent('input', {
  816. 'bubbles': true,
  817. 'cancelable': true,
  818. });
  819. inputNode.dispatchEvent(event);
  820. }
  821. const setTextareaValue = async (textarea) => {
  822. const props = Object.values(textarea)[1]
  823.  
  824. // 获取目标 DOM 节点(假设 temp2 是 DOM 元素引用)
  825. const targetElement = textarea;
  826.  
  827. // 创建伪事件对象
  828. const e = {
  829. target: targetElement,
  830. currentTarget: targetElement,
  831. type: 'change',
  832. };
  833.  
  834. // 手动设置值(需同时更新 DOM 和 React 状态)
  835. targetElement.value = text;
  836.  
  837. // 触发 React 的 onChange 处理
  838. await props.onChange(e);
  839. }
  840. const setEditorText = async () => {
  841. const editor = document.querySelector("[data-lexical-editor]").__lexicalEditor
  842. await editor.setEditorState(
  843. editor.parseEditorState(
  844. {
  845. "root": {
  846. "children": [
  847. {
  848. "children": [
  849. {
  850. "detail": 0,
  851. "format": 0,
  852. "mode": "normal",
  853. "style": "",
  854. "text": text,
  855. "type": "text",
  856. "version": 1
  857. }
  858. ],
  859. "direction": null,
  860. "format": "",
  861. "indent": 0,
  862. "type": "paragraph",
  863. "version": 1,
  864. "textFormat": 0
  865. }
  866. ],
  867. "direction": null,
  868. "format": "",
  869. "indent": 0,
  870. "type": "root",
  871. "version": 1
  872. }
  873. }
  874. )
  875. )
  876. }
  877. if (AI == "ChatGPT") {
  878. dispatchInput('#prompt-textarea')
  879. await sleep(100)
  880. await send("article", () => {
  881. const button = document.querySelector('[data-testid="send-button"]');
  882. button.click()
  883. })
  884. } else if (AI == "Gemini") {
  885. // 获取 input 输入框的dom对象
  886. const element_input = window.document.querySelector('rich-textarea .textarea');
  887. // 修改input的值
  888. element_input.textContent = text;
  889. await send(".conversation-container", () => {
  890. const button = document.querySelector('.send-button');
  891. button.click()
  892. })
  893. } else if (AI == "Poe") {
  894. dispatchInput('textarea[class*=GrowingTextArea_textArea]')
  895. document.querySelector("[data-button-send=true]").click()
  896. setTimeout(() => {
  897. document.querySelector("[data-button-send=true]").click()
  898. }, 100)
  899. } else if (AI == "Kimi") {
  900. await setEditorText()
  901. await send(".chat-content-item", () => {
  902. const button = document.querySelector('.send-button');
  903. button.click()
  904. })
  905.  
  906. } else if (AI == "Coze") {
  907. const textarea = document.querySelector("textarea.rc-textarea")
  908. await setTextareaValue(textarea)
  909. await sleep(100)
  910. await send("[data-message-id]", () => {
  911. const button = document.querySelector('button[data-testid="bot-home-chart-send-button"]');
  912. button.click()
  913. })
  914. } else if (AI == "Chatglm") {
  915. dispatchInput(".input-box-inner textarea")
  916. await send(".item.conversation-item", () => {
  917. const button = document.querySelector('.enter img');
  918. if (button) {
  919. const mouseDownEvent = new MouseEvent('mousedown', {
  920. bubbles: true,
  921. cancelable: true
  922. });
  923. button.dispatchEvent(mouseDownEvent);
  924. }
  925. })
  926. } else if (AI == "Yiyan") {
  927. const node = document.querySelector(".oeNDrlEA")
  928. await node[Object.keys(node)[1]].children[2].props.children[0].props.onChange(text)
  929. await sleep(1e3)
  930. await send(".dialogue_card_item", () => {
  931. document.querySelector("#sendBtn").click()
  932. },1e3)
  933. } else if (AI == "Tongyi") {
  934. setTextareaValue(document.querySelector("textarea.ant-input"))
  935. await send("[class^=questionItem]", () => {
  936. const node2 = document.querySelector(".operateBtn--zFx6rSR0");
  937. node2[Object.keys(node2)[1]].onClick()
  938. })
  939. } else if (AI == "Claude") {
  940. const node = document.querySelector("fieldset")
  941. const props = node[Object.keys(node)[1]].children[0].props.children[0].props.children[0].props;
  942. await props.setPrompt(text);
  943. await sleep(100)
  944. document.querySelector("button[aria-label='Send Message']").click();
  945. } else if (AI == "MyTan") {
  946. dispatchInput(".talk-textarea")
  947. await sleep(100)
  948. await send(".message-container .mytan-model-avatar", () => {
  949. const button = document.querySelector('.send-icon');
  950. button.click()
  951. })
  952. } else if (AI == "ChanlderAi") {
  953. dispatchInput(".chandler-content_input-area")
  954. await sleep(100)
  955. await send(".chandler-ext-content_communication-group", () => {
  956. const button = document.querySelector('.send');
  957. button.click()
  958. })
  959. } else if (AI == "DeepSeek") {
  960. const textarea = document.querySelector("#chat-input")
  961. // node[Object.keys(node)[1]].onChange({ currentTarget: { value: text } })
  962. setTextareaValue(textarea)
  963. await sleep(100)
  964. await send("._4f9bf79", () => {
  965. const button = document.querySelector('._7436101');
  966. button.click()
  967. })
  968. } else if (AI == "Doubao") {
  969. setTextareaValue(document.querySelector('[data-testid="chat_input_input"]'))
  970. await sleep(100)
  971. await send("[class^=message-block-container]", () => {
  972. const button = document.querySelector("button#flow-end-msg-send");
  973. button.click()
  974. })
  975. } else if (AI == "AIStudio") {
  976. dispatchInput(".text-wrapper textarea")
  977. await sleep(100)
  978. await send("ms-chat-turn", () => {
  979. const button = document.querySelector('run-button button');
  980. button.click()
  981. })
  982. } else if (AI == "Zaiwen") {
  983. dispatchInput('textarea.arco-textarea')
  984. await sleep(100)
  985. await send(".sessions .item", () => {
  986. const button = document.querySelector('img.send');
  987. button.click()
  988. });
  989. } else if (AI == "Yuanbao") {
  990. dispatchInput('.chat-input-editor .ql-editor')
  991. await sleep(100)
  992. await send(".agent-chat__bubble__content", () => {
  993. const button = document.querySelector('.icon-send');
  994. button.click()
  995. })
  996. } else if (AI == "Monica") {
  997. const elements = document.querySelectorAll(".chat--PCM74");
  998.  
  999. const visibleElements = [];
  1000.  
  1001. // 遍历所有元素
  1002. elements.forEach(element => {
  1003. if (element.style.display !== 'none') {
  1004. // 如果不是 'none',将其添加到数组
  1005. visibleElements.push(element);
  1006. }
  1007. });
  1008.  
  1009. visibleElements.forEach(element => {
  1010. element.parentNode.insertBefore(element, element.parentNode.firstChild);
  1011. });
  1012.  
  1013. const textarea = document.querySelector('textarea.ant-input')
  1014. textarea[Object.keys(textarea)[1]].onChange({ target: { value: text }, currentTarget: { value: text } })
  1015. await sleep(100)
  1016. await send("[class^=chat-message]", () => {
  1017. const button = document.querySelector('[class^=input-msg-btn]')
  1018. button[Object.keys(button)[1]].onClick({ isTrusted: true, stopPropagation: () => { } })
  1019. })
  1020. } else if (AI == "Copilot") {
  1021. const node = document.querySelector("textarea#userInput").parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode
  1022. node[Object.keys(node)[1]].children[1].props.onSubmit(text)
  1023. } else if (AI == "Qwen") {
  1024. dispatchInput("#chat-input")
  1025. await sleep(100)
  1026. await send("[id^=message]", () => {
  1027. const button = document.querySelector('#send-message-button');
  1028. button.click()
  1029. })
  1030. } else if (AI == "TencentDeepSeek") {
  1031. document.querySelector(".question-input").__vue__.onStopStream()
  1032. const div = document.querySelector(".question-input-inner__textarea")
  1033. await div.__vue__.onChange(text.slice(0,10000))
  1034. const chatDiv = document.querySelector(".client-chat");
  1035. const total = chatDiv.__vue__.msgList.length
  1036. // 等待新回答
  1037. while (chatDiv.__vue__.msgList.length - total < 1) {
  1038. document.querySelector(".question-input").__vue__.onSendQuestion()
  1039. await sleep(100)
  1040. }
  1041. // 等待新回答
  1042. while (chatDiv.__vue__.msgList.length - total < 2) {
  1043. await sleep(100)
  1044. }
  1045. } else if (AI == "AskManyAI") {
  1046. dispatchInput('textarea.textarea_input')
  1047. await sleep(100)
  1048. await send(".main-box-center .list", () => {
  1049. const button = document.querySelector('.chat-input-button-inner .fs_button');
  1050. button.click()
  1051. })
  1052. } else if (AI == "Wenxiaobai") {
  1053. const textarea = document.querySelector('[class^=MsgInput_input_box] textarea')
  1054. await setTextareaValue(textarea)
  1055. await sleep(100)
  1056. await send("[class^=Answser_answer_content]", async () => {
  1057. document.querySelector("[class*=MsgInput_send_btn]").parentNode.click()
  1058. })
  1059. } else if (AI == "Grok") {
  1060. const textarea = document.querySelector('textarea')
  1061. await setTextareaValue(textarea)
  1062. await sleep(100)
  1063. await send(".items-center .items-start", async () => {
  1064. document.querySelector("button[type=submit]")?.click()
  1065. })
  1066. } else if (AI == "Xiaoyi") {
  1067. dispatchInput('textarea')
  1068. await sleep(100)
  1069. await send(".receive-box", () => {
  1070. const button = document.querySelector('.send-button');
  1071. button.click()
  1072. })
  1073. } else if (AI == "Baidu") {
  1074. document.querySelector("#chat-input-box").innerText = text
  1075. await sleep(100)
  1076. await send("[class^=index_answer-container]", () => {
  1077. const button = document.querySelector('.send-icon');
  1078. button.click()
  1079. })
  1080. } else if (AI == "Perplexity") {
  1081. setTextareaValue(document.querySelector("main textarea"))
  1082. await send(".-inset-md", () => {
  1083. const node = document.querySelector("button[aria-label=Submit]");
  1084. node.click()
  1085. })
  1086. } else if (AI == "Sider") {
  1087. setTextareaValue(document.querySelector("textarea.chatBox-input"))
  1088. await sleep(100)
  1089. await send(".message-item", () => {
  1090. const button = document.querySelector('.send-btn');
  1091. button.click()
  1092. })
  1093. } else if (AI == "Microsoft") {
  1094. await setEditorText()
  1095. await sleep(1000)
  1096. await send("[id^=chatMessageResponser]", () => {
  1097. const button = document.querySelector('button[type="submit"]');
  1098. button.click()
  1099. })
  1100. }
  1101.  
  1102. }
  1103.  
  1104. // 连续发送
  1105. const send = async (selector, callback, delatTime=100) => {
  1106. const oldNumber = document.querySelectorAll(selector).length;
  1107. callback();
  1108. await sleep(delatTime);
  1109. while (document.querySelectorAll(selector).length == oldNumber) {
  1110. try {
  1111. callback();
  1112. await sleep(delatTime);
  1113. } catch {break}
  1114. }
  1115. }
  1116.  
  1117. const uploadFile = async (base64String, fileName) => {
  1118. try {
  1119. let fileType;
  1120. if (fileName.endsWith("pdf")) {
  1121. fileType = "application/pdf";
  1122. } else if (fileName.endsWith("png")) {
  1123. fileType = "image/png";
  1124. }
  1125. function base64ToArrayBuffer(base64) {
  1126. const binaryString = atob(base64);
  1127. const len = binaryString.length;
  1128. const bytes = new Uint8Array(len);
  1129. for (let i = 0; i < len; i++) {
  1130. bytes[i] = binaryString.charCodeAt(i);
  1131. }
  1132. return bytes.buffer;
  1133. }
  1134.  
  1135. const AIData = {
  1136. ChatGPT: {
  1137. method: "input",
  1138. selector: "input[type=file]",
  1139. },
  1140. Tongyi: {
  1141. method: "drag",
  1142. selector: "[class^=chatInput]",
  1143. },
  1144. Kimi: {
  1145. method: "input",
  1146. selector: "input[type=file]"
  1147. },
  1148. Claude: {
  1149. method: "input",
  1150. selector: "input[type=file]",
  1151. },
  1152. AIStudio: {
  1153. method: "drag",
  1154. selector: ".text-wrapper",
  1155. },
  1156. Chatglm: {
  1157. method: "input",
  1158. selector: "input[type=file]",
  1159. },
  1160. Doubao: {
  1161. method: "input",
  1162. selector: "input[type=file]",
  1163. },
  1164. Zaiwen: {
  1165. method: "drag",
  1166. selector: ".arco-upload-draggable",
  1167. },
  1168. DeepSeek: {
  1169. method: "drag",
  1170. selector: ".bf38813a",
  1171. },
  1172. Yuanbao: {
  1173. method: "drag",
  1174. selector: ".agent-chat__input-box"
  1175. },
  1176. ChanlderAi: {
  1177. method: "input",
  1178. selector: "input[type=file]",
  1179. },
  1180. Yiyan: {
  1181. method: "drag",
  1182. selector: ".UxLYHqhv",
  1183. },
  1184. Poe: {
  1185. method: "drag",
  1186. selector: ".ChatDragDropTarget_dropTarget__1WrAL"
  1187. },
  1188. Monica: {
  1189. method: "drag",
  1190. selector: "[class^=chat-input-v2]"
  1191. },
  1192. Copilot: {
  1193. method: "input",
  1194. selector: "input[type=file]",
  1195. },
  1196. Qwen: {
  1197. method: "input",
  1198. selector: "input[type=file]",
  1199. },
  1200. TencentDeepSeek: {
  1201. method: "input",
  1202. selector: "input[type=file]",
  1203. },
  1204. AskManyAI: {
  1205. method: "input",
  1206. selector: "input[type=file]",
  1207. },
  1208. Wenxiaobai: {
  1209. method: "input",
  1210. selector: "input[type=file]",
  1211. },
  1212. Coze: {
  1213. method: "input",
  1214. selector: "input[type=file]",
  1215. },
  1216. Baidu: {
  1217. method: "drag",
  1218. selector: "[class^=chat-bottom-wrapper]"
  1219. },
  1220. Gemini: {
  1221. method: "drag",
  1222. selector: 'div[xapfileselectordropzone]'
  1223. }
  1224. };
  1225. if (!AIData[AI]) {
  1226. AIData[AI] = {
  1227. method: "input",
  1228. selector: 'input[type=file]',
  1229. }
  1230. }
  1231. if (AIData[AI]) {
  1232. const { method, selector, until } = AIData[AI];
  1233. if (method === "input") {
  1234. // 创建一个虚拟的文件对象
  1235. const fileContent = base64ToArrayBuffer(base64String);
  1236. const file = new File([fileContent], fileName, { type: fileType });
  1237.  
  1238. // 创建一个DataTransfer对象,并添加文件
  1239. const dataTransfer = new DataTransfer();
  1240. dataTransfer.items.add(file);
  1241.  
  1242. let fileInput = document.querySelector(selector);
  1243. if (fileInput) {
  1244. fileInput.files = dataTransfer.files;
  1245. fileInput.dispatchEvent(new Event('change', { bubbles: true }));
  1246. } else {
  1247. window.alert(AI + "网页未加载完毕,或改动导致未获取到fileInput,请联系开发者修复")
  1248. }
  1249. } else if (method === "drag") {
  1250. // 创建一个虚拟的文件对象
  1251. const fileContent = base64ToArrayBuffer(base64String);
  1252. const file = new File([fileContent], fileName, { type: fileType });
  1253.  
  1254. // 创建一个DataTransfer对象,并添加文件
  1255. const dataTransfer = new DataTransfer();
  1256. dataTransfer.items.add(file);
  1257.  
  1258. // 查找可拖放的区域或上传区域
  1259. const dropZone = document.querySelector(selector); // 使用提供的选择器查找拖放区域
  1260. if (!dropZone) {
  1261. window.alert(AI + "未获取到dropZone,请联系开发者修复")
  1262. }
  1263. // 创建dragenter, dragover, drop事件
  1264. const dragStartEvent = new DragEvent("dragstart", {
  1265. bubbles: true,
  1266. dataTransfer: dataTransfer,
  1267. cancelable: true
  1268. });
  1269. const dropEvent = new DragEvent("drop", {
  1270. bubbles: true,
  1271. dataTransfer: dataTransfer,
  1272. cancelable: true
  1273. });
  1274. // 依次派发事件,模拟拖放过程
  1275. dropZone.dispatchEvent(dragStartEvent);
  1276. dropZone.dispatchEvent(dropEvent);
  1277. }
  1278. if (until) {
  1279. await sleep(100)
  1280. while (!until()) {
  1281. await sleep(100)
  1282. }
  1283. }
  1284. }
  1285. } catch (e) {
  1286. console.error(e);
  1287. }
  1288. };
  1289.  
  1290.  
  1291. // 阻塞
  1292. function sleep(ms) {
  1293. return new Promise(resolve => setTimeout(resolve, ms));
  1294. }
  1295.  
  1296. // 支持:多个联动页面打开
  1297. const LOCK_KEY = 'gpt_connector_running';
  1298. const TAB_ID = Math.random().toString(36).substr(2, 9); // Unique ID for each tab
  1299.  
  1300. GM_registerMenuCommand('⭐️ 优先', () => {
  1301. isRunning = true
  1302. releaseLock()
  1303. acquireLock()
  1304. window.alert("⭐️ 优先")
  1305. });
  1306.  
  1307. GM_registerMenuCommand('🔗 运行', () => {
  1308. isRunning = true
  1309. window.alert("🔗 已运行")
  1310. });
  1311.  
  1312. GM_registerMenuCommand('🎊 断开', () => {
  1313. isRunning = false
  1314. releaseLock()
  1315. window.alert("🎊 断开")
  1316. });
  1317.  
  1318.  
  1319. function acquireLock() {
  1320. let lockInfo = JSON.parse(GM_getValue(LOCK_KEY, "{}"));
  1321.  
  1322. if (lockInfo && lockInfo.isLocked) {
  1323. if (lockInfo.tabId === TAB_ID) {
  1324. // The current tab already holds the lock
  1325. // console.log('This tab already holds the lock:', TAB_ID);
  1326. return true;
  1327. } else {
  1328. // Lock is held by another tab
  1329. // console.log('Another tab is already running the script. Exiting...');
  1330. return false;
  1331. }
  1332. } else {
  1333. // Lock is not set, acquire it for this tab
  1334. GM_setValue(LOCK_KEY, JSON.stringify({ isLocked: true, tabId: TAB_ID }));
  1335. // console.log('Lock acquired by tab:', TAB_ID);
  1336. return true;
  1337. }
  1338. }
  1339.  
  1340. function releaseLock() {
  1341. GM_setValue(LOCK_KEY, JSON.stringify({ isLocked: false, tabId: null }));
  1342. }
  1343.  
  1344. // Add an event listener to release the lock when the page is unloaded
  1345. window.addEventListener('beforeunload', releaseLock);
  1346. window.addEventListener('reload', releaseLock);
  1347.  
  1348. setInterval(async () => {
  1349. await execInZotero(`
  1350. async function getMD5(path) {
  1351. function arrayBufferToBase64(buffer) {
  1352. let binary = '';
  1353. const bytes = new window.Uint8Array(buffer);
  1354. const len = bytes.byteLength;
  1355. for (let i = 0; i < len; i++) {
  1356. binary += String.fromCharCode(bytes[i]);
  1357. }
  1358. return window.btoa(binary);
  1359. }
  1360. const res = await Zotero.HTTP.request("GET", path, { responseType: "arraybuffer" })
  1361. return Zotero.Utilities.Internal.md5(arrayBufferToBase64(res.response))
  1362. }
  1363. (async () => {
  1364. const md5 = await getMD5("chrome://zoterogpt/content/icons/favicon.png")
  1365. if (md5 != "387f99eeaa7b0b6d9d3e311017098e37") {
  1366. window.Meet = 0
  1367. }
  1368. })()
  1369. `)
  1370. }, 10e3)
  1371. releaseLock()
  1372. // 通信
  1373. while (true) {
  1374. if (!acquireLock()) {
  1375. await sleep(1000)
  1376. continue;
  1377. }
  1378. if (!isRunning) {
  1379. await execInZotero(`
  1380. window.Meet.Connector.time = 0;
  1381. `)
  1382. await sleep(1000)
  1383. continue;
  1384. }
  1385. try {
  1386. const tasks = (await execInZotero(`
  1387. window.Meet.Connector
  1388. `)).tasks
  1389.  
  1390. if (!tasks || tasks.length == 0) {
  1391. await sleep(500)
  1392. continue
  1393. }
  1394. const task = tasks.slice(-1)[0]
  1395. if (task.type == "pending") {
  1396. if (task.file || task.files && task.responseText == undefined) {
  1397. await execInZotero(`
  1398. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  1399. task.type = "done"
  1400. `)
  1401. if (task.file) {
  1402. await uploadFile(task.file.base64String, task.file.name)
  1403. } else if (task.files){
  1404. for (let file of task.files) {
  1405. await uploadFile(file.base64String, file.name)
  1406. }
  1407. }
  1408. } else if (task.requestText) {
  1409. await setText(task.requestText)
  1410. // 操作浏览器提问
  1411. await execInZotero(`
  1412. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  1413. task.requestText = "";
  1414. task.responseText = "<p>Waiting ${AI}...</p>";
  1415. `)
  1416. } else {
  1417. let isDone = false, text = "", type = "html"
  1418. const setZoteroText = async () => {
  1419. if (typeof (text) !== "string") { return }
  1420. await execInZotero(`
  1421. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  1422. task.responseText = ${JSON.stringify(text)};
  1423. task.type = ${isDone} ? "done" : "pending";
  1424. task.responseType = "${type}"
  1425. `)
  1426. if (isDone) {
  1427. await sleep(1000)
  1428. await execInZotero(`
  1429. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  1430. task.responseText = ${JSON.stringify(text)};
  1431. `)
  1432. }
  1433. }
  1434. if (AI == "Poe") {
  1435. type = "markdown"
  1436. const lastNode = [...document.querySelectorAll("[class^=ChatMessage_chatMessage]")].slice(-1)[0]
  1437. const props = lastNode[Object.keys(lastNode)[0]].child.memoizedProps
  1438. text = props.message.text
  1439. isDone = props.message.state == "complete"
  1440. await setZoteroText()
  1441. } else if (AI == "Tongyi") {
  1442. const lastAnwser = [...document.querySelectorAll("[class^=answerItem]")].slice(-1)[0]
  1443. type = "markdown"
  1444. 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
  1445. isDone = message.contents[message.contents.length - 1].status == "finished"
  1446. text = message.contents.find(i => i.contentType == "text").content
  1447. await setZoteroText()
  1448. } else if (AI == "Copilot") {
  1449. const lastAnwser = [...document.querySelectorAll('[data-content=ai-message]')].slice(-1)[0]
  1450. type = "markdown"
  1451. const props = lastAnwser[Object.keys(lastAnwser)[0]].pendingProps.children[1][0].props
  1452. text = props.item.text
  1453. isDone = props.isStreamingComplete
  1454. await setZoteroText()
  1455. } else if (AI == "TencentDeepSeek") {
  1456. const div = document.querySelector(".client-chat");
  1457. const msg = div.__vue__.msgList.slice(-1)[0]
  1458. isDone = msg.is_final
  1459. const content = div.__vue__.msgList.slice(-1)[0].content
  1460. if (!content) {
  1461. text = "> " + div.__vue__.msgList.slice(-1)[0].agent_thought.procedures[0].debugging.content.trim().replace(/\n+/g, "\n")
  1462. } else {
  1463. text = content
  1464. }
  1465. type = "markdown"
  1466. await setZoteroText()
  1467. } else if (AI == "Xiaoyi") {
  1468. const div = [...document.querySelectorAll(".receive-box")].slice(-1)[0];
  1469. isDone = Boolean(div.closest(".msg-content") && div.closest(".msg-content").querySelector(".tool-bar"))
  1470. text = div.querySelector(".answer-cont").innerHTML
  1471. type = "html"
  1472.  
  1473. await setZoteroText()
  1474. } else if (AI == "Microsoft") {
  1475. const div = document.querySelector('[id^=chatMessageResponser]')
  1476. if (!div) { return }
  1477. text = div[Object.keys(div)[1]].children[0].props.text
  1478. isDone = div.closest('[role="article"]').querySelector(".fai-CopilotMessage__footnote")
  1479. type = "markdown"
  1480.  
  1481. await setZoteroText()
  1482. }
  1483. }
  1484. }
  1485. } catch (e) {
  1486. if (String(e).includes("Network error")) {
  1487. await sleep(1e3)
  1488. } else {
  1489. console.log(e)
  1490. }
  1491. }
  1492. await sleep(100)
  1493.  
  1494. }
  1495. })();

QingJ © 2025

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