Zotero GPT Connector

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

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

QingJ © 2025

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