Zotero GPT Connector

Zotero GPT Pro, support ChatGPT Gemini Poe Kimi Coze Chatglm

目前为 2024-07-12 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Zotero GPT Connector
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.4
  5. // @description Zotero GPT Pro, support ChatGPT Gemini Poe Kimi Coze Chatglm
  6. // @author Polygon
  7. // @match https://chatgpt.com/*
  8. // @match https://gemini.google.com/app*
  9. // @match https://poe.com/*
  10. // @match https://www.coze.com/*
  11. // @match https://kimi.moonshot.cn/*
  12. // @match https://chatglm.cn/*
  13. // @icon https://cdn.oaistatic.com/_next/static/media/favicon-32x32.be48395e.png
  14. // @grant GM_xmlhttpRequest
  15. // @grant GM_registerMenuCommand
  16. // @grant GM_cookie
  17. // @grant unsafeWindow
  18. // @run-at document-start
  19. // ==/UserScript==
  20.  
  21. (async function () {
  22. 'use strict';
  23. let isRunning = true
  24. let AI = ""
  25. if (location.host == 'chatgpt.com') {
  26. AI = "ChatGPT"
  27. } else if (location.host == 'gemini.google.com') {
  28. AI = "Gemini"
  29. } else if (location.host == 'poe.com') {
  30. AI = "Poe"
  31. } else if (location.host == 'kimi.moonshot.cn') {
  32. AI = "Kimi"
  33. } else if (location.host == 'www.coze.com') {
  34. AI = "Coze"
  35. } else if (location.host == "chatglm.cn") {
  36. AI = "Chatglm"
  37. GM_cookie.list({
  38. url: 'https://chatglm.cn/chatglm/feed-api/assistant_top/v4/recent_list' }, function (cookies, error) {
  39. if (!error) {
  40. localStorage.setItem("token", cookies.find(i => i.name == "chatglm_token").value)
  41. }
  42. });
  43. }
  44. // 在Zotero中执行代码
  45. async function execInZotero(code) {
  46. return new Promise((resolve, reject) => {
  47. GM_xmlhttpRequest({
  48. method: "POST",
  49. url: "http://127.0.0.1:23119/zoterogpt",
  50. headers: {
  51. "Content-Type": "application/json",
  52. },
  53. responseType: "json",
  54. data: JSON.stringify({ code }),
  55. onload: function (response) {
  56. if (response.status >= 200 && response.status < 300) {
  57. resolve(response.response.result);
  58. } else {
  59. reject(new Error(`Request failed with status: ${response.status}`));
  60. }
  61. },
  62. onerror: function (error) {
  63. reject(new Error('Network error'));
  64. }
  65. });
  66. });
  67. }
  68.  
  69. // 设定ChatGPT输入框文本并发送
  70. const setText = (text) => {
  71. const originalText = text
  72. if (AI == "ChatGPT") {
  73. // 获取 input 输入框的dom对象
  74. var element_input = window.document.querySelector('#prompt-textarea');
  75. // 修改input的值
  76. element_input.value = text;
  77. // 设置输入框的 input 事件
  78. var event = new InputEvent('input', {
  79. 'bubbles': true,
  80. 'cancelable': true,
  81. });
  82. element_input.dispatchEvent(event);
  83. const buttons = document.querySelector("#prompt-textarea").parentElement.parentElement.querySelectorAll("button");
  84. const button = buttons[buttons.length - 1]
  85. button.click()
  86. setTimeout(() => {
  87. button.click()
  88. }, 100)
  89. } else if (AI == "Gemini") {
  90. // 获取 input 输入框的dom对象
  91. var element_input = window.document.querySelector('rich-textarea .textarea');
  92. // 修改input的值
  93. element_input.textContent = text;
  94. document.querySelector(".send-button").click();
  95. setTimeout(() => {
  96. document.querySelector(".send-button").click()
  97. }, 100)
  98. } else if (AI == "Poe") {
  99. var element_input = window.document.querySelector('textarea[class*=GrowingTextArea_textArea]');
  100. element_input.value = text;
  101. // 设置输入框的 input 事件
  102. var event = new InputEvent('input', {
  103. 'bubbles': true,
  104. 'cancelable': true,
  105. });
  106. element_input.dispatchEvent(event);
  107. document.querySelector("button[class*=ChatMessageSendButton_sendButton]").click();
  108. setTimeout(() => {
  109. document.querySelector("button[class*=ChatMessageSendButton_sendButton]").click()
  110. }, 100)
  111. } else if (AI == "Kimi") {
  112. fetch(`https://kimi.moonshot.cn/api/chat/${location.href.match(/chat\/([0-9a-z]+)/)[1]}/completion/stream`, {
  113. method: 'POST',
  114. headers: {
  115. 'Content-Type': 'application/json',
  116. "Authorization": `Bearer ${localStorage.access_token}`
  117. },
  118. body: JSON.stringify({ "messages": [{ "role": "user", "content": text }], "refs": [], "use_search": false })
  119. })
  120. .then(response => {
  121. if (response.status == 200) {
  122. return response.body.getReader()
  123. } else {
  124. throw Error("授权失败")
  125. }
  126. })
  127. .then(reader => {
  128. let text = ""
  129. const decoder = new TextDecoder();
  130. window.setTimeout(async () => {
  131. while (true) {
  132. const { done, value } = await reader.read();
  133. if (done) {
  134. await execInZotero(`
  135. let task = window.Meet.tasks[window.Meet.tasks.length-1]
  136. task.responseText = ${JSON.stringify(text)};
  137. task.type = "done";
  138. task.responseType = "markdown"
  139. `)
  140. break
  141. }
  142. try {
  143. const newLines = decoder.decode(value, { stream: true })
  144. for (let line of newLines.match(/data: .+/g)) {
  145. try {
  146. const data = JSON.parse(line.split("data: ")[1])
  147. if (data.event && data.event == "cmpl") {
  148. text += data.text
  149. } else if (data.error) {
  150. text += data.error.message;
  151. console.log(data);
  152. document.querySelector("[data-testid=msh-sidebar-new]").click();
  153. window.setTimeout(() => {
  154. setText(originalText)
  155. }, 1000)
  156. return
  157. }
  158. } catch { }
  159. execInZotero(`
  160. let task = window.Meet.tasks[window.Meet.tasks.length-1]
  161. task.responseText = ${JSON.stringify(text)};
  162. task.type = "pending";
  163. task.responseType = "markdown"
  164. `)
  165. }
  166. } catch (e) {
  167. console.log(e)
  168. }
  169.  
  170. }
  171. }, 0)
  172. })
  173. .catch(e => {
  174. console.log(e)
  175.  
  176. window.setTimeout(async () => {
  177. const res = await fetch("https://kimi.moonshot.cn/api/auth/token/refresh", {
  178. headers: {
  179. "Authorization": `Bearer ${localStorage.refresh_token}`
  180. }
  181. })
  182. const data = await res.json()
  183. localStorage.access_token = data.access_token
  184. localStorage.refresh_token = data.refresh_token
  185. setText(text)
  186. })
  187. })
  188. } else if (AI == "Coze") {
  189. const node = document.querySelector(".b5gKALp6yXERRDn8TV4r")
  190. node[Object.keys(node)[0]].pendingProps.children[0].props.onSendMessage({ text, mentionList: [] })
  191. } else if (AI == "Chatglm") {
  192. fetch(`https://chatglm.cn/chatglm/backend-api/assistant/stream`, {
  193. method: 'POST',
  194. headers: {
  195. 'Content-Type': 'application/json',
  196. "Authorization": `Bearer ${localStorage.token}`
  197. },
  198. body: JSON.stringify({
  199. "assistant_id": "65940acff94777010aa6b796",
  200. "conversation_id": localStorage.conversation_id || "",
  201. "meta_data": {
  202. "mention_conversation_id": "",
  203. "is_test": false,
  204. "input_question_type": "xxxx",
  205. "channel": "",
  206. "draft_id": ""
  207. }, "messages": [
  208. { "role": "user", "content": [{ "type": "text", "text": text }] }]
  209. })
  210. })
  211. .then(response => {
  212. if (response.status == 200) {
  213. return response.body.getReader()
  214. } else {
  215. throw Error("授权失败")
  216. }
  217. })
  218. .then(reader => {
  219. let text = ""
  220. const decoder = new TextDecoder();
  221. window.setTimeout(async () => {
  222. while (true) {
  223. const { done, value } = await reader.read();
  224. if (done) {
  225. await execInZotero(`
  226. let task = window.Meet.tasks[window.Meet.tasks.length-1]
  227. task.responseText = ${JSON.stringify(text)};
  228. task.type = "done";
  229. task.responseType = "markdown"
  230. `)
  231. break
  232. }
  233. try {
  234. const newLines = decoder.decode(value, { stream: true })
  235. for (let line of newLines.match(/event:message\ndata: .+/g)) {
  236. try {
  237. const data = JSON.parse(line.split("data: ")[1])
  238. localStorage.localStorage ??= data.conversation_id
  239. text = data.parts[0].content[0].text
  240. } catch { }
  241. execInZotero(`
  242. let task = window.Meet.tasks[window.Meet.tasks.length-1]
  243. task.responseText = ${JSON.stringify(text)};
  244. task.type = "pending";
  245. task.responseType = "markdown"
  246. `)
  247. }
  248. } catch (e) {
  249. console.log(e)
  250. }
  251.  
  252. }
  253. }, 0)
  254. })
  255. .catch(e => {
  256. console.log(e)
  257. localStorage.conversation_id = ""
  258. location.reload()
  259. })
  260. }
  261. }
  262.  
  263. // 阻塞
  264. function sleep(ms) {
  265. return new Promise(resolve => setTimeout(resolve, ms));
  266. }
  267.  
  268. GM_registerMenuCommand('🔗 运行', () => {
  269. isRunning = true
  270. window.alert("🔗 已运行")
  271. });
  272. GM_registerMenuCommand('🎊 断开', () => {
  273. isRunning = false
  274. window.alert("🎊 断开")
  275. });
  276.  
  277.  
  278. // 通信
  279. await sleep(3000)
  280. while (true) {
  281. if (!isRunning) {
  282. await execInZotero(`
  283. window.Meet.tasks = undefined
  284. `)
  285. await sleep(1000)
  286. continue;
  287. }
  288. try {
  289. const result = await execInZotero(`
  290. if (!window.Meet.tasks){
  291. window.Meet.tasks = []
  292. } else {
  293. window.Meet.tasks
  294. }
  295. `)
  296. const tasks = result;
  297. if (!tasks || tasks.length == 0) {
  298. await sleep(500)
  299. continue
  300. }
  301. const task = tasks.slice(-1)[0]
  302. if (task.type == "pending") {
  303. if (task.requestText) {
  304. // 操作浏览器提问
  305. if (AI == "ChatGPT") {
  306. let getUserQuestionNum = () => document.querySelectorAll("[data-message-author-role=user]").length
  307. const questionNum = getUserQuestionNum()
  308. setText(task.requestText)
  309. while (getUserQuestionNum() == questionNum) {
  310. await sleep(100)
  311. }
  312. } else if (AI == "Gemini") {
  313. let getUserQuestionNum = () => document.querySelectorAll("user-query").length
  314. const questionNum = getUserQuestionNum()
  315. setText(task.requestText)
  316. while (getUserQuestionNum() == questionNum) {
  317. await sleep(100)
  318. }
  319. while (document.querySelectorAll('model-response').length != getUserQuestionNum()) {
  320. await sleep(100)
  321.  
  322. }
  323. } else if (AI == "Poe") {
  324. let getUserQuestionNum = () => document.querySelectorAll("[class*=ChatMessage_humanMessageWrapper]").length
  325. const questionNum = getUserQuestionNum()
  326. setText(task.requestText)
  327. while (getUserQuestionNum() == questionNum) {
  328. await sleep(100)
  329. }
  330. while (document.querySelectorAll('[class*=Message_botMessageBubble]').length != getUserQuestionNum()) {
  331. await sleep(100)
  332. }
  333. } else if (AI == "Kimi") {
  334. setText(task.requestText)
  335. } else if (AI == "Coze") {
  336. setText(task.requestText)
  337. } else if (AI == "Chatglm") {
  338. setText(task.requestText)
  339. }
  340. await execInZotero(`
  341. let task = window.Meet.tasks[window.Meet.tasks.length-1]
  342. task.requestText = "";
  343. task.responseText = "<p>Answering (本过程不消耗Api Key额度)...</p>";
  344. `)
  345. } else {
  346. let isDone = false, text = "", type = "html"
  347. const setZoteroText = async () => {
  348. await execInZotero(`
  349. let task = window.Meet.tasks[window.Meet.tasks.length-1]
  350. task.responseText = ${JSON.stringify(text)};
  351. task.type = ${isDone} ? "done" : "pending";
  352. task.responseType = "${type}"
  353. `)
  354. if (isDone) {
  355. await sleep(1000)
  356. await execInZotero(`
  357. let task = window.Meet.tasks[window.Meet.tasks.length-1]
  358. task.responseText = ${JSON.stringify(text)};
  359. `)
  360. }
  361. }
  362. if (AI == "ChatGPT") {
  363. const outputEle = [...document.querySelectorAll('[data-testid^=conversation-turn]')].slice(-1)[0];
  364. // const contentEle = outputEle.querySelector("div>div>div:nth-child(2)>div:nth-child(2)>div")
  365. isDone = Boolean(outputEle.querySelector("span[data-state=closed]"))
  366. text = outputEle.querySelector(".markdown").innerHTML
  367. await setZoteroText()
  368. } else if (AI == "Gemini") {
  369. const outputEle = [...document.querySelectorAll('model-response')].slice(-1)[0];
  370. const contentEle = outputEle.querySelector(".response-content message-content")
  371. isDone = Boolean(outputEle.querySelector(".complete"))
  372. text = contentEle.querySelector(".markdown").innerHTML
  373. await setZoteroText()
  374. } else if (AI == "Poe") {
  375. const outputEle = [...document.querySelectorAll('[class*=Message_botMessageBubble]')].slice(-1)[0];
  376. const contentEle = outputEle.querySelector("[class*=Markdown_markdownContainer]")
  377. isDone = Boolean(document.querySelector("[class*=ChatMessageActionBar_actionBar]"))
  378. text = contentEle.innerHTML
  379. await setZoteroText()
  380. } else if (AI == "Kimi") {
  381. // 无需这一步
  382. } else if (AI == "Coze") {
  383. const outputEle = document.querySelector(".message-group-wrapper");
  384. const contentEle = outputEle.querySelector("[data-testid='bot.ide.chat_area.message_box'] .flow-markdown-body")
  385. isDone = Boolean(outputEle.querySelector(".chat-uikit-message-box-container__message__message-box__footer").childNodes.length != 0)
  386. text = contentEle.innerHTML.replace(/<br .+?>/g, "").replace(/<hr .+?>/g, "<hr/>")
  387. await setZoteroText()
  388. } else if (AI == "Kimi") {
  389. // 无需这一步
  390. }
  391. }
  392. }
  393. } catch (e) {
  394. console.log(e)
  395. }
  396. await sleep(100)
  397. }
  398. })();

QingJ © 2025

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