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-03-29 提交的版本。查看 最新版本

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

QingJ © 2025

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