Zotero GPT Connector

Zotero GPT Pro, support ChatGPT Gemini Poe Kimi

当前为 2024-04-01 提交的版本,查看 最新版本

// ==UserScript==
// @name         Zotero GPT Connector
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  Zotero GPT Pro, support ChatGPT Gemini Poe Kimi
// @author       Polygon
// @match        https://chat.openai.com/*
// @match        https://gemini.google.com/app/*
// @match        https://poe.com/*
// @match        https://kimi.moonshot.cn/*
// @icon         https://cdn.oaistatic.com/_next/static/media/favicon-32x32.be48395e.png
// @grant        GM_xmlhttpRequest
// @grant        GM_registerMenuCommand
// @grant        unsafeWindow
// @run-at       document-start
// ==/UserScript==

(async function () {
  'use strict';
  let AI = ""
  if (location.host == 'chat.openai.com') {
    AI = "ChatGPT"
  } else if (location.host == 'gemini.google.com') {
    AI = "Gemini"
  } else if (location.host == 'poe.com') {
    AI = "Poe"
  } else if (location.host == 'kimi.moonshot.cn') {
    AI = "Kimi"
  }
  // 在Zotero中执行代码
  async function execInZotero(code) {
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        method: "POST",
        url: "http://127.0.0.1:23119/zoterogpt",
        headers: {
          "Content-Type": "application/json",
        },
        responseType: "json",
        data: JSON.stringify({ code }),
        onload: function (response) {
          if (response.status >= 200 && response.status < 300) {
            resolve(response.response.result);
          } else {
            reject(new Error(`Request failed with status: ${response.status}`));
          }
        },
        onerror: function (error) {
          reject(new Error('Network error'));
        }
      });
    });
  }

  // 设定ChatGPT输入框文本并发送
  const setText = (text) => {
    if (AI == "ChatGPT") {
      // 获取 input 输入框的dom对象
      var element_input = window.document.querySelector('#prompt-textarea');
      // 修改input的值
      element_input.value = text;
      // 设置输入框的 input 事件
      var event = new InputEvent('input', {
        'bubbles': true,
        'cancelable': true,
      });
      element_input.dispatchEvent(event);
      document.querySelector("[data-testid=send-button]").click()
      setTimeout(() => {
        document.querySelector("[data-testid=send-button]").click()
      }, 100)
    } else if (AI == "Gemini") {
      // 获取 input 输入框的dom对象
      var element_input = window.document.querySelector('rich-textarea .textarea');
      // 修改input的值
      element_input.textContent = text;
      document.querySelector(".send-button").click();
      setTimeout(() => {
        document.querySelector(".send-button").click()
      }, 100)
    } else if (AI == "Poe") {
      var element_input = window.document.querySelector('textarea[class*=GrowingTextArea_textArea]');
      element_input.value = text;
      // 设置输入框的 input 事件
      var event = new InputEvent('input', {
        'bubbles': true,
        'cancelable': true,
      });
      element_input.dispatchEvent(event);
      document.querySelector("button[class*=ChatMessageSendButton_sendButton]").click();
      setTimeout(() => {
        document.querySelector("button[class*=ChatMessageSendButton_sendButton]").click()
      }, 100)
    } else if (AI == "Kimi") {
      fetch(`https://kimi.moonshot.cn/api/chat/${location.href.match(/chat\/([0-9a-z]+)/)[1]}/completion/stream`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          "Authorization": `Bearer ${localStorage.access_token}`
        },
        body: JSON.stringify({ "messages": [{ "role": "user", "content": text }], "refs": [], "use_search": false })
      })
        .then(response => {
          let text = ""
          const reader = response.body.getReader();
          const decoder = new TextDecoder();

          return reader.read().then(function processText({ done, value }) {
            if (done) {
              window.toZotero.isDone = true
              return;
            }

            text += JSON.parse(decoder.decode(value, { stream: true }).split("data: ")[1]).text || ""
            window.toZotero = {
              isDone: false,
              text: text.replace("```markdown", "```\n")
            }

            return reader.read().then(processText);
          });

        })
        .catch(e => {
          window.toZotero = {
            isDone: false,
            text: "请等待"
          }
          location.reload();
          sleep(3000)
          setText(text)
        })

    }
  }

  // 阻塞
  function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
  let isRunning = true
  GM_registerMenuCommand('🔗 运行', () => {
    isRunning = true
    window.alert("🔗 已运行")
  });
  GM_registerMenuCommand('🎊 断开', () => {
    isRunning = false
    window.alert("🎊 断开")
  });

  // Ctrl+Enter 发送到Zotero GPT执行
  // const callback = async (e) => {
  //   e.stopPropagation();
  //   e.preventDefault();
  //   const text = document.querySelector('#prompt-textarea').value
  //   document.querySelector('#prompt-textarea').innerHTML = "Thinking..."
  //   document.querySelector('#prompt-textarea').value = "Thinking..."
  //   console.log("接收文本", JSON.stringify(text))
  //   await execInZotero(`
  //       const views = Zotero.ZoteroGPT.views;
  //       views.inputContainer.querySelector("input").value = ${JSON.stringify(text)}
  //       window.setTimeout(async()=>{
  //         await Zotero.ZoteroGPT.views.execText(${JSON.stringify(text)
  //       })
  //       })
  //     `)
  // }
  // window.setInterval(() => {
  //   const isListener = document.querySelector('#prompt-textarea').getAttribute("is-listener")
  //   if (!isListener) {
  //     document.querySelector('#prompt-textarea')
  //       .addEventListener("keydown", async (e) => {
  //         if (!(e.ctrlKey && e.key == "Enter" && isRunning)) { return }
  //         callback(e)
  //       })
  //     document.querySelector('#prompt-textarea').setAttribute("is-listener", "1")
  //   }
  // }, 1000)


  // 通信
  while (true) {
    if (!isRunning) {
      await execInZotero(`
        window.Meet.tasks = undefined
      `)
      await sleep(1000)
      continue;
    }
    try {
      const result = await execInZotero(`
        if (!window.Meet.tasks){
          window.Meet.tasks = []
        } else {
          window.Meet.tasks
        }
      `)
      const tasks = result;
      if (!tasks || tasks.length == 0) {
        await sleep(500)
        continue
      }
      const task = tasks.slice(-1)[0]
      if (task.type == "pending") {
        if (task.requestText) {
          // 操作浏览器提问
          if (AI == "ChatGPT") {
            let getUserQuestionNum = () => document.querySelectorAll("[data-message-author-role=user]").length
            const questionNum = getUserQuestionNum()
            setText(task.requestText)
            while (getUserQuestionNum() == questionNum) {
              await sleep(100)
            }
          } else if (AI == "Gemini") {
            let getUserQuestionNum = () => document.querySelectorAll("user-query").length
            const questionNum = getUserQuestionNum()
            setText(task.requestText)
            while (getUserQuestionNum() == questionNum) {
              await sleep(100)
            }
            while (document.querySelectorAll('model-response').length != getUserQuestionNum()) {
              await sleep(100)

            }
          } else if (AI == "Poe") {
            let getUserQuestionNum = () => document.querySelectorAll("[class*=ChatMessage_humanMessageWrapper]").length
            const questionNum = getUserQuestionNum()
            setText(task.requestText)
            while (getUserQuestionNum() == questionNum) {
              await sleep(100)
            }
            while (document.querySelectorAll('[class*=Message_botMessageBubble]').length != getUserQuestionNum()) {
              await sleep(100)
            }
          } else if (AI == "Kimi") {
            setText(task.requestText)
          }
          await execInZotero(`
            let task = window.Meet.tasks[window.Meet.tasks.length-1]
            task.requestText = "";
            task.responseText = "<p>Answering (本过程不消耗Api Key额度)...</p>";
          `)
        } else {
          let isDone = false, text = "", type = "html"
          if (AI == "ChatGPT") {
            const outputEle = [...document.querySelectorAll('[data-testid^=conversation-turn]')].slice(-1)[0];
            const contentEle = outputEle.querySelector("div>div>div:nth-child(2)>div:nth-child(2)>div")
            isDone = Boolean(outputEle.querySelector("div>div>div:nth-child(2)>div:nth-child(2)>div:nth-child(2) button"))
            text = contentEle.querySelector(".markdown").innerHTML
          } else if (AI == "Gemini") {
            const outputEle = [...document.querySelectorAll('model-response')].slice(-1)[0];
            const contentEle = outputEle.querySelector(".response-content message-content")
            isDone = Boolean(outputEle.querySelector(".complete"))
            text = contentEle.querySelector(".markdown").innerHTML
          } else if (AI == "Poe") {
            const outputEle = [...document.querySelectorAll('[class*=Message_botMessageBubble]')].slice(-1)[0];
            const contentEle = outputEle.querySelector("[class*=Markdown_markdownContainer]")
            isDone = Boolean(document.querySelector("[class*=ChatMessageActionBar_actionBar]"))
            text = contentEle.innerHTML
          } else if (AI == "Kimi") {
            isDone = window.toZotero && window.toZotero.isDone
            text = window.toZotero && window.toZotero.text
            type = "markdown"
          }
          console.log(text)
          await execInZotero(`
            let task = window.Meet.tasks[window.Meet.tasks.length-1]
            task.responseText = ${JSON.stringify(text)};
            task.type = ${isDone} ? "done" : "pending";
            task.responseType = "${type}"
          `)
          if (isDone) {
            await sleep(1000)
            await execInZotero(`
            let task = window.Meet.tasks[window.Meet.tasks.length-1]
            task.responseText = ${JSON.stringify(text)};
          `)
          }
        }
      }
    } catch (e) {
      console.log(e)
    }
    await sleep(10)
  }
})();

QingJ © 2025

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