Zotero GPT Connector

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

目前为 2024-10-14 提交的版本。查看 最新版本

  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
  4. // @namespace http://tampermonkey.net/
  5. // @icon https://github.com/MuiseDestiny/zotero-gpt/blob/bootstrap/addon/chrome/content/icons/favicon.png?raw=true
  6. // @version 3.1.4
  7. // @author Polygon
  8. // @match https://chatgpt.com/*
  9. // @match https://gemini.google.com/app*
  10. // @match https://poe.com/*
  11. // @match https://www.coze.com/*
  12. // @match https://kimi.moonshot.cn/*
  13. // @match https://chatglm.cn/*
  14. // @match https://chatglm.cn/*
  15. // @match https://yiyan.baidu.com/*
  16. // @match https://tongyi.aliyun.com/*
  17. // @match https://qianwen.aliyun.com/*
  18. // @match https://claude.ai/*
  19. // @match https://mytan.maiseed.com.cn/*
  20. // @match https://mychandler.bet/*
  21. // @match https://chat.deepseek.com/*
  22. // @match https://www.doubao.com/chat/*
  23. // @match https://*.chatshare.biz/*
  24. // @match https://chat.kelaode.ai/*
  25. // @match https://chat.rawchat.cn/*
  26. // @match https://chat.sharedchat.top/*
  27. // @match https://node.dawuai.buzz/*
  28. // @match https://aistudio.google.com/*
  29. // @connect *
  30. // @grant GM_xmlhttpRequest
  31. // @grant GM_registerMenuCommand
  32. // @grant GM_cookie
  33. // @grant unsafeWindow
  34. // @run-at document-start
  35. // ==/UserScript==
  36.  
  37. (async function () {
  38. 'use strict';
  39. let isRunning = true
  40. let AI = "ChatGPT"
  41. const host = location.host
  42. if (host == 'chatgpt.com') {
  43. AI = "ChatGPT"
  44. } else if (host == 'gemini.google.com') {
  45. AI = "Gemini"
  46. } else if (host == 'poe.com') {
  47. AI = "Poe"
  48. } else if (host == 'kimi.moonshot.cn') {
  49. AI = "Kimi"
  50. } else if (host == 'www.coze.com') {
  51. AI = "Coze"
  52. } else if (host == "chatglm.cn") {
  53. localStorage.conversation_id = ""
  54. AI = "Chatglm"
  55. } else if (host == 'yiyan.baidu.com') {
  56. AI = "Yiyan"
  57. } else if (host == 'tongyi.aliyun.com' || host == 'qianwen.aliyun.com') {
  58. AI = "Tongyi"
  59. } else if (host == "claude.ai" || host == 'chat.kelaode.ai') {
  60. AI = "Claude"
  61. } else if (host == 'mytan.maiseed.com.cn') {
  62. AI = "MyTan"
  63. } else if (host == 'mychandler.bet') {
  64. localStorage.conversation_id = ""
  65. AI = "ChanlderAi"
  66. } else if (host == 'chat.deepseek.com') {
  67. AI = "DeepSeek"
  68. } else if (host == "www.doubao.com") {
  69. AI = "Doubao"
  70. } else if (host == 'aistudio.google.com') {
  71. AI = "AIStudio"
  72. }
  73. // 在Zotero中执行代码
  74. async function execInZotero(code) {
  75. try {
  76. return new Promise((resolve, reject) => {
  77. GM_xmlhttpRequest({
  78. method: "POST",
  79. url: "http://127.0.0.1:23119/zoterogpt",
  80. headers: {
  81. "Content-Type": "application/json",
  82. },
  83. responseType: "json",
  84. data: JSON.stringify({ code }),
  85. onload: function (response) {
  86. if (response.status >= 200 && response.status < 300) {
  87. resolve(response.response.result);
  88. } else {
  89. reject(new Error(`Request failed with status: ${response.status}`));
  90. }
  91. },
  92. onerror: function (error) {
  93. reject(new Error('Network error'));
  94. }
  95. });
  96. });
  97. } catch (e) {
  98. window.alert("execInZotero error: " + code);
  99. return ""
  100. }
  101. }
  102.  
  103. // 设定ChatGPT输入框文本并发送
  104. const setText = async (text) => {
  105. const dispatchInput = (selector) => {
  106. // 获取 input 输入框的dom对象
  107. var inputNode = document.querySelector(selector);
  108. // 修改input的值
  109. inputNode.value = text;
  110. // plus
  111. inputNode.innerText = text;
  112. // 设置输入框的 input 事件
  113. var event = new InputEvent('input', {
  114. 'bubbles': true,
  115. 'cancelable': true,
  116. });
  117. inputNode.dispatchEvent(event);
  118. }
  119. const originalText = text
  120. if (AI == "ChatGPT") {
  121. dispatchInput('#prompt-textarea')
  122. await sleep(100)
  123. await send("article", () => {
  124. const button = document.querySelector('[data-testid="send-button"]');
  125. button.click()
  126. })
  127. } else if (AI == "Gemini") {
  128. // 获取 input 输入框的dom对象
  129. const element_input = window.document.querySelector('rich-textarea .textarea');
  130. // 修改input的值
  131. element_input.textContent = text;
  132. document.querySelector(".send-button").click();
  133. setTimeout(() => {
  134. document.querySelector(".send-button").click()
  135. }, 100)
  136. } else if (AI == "Poe") {
  137. dispatchInput('textarea[class*=GrowingTextArea_textArea]')
  138. document.querySelector("button[class*=ChatMessageSendButton_sendButton]").click();
  139. setTimeout(() => {
  140. document.querySelector("button[class*=ChatMessageSendButton_sendButton]").click()
  141. }, 100)
  142. } else if (AI == "Kimi") {
  143. const node = document.querySelector("[class^=inputInner]")
  144. await node[Object.keys(node)[1]].children[1][1].ref.current.insertText(text)
  145.  
  146. await send("[class^=segmentItem]", () => {
  147. const button = document.querySelector('[data-testid=msh-chatinput-send-button]');
  148. button.click()
  149. })
  150.  
  151. } else if (AI == "Coze") {
  152. const node = document.querySelector(".b5gKALp6yXERRDn8TV4r")
  153. node[Object.keys(node)[0]].pendingProps.children[0].props.onSendMessage({ text, mentionList: [] })
  154. } else if (AI == "Chatglm") {
  155. const token = document.cookie.match(/chatglm_token=([^;]+);/)[1];
  156. requestStream({
  157. api: `https://chatglm.cn/chatglm/backend-api/assistant/stream`,
  158. token,
  159. data: {
  160. "assistant_id": "65940acff94777010aa6b796",
  161. "conversation_id": localStorage.conversation_id || "",
  162. "meta_data": {
  163. "mention_conversation_id": "",
  164. "is_test": false,
  165. "input_question_type": "xxxx",
  166. "channel": "",
  167. "draft_id": ""
  168. }, "messages": [
  169. { "role": "user", "content": [{ "type": "text", "text": text }] }]
  170. },
  171. lineRegex: /event:message\ndata: .+/g,
  172. getContent: (data) => data.parts[0].content[0].type == "text" ? data.parts[0].content[0].text : "",
  173. midFunction: (data) => {
  174. if (!localStorage.conversation_id || localStorage.conversation_id.length == 0) {
  175. localStorage.conversation_id = data.conversation_id
  176. }
  177. },
  178. isNotDelta: true
  179. })
  180. } else if (AI == "Yiyan") {
  181. const node = document.querySelector("#eb_model_footer")
  182. node[Object.keys(node)[1]].children[3].props.children[2].props.children[0].props.setText(text);
  183. document.querySelector(".VAtmtpqL").click()
  184. } else if (AI == "Tongyi") {
  185. const node = document.querySelector(".chatInput--eJzBH8LP")
  186. await node[Object.keys(node)[1]].children[0].props.setText(text);
  187.  
  188. await send("[class^=questionItem]", () => {
  189. const node2 = document.querySelector(".operateBtn--zFx6rSR0");
  190. node2[Object.keys(node2)[1]].onClick()
  191. })
  192. } else if (AI == "Claude") {
  193. const node = document.querySelector("fieldset")
  194. const props = node[Object.keys(node)[1]].children[0].props.children[0].props.children[0].props;
  195. props.setInput(text);
  196. document.querySelector("button[aria-label='Send Message']").click();
  197. } else if (AI == "MyTan") {
  198. const conversation_id = location.href.split("chat/")?.[1]
  199. const data = {
  200. "content": [
  201. { "type": "text", "text": text }
  202. ],
  203. "stream": true,
  204. }
  205. if (conversation_id) {
  206. data.conversation_id = conversation_id
  207. } else {
  208. data.conversation = { title: "新对话", model: JSON.parse(localStorage["chosen-model-obj"]).model }
  209. }
  210. requestStream({
  211. api: `https://mytan.maiseed.com.cn/api/v2/messages`,
  212. token: JSON.parse(localStorage["chat-tan-token"]).token,
  213. data,
  214. lineRegex: /data: .+/g,
  215. getContent: (data) => data.choices[0].delta.content,
  216. })
  217. } else if (AI == "ChanlderAi") {
  218. // 更新id
  219. if (!localStorage.conversation_id || localStorage.conversation_id == "") {
  220. async function readStream(stream) {
  221. const reader = stream.getReader();
  222. let result = '';
  223. const decoder = new TextDecoder();
  224. while (true) {
  225. const { done, value } = await reader.read();
  226. if (done) break;
  227.  
  228. result += decoder.decode(value, { stream: true });
  229. }
  230. return result
  231. }
  232. const res = await fetch("https://api.chandler.bet/api/chat/chatHistory", {
  233. method: 'POST',
  234. headers: {
  235. 'Content-Type': 'application/json',
  236. "Authorization": `Bearer ${localStorage.token}`,
  237. "accept": "application/json, text/plain, */*"
  238. },
  239. body: JSON.stringify({ "keywords": "", "model_names": [], "page_size": 10, "page_num": 1 }),
  240. })
  241. const data = JSON.parse(await readStream(res.body)).data[0]
  242. localStorage.conversation_id = data.conversation_id
  243. localStorage.model_name = data.model_name
  244. localStorage.parent_message_id = data.parent_message_id
  245. }
  246. const appData = JSON.parse(localStorage.appLocalStorage)
  247. requestStream({
  248. api: `https://api.chandler.bet/api/chat/Chat`,
  249. token: localStorage.token,
  250. data: {
  251. "uid": appData.email,
  252. "prompt": text,
  253. "model_name": localStorage.model_name,
  254. "request_timeout": 30,
  255. "global_timeout": 100,
  256. "max_retries": 1,
  257. "attachment_list": [],
  258. "parent_message_id": localStorage.parent_message_id,
  259. "conversation_id": localStorage.conversation_id,
  260. "answer_again": false,
  261. "aireply": "",
  262. "timestamp": (new Date()).getTime(),
  263. "status": "",
  264. "app_name": "",
  265. "web_url": "https://api.chandler.bet"
  266. },
  267. lineRegex: /event:message\ndata:.+/g,
  268. getContent: (data) => data.delta
  269. })
  270. } else if (AI == "DeepSeek") {
  271. requestStream({
  272. api: `https://chat.deepseek.com/api/v0/chat/completions`,
  273. token: JSON.parse(localStorage.userToken).value,
  274. data: {
  275. "message": text,
  276. "stream": true,
  277. "model_preference": null,
  278. "model_class":
  279. "deepseek_chat",
  280. "temperature": 0
  281. },
  282. lineRegex: /data: .+/g,
  283. getContent: (data) => data.choices[0].delta.content || ""
  284. })
  285. } else if (AI == "Doubao") {
  286. const node = document.querySelector("[class^=footer]")
  287. await node[Object.keys(node)[1]].children.ref.current.autoTransValue(text);
  288. await sleep(1e3)
  289. document.querySelector("button#flow-end-msg-send").click();
  290. } else if (AI == "AIStudio") {
  291. // 获取 input 输入框的dom对象
  292. const element_input = document.querySelector(".input-wrapper textarea")
  293. // 修改input的值
  294. element_input.value = text;
  295. // plus
  296. element_input.innerText = text;
  297. await sleep(100)
  298. // 设置输入框的 input 事件
  299. const event = new InputEvent('input', {
  300. 'bubbles': true,
  301. 'cancelable': true,
  302. });
  303. element_input.dispatchEvent(event);
  304. await sleep(100)
  305. await send("ms-chat-turn", () => {
  306. const button = document.querySelector('run-button button');
  307. button.click()
  308. })
  309. await sleep(1000)
  310. }
  311. }
  312. // 连续发送
  313. const send = async (selector, callback) => {
  314. const oldNumber = document.querySelectorAll(selector).length;
  315. callback();
  316. while (document.querySelectorAll(selector).length == oldNumber) {
  317. callback();
  318. await sleep(100);
  319. }
  320. }
  321.  
  322. const uploadFile = async (base64String, fileName) => {
  323. try {
  324. let type
  325. if (fileName.endsWith("pdf")) {
  326. type = "application/pdf"
  327. } else if (fileName.endsWith("png")) {
  328. type = "image/png"
  329. }
  330. function base64ToArrayBuffer(base64) {
  331. const binaryString = atob(base64);
  332. const len = binaryString.length;
  333. const bytes = new Uint8Array(len);
  334. for (let i = 0; i < len; i++) {
  335. bytes[i] = binaryString.charCodeAt(i);
  336. }
  337. return bytes.buffer;
  338. }
  339. if (AI == "AIStudio") {
  340. const button = document.querySelector("ms-add-chunk-menu button")
  341. button && button.click()
  342. }
  343. // 创建一个虚拟的 PDF 文件对象
  344. const fileContent = base64ToArrayBuffer(base64String);
  345. const file = new File([fileContent], fileName, { type });
  346.  
  347. // 创建一个DataTransfer对象
  348. const dataTransfer = new DataTransfer();
  349. dataTransfer.items.add(file);
  350. const fileInput = document.querySelector("input[type=file]")
  351.  
  352. fileInput.files = dataTransfer.files
  353. fileInput.dispatchEvent(new Event('change', { bubbles: true }));
  354. // 需要特殊处理的
  355. if (AI == "AIStudio") {
  356. const button = document.querySelector("ms-add-chunk-menu button")
  357. button && button.click()
  358. }
  359. } catch (e) {
  360. // window.alert(e)
  361. }
  362. }
  363.  
  364. /**
  365. * {api, token, data, lineRegex, getContent, errorFunction, midFunction}
  366. * @param {*} data
  367. */
  368. const requestStream = async (params) => {
  369. fetch(params.api, {
  370. method: 'POST',
  371. headers: {
  372. 'Content-Type': 'application/json',
  373. 'Authorization': `Bearer ${params.token}`
  374. },
  375. body: JSON.stringify(params.data)
  376. })
  377. .then(response => {
  378. if (response.status == 200) {
  379. return response.body.getReader()
  380. } else if (response.status == 400) {
  381. throw new Error('频率过高');
  382. } {
  383. throw new Error('授权失败');
  384. }
  385. })
  386. .then(reader => {
  387. let text = ""
  388. const decoder = new TextDecoder();
  389. window.setTimeout(async () => {
  390. while (true) {
  391. const { done, value } = await reader.read();
  392. if (done) {
  393. await execInZotero(`
  394. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  395. task.responseText = ${JSON.stringify(text)};
  396. task.type = "done";
  397. task.responseType = "markdown"
  398. `)
  399. break
  400. }
  401. try {
  402. const newLines = decoder.decode(value, { stream: true })
  403. for (let line of newLines.match(params.lineRegex)) {
  404. try {
  405. const data = JSON.parse(line.split("data:")[1].trim())
  406. params.midFunction && params.midFunction(data)
  407. text = params.isNotDelta ? params.getContent(data) : (text + params.getContent(data));
  408. } catch (e) {
  409. if (String(e).includes("Stop")) { return }
  410. }
  411. execInZotero(`
  412. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  413. task.responseText = ${JSON.stringify(text)};
  414. task.type = "pending";
  415. task.responseType = "markdown"
  416. `)
  417. }
  418. } catch (e) {
  419. console.log(e)
  420. }
  421. }
  422. }, 0)
  423. })
  424. .catch(e => {
  425. params.errorFunction && params.errorFunction()
  426. })
  427. }
  428. // 阻塞
  429. function sleep(ms) {
  430. return new Promise(resolve => setTimeout(resolve, ms));
  431. }
  432.  
  433. GM_registerMenuCommand('🔗 运行', () => {
  434. isRunning = true
  435. window.alert("🔗 已运行")
  436. });
  437. GM_registerMenuCommand('🎊 断开', () => {
  438. isRunning = false
  439. window.alert("🎊 断开")
  440. });
  441.  
  442. const waitSend = async (selector) => {
  443. let getUserQuestionNum = () => document.querySelectorAll(selector).length
  444. const questionNum = getUserQuestionNum()
  445. while (getUserQuestionNum() == questionNum) {
  446. await sleep(100)
  447. }
  448. }
  449.  
  450. // 通信
  451. await sleep(3000)
  452. while (true) {
  453. if (!isRunning) {
  454. await execInZotero(`
  455. window.Meet.Connector.time = 0;
  456. `)
  457. await sleep(1000)
  458. continue;
  459. }
  460. try {
  461. const tasks = (await execInZotero(`
  462. if (!window.Meet.Connector){
  463. window.Meet.Connector = ${JSON.stringify({
  464. AI, time: Date.now() / 1e3, tasks: []
  465. })};
  466. } else {
  467. window.Meet.Connector.time = ${Date.now() / 1e3};
  468. window.Meet.Connector.AI = "${AI}";
  469. }
  470. window.Meet.Connector
  471. `)).tasks
  472. if (!tasks || tasks.length == 0) {
  473. await sleep(500)
  474. continue
  475. }
  476. const task = tasks.slice(-1)[0]
  477. if (task.type == "pending") {
  478. if (task.file) {
  479. // document.querySelector("[data-testid='create-new-chat-button']").click();
  480. // await sleep(1e3)
  481. await uploadFile(task.file.base64String, task.file.name)
  482. await execInZotero(`
  483. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  484. task.type = "done"
  485. `)
  486. } else if (task.requestText) {
  487. await setText(task.requestText)
  488. // 操作浏览器提问
  489. if (AI == "Gemini") {
  490. // await waitSend("user-query")
  491. while (document.querySelectorAll('model-response').length != getUserQuestionNum()) {
  492. await sleep(100)
  493. }
  494. }
  495. await execInZotero(`
  496. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  497. task.requestText = "";
  498. task.responseText = "<p>Waiting ${AI}...</p>";
  499. `)
  500. } else {
  501. let isDone = false, text = "", type = "html"
  502. const setZoteroText = async () => {
  503. if (typeof (text) !== "string") { return }
  504. await execInZotero(`
  505. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  506. task.responseText = ${JSON.stringify(text)};
  507. task.type = ${isDone} ? "done" : "pending";
  508. task.responseType = "${type}"
  509. `)
  510. if (isDone) {
  511. await sleep(1000)
  512. await execInZotero(`
  513. let task = window.Meet.Connector.tasks[window.Meet.Connector.tasks.length-1]
  514. task.responseText = ${JSON.stringify(text)};
  515. `)
  516. }
  517. }
  518. if (AI == "ChatGPT") {
  519. type = "markdown"
  520. const outputEle = [...document.querySelectorAll('[data-testid^=conversation-turn]')].slice(-1)[0];
  521. const node = outputEle.querySelector("[data-message-author-role=assistant]")
  522. const message = node[Object.keys(node)[0]].alternate.alternate.pendingProps.children[2].props.messages[0].message
  523. isDone = message.status == "finished_successfully"
  524. text = message.content.parts[0];
  525. await setZoteroText()
  526. } else if (AI == "Gemini") {
  527. const outputEle = [...document.querySelectorAll('model-response')].slice(-1)[0];
  528. const contentEle = outputEle.querySelector(".response-content message-content")
  529. isDone = Boolean(outputEle.querySelector(".complete"))
  530. text = contentEle.querySelector(".markdown").innerHTML
  531. await setZoteroText()
  532. } else if (AI == "Poe") {
  533. type = "markdown"
  534. const lastNode = [...document.querySelectorAll("[class^=ChatMessagesView_messagePair]")].slice(-1)[0]
  535. const props = lastNode[Object.keys(lastNode)[0]].child.memoizedProps
  536. text = props.pairedMessage.text
  537. isDone = props.pairedMessage.state == "complete"
  538. await setZoteroText()
  539. } else if (AI == "Kimi") {
  540. type = "markdown"
  541. const lastNode = [...document.querySelectorAll(".kimi-default-segment-component")].slice(-1)[0]
  542. const data = lastNode[Object.keys(lastNode)[0]].child.pendingProps.data
  543. const sections = data.contents.zones[0].sections
  544. text = sections.find(i=>i.cmpl).cmpl
  545. isDone = Boolean(data.status)
  546. await setZoteroText()
  547. } else if (AI == "Coze") {
  548. const outputEle = document.querySelector(".message-group-wrapper");
  549. const contentEle = outputEle.querySelector("[data-testid='bot.ide.chat_area.message_box'] .flow-markdown-body")
  550. isDone = Boolean(outputEle.querySelector(".chat-uikit-message-box-container__message__message-box__footer").childNodes.length != 0)
  551. text = contentEle.innerHTML.replace(/<br .+?>/g, "").replace(/<hr .+?>/g, "<hr/>")
  552. await setZoteroText()
  553. } else if (AI == "Yiyan") {
  554. const outputEle = document.querySelector(".ErXhAgf5 .RmHagX8t");
  555. const contentEle = outputEle.querySelector(".custom-html.md-stream-desktop")
  556. isDone = Boolean(outputEle.querySelector(".fXxD0Rtx"))
  557. text = contentEle.innerHTML.replace(/<br .+?>/g, "").replace(/<hr .+?>/g, "<hr/>")
  558. await setZoteroText()
  559. } else if (AI == "Tongyi") {
  560. const lastAnwser = [...document.querySelectorAll("[class^=answerItem]")].slice(-1)[0]
  561. type = "markdown"
  562. 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
  563. isDone = message.contents[message.contents.length - 1].status == "finished"
  564. text = message.contents[message.contents.length - 1].content
  565. await setZoteroText()
  566. } else if (AI == "Claude") {
  567. const node = [...document.querySelectorAll("div[data-test-render-count]")].slice(-1)[0].querySelector("[data-is-streaming]");
  568. type = "markdown"
  569. text = node[Object.keys(node)[1]].children[0].props.children[0].props.text
  570. isDone = node.getAttribute("data-is-streaming") == "false";
  571. await setZoteroText()
  572. } else if (AI == "Doubao") {
  573. const nodes = [...document.querySelectorAll("[class^=message-box-content-wrapper]")]
  574. const node = nodes.slice(-1)[0]
  575. const data = node[Object.keys(node)[0]].child.child.child.child.memoizedState.memoizedState.current.value
  576. type = "markdown"
  577. text = data.content_obj.text;
  578. isDone = data.ext.is_finish == "1";
  579. await setZoteroText()
  580. } else if (AI == "AIStudio") {
  581. type = "html"
  582. const msgNodes = document.querySelectorAll('ms-text-chunk.ng-star-inserted')
  583. const msgNode = msgNodes[msgNodes.length - 1]
  584. isDone = !Boolean(msgNode.closest("ms-chat-turn").querySelector(".loading-indicator"));
  585. if (isDone) {
  586. // 消息会有延迟,务必等待
  587. let n = 10
  588. isDone = false
  589. while (n > 0) {
  590. await sleep(100)
  591. text = msgNode.innerHTML;
  592. await setZoteroText()
  593. n -= 1
  594. }
  595. isDone = true
  596. }
  597. text = msgNode.innerHTML;
  598. await setZoteroText()
  599. }
  600. }
  601. }
  602. } catch (e) {
  603. console.log(e)
  604. await sleep(1000)
  605. }
  606. await sleep(10)
  607. }
  608. })();

QingJ © 2025

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