GPT Auto task

根据缓存中的数据自动在网页上与chat gpt对话

当前为 2023-12-07 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @namespace https://greasyfork.org/zh-CN/users/1106595-ikkem-lin
  3. // @name GPT Auto task
  4. // @author Mark
  5. // @description 根据缓存中的数据自动在网页上与chat gpt对话
  6. // @homepageURL https://github.com/IKKEM-Lin/gpt-auto-task
  7. // @version 0.2.0
  8. // @match *chat.openai.com/*
  9. // @run-at document-idle
  10. // @require https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js
  11. // @require https://cdn.jsdelivr.net/npm/idb-keyval@6/dist/umd.js
  12. // ==/UserScript==
  13. (function () {
  14. "use strict";
  15.  
  16. const tableName = "data";
  17.  
  18. const dbTable = {
  19. tasks: idbKeyval.createStore("tasks", tableName),
  20. config: idbKeyval.createStore("config", tableName),
  21. skipSnippet: idbKeyval.createStore("skipSnippet", tableName),
  22. response1: idbKeyval.createStore("response1", tableName),
  23. response2: idbKeyval.createStore("response2", tableName),
  24. responseProcessed: idbKeyval.createStore("responseProcessed", tableName),
  25. };
  26.  
  27. const locationReload = () => {
  28. location.href = "https://chat.openai.com/?model=gpt-4";
  29. };
  30.  
  31. const downloadFile = (data, fileName) => {
  32. const a = document.createElement("a");
  33. document.body.appendChild(a);
  34. a.style = "display: none";
  35. const blob = new Blob([data], {
  36. type: "application/octet-stream",
  37. });
  38. const url = window.URL.createObjectURL(blob);
  39. a.href = url;
  40. a.download = fileName;
  41. a.click();
  42. window.URL.revokeObjectURL(url);
  43. };
  44.  
  45. const yaml2object = (yamlStr) => {
  46. try {
  47. return jsyaml.load(yamlStr);
  48. } catch (error) {
  49. return null;
  50. }
  51. };
  52.  
  53. function hashFnv32a(str, asString = true, seed = undefined) {
  54. /*jshint bitwise:false */
  55. var i,
  56. l,
  57. hval = seed === undefined ? 0x811c9dc5 : seed;
  58.  
  59. for (i = 0, l = str.length; i < l; i++) {
  60. hval ^= str.charCodeAt(i);
  61. hval +=
  62. (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
  63. }
  64. if (asString) {
  65. // Convert to 8 digit hex string
  66. return ("0000000" + (hval >>> 0).toString(16)).substr(-8);
  67. }
  68. return hval >>> 0;
  69. }
  70.  
  71. function reactionObjHandler(input) {
  72. let result = [];
  73. const validateKeys = ["reactants", "products", "condition", "catalysts"];
  74. function test(reaction) {
  75. // if (!reaction) {
  76. // return
  77. // }
  78. if (reaction instanceof Array) {
  79. return reaction.map((item) => {
  80. return test(item);
  81. });
  82. } else {
  83. try {
  84. var keys = Object.keys(reaction);
  85. } catch (error) {
  86. // debugger;
  87. console.error(error);
  88. throw new Error();
  89. }
  90. if (validateKeys.some((key) => keys.includes(key))) {
  91. result.push(reaction);
  92. return;
  93. }
  94. keys.forEach((key) => {
  95. if (reaction[key] && typeof reaction[key] === "object") {
  96. test(reaction[key]);
  97. }
  98. });
  99. }
  100. }
  101. test(input);
  102. return result;
  103. }
  104.  
  105. function readFile(accept = "", multiple = false) {
  106. const inputEl = document.createElement("input");
  107. inputEl.setAttribute("type", "file");
  108. inputEl.setAttribute("accept", accept);
  109. inputEl.setAttribute("multiple", !!multiple);
  110. return new Promise((resolve, reject) => {
  111. inputEl.addEventListener("change", (e) => {
  112. resolve(multiple ? inputEl.files : inputEl.files[0]);
  113. window.removeEventListener("click", onWindowClick, true);
  114. });
  115. inputEl.click();
  116.  
  117. const onWindowClick = () => {
  118. if (!inputEl.value) {
  119. reject(new Error("用户取消选择"));
  120. }
  121. window.removeEventListener("click", onWindowClick, true);
  122. };
  123. setTimeout(() => {
  124. window.addEventListener("click", onWindowClick, true);
  125. }, 100);
  126. });
  127. }
  128.  
  129. class GPT_ASK_LOOP {
  130. queue = [];
  131. abstract = [];
  132. responds = [];
  133. checkInterval = 20000;
  134. account = "";
  135. downloadBtn = null;
  136. retrying = false;
  137. lastSaveTime = 0;
  138. prompt1 = "";
  139. prompt2 = "";
  140. modelNum = 1;
  141.  
  142. INPUT_SELECTOR = "#prompt-textarea";
  143. SUBMIT_BTN_SELECTOR = 'button[data-testid="send-button"]';
  144. RESPOND_SELECTOR = 'main div[data-message-author-role="assistant"]';
  145. NEW_CHART_BTN_SELECTOR = "nav div.flex-col a[href='/']";
  146. NORMAL_RESPOND_BTN_SELECTOR = "form div button.btn-neutral";
  147. ERROR_RESPOND_BTN_SELECTOR = "form div button.btn-primary";
  148.  
  149. sleep(duration) {
  150. return new Promise((resolve, reject) => {
  151. setTimeout(() => {
  152. resolve(true);
  153. }, duration);
  154. });
  155. }
  156.  
  157. constructor(account) {
  158. this.initData().then(() => {
  159. this.account = account || Math.ceil(Math.random() * 1e10).toString(32);
  160. const btnWrap = document.createElement("div");
  161. btnWrap.innerHTML = `<button style="padding: 4px 8px;position: fixed;bottom: 20%;right: 8px;border-radius: 4px;background-color: #224466;color: #fff;">下载已生成结果(queue: ${this.queue.length}, res: ${this.responds.length})</button>`;
  162. this.downloadBtn = btnWrap.querySelector("button");
  163. this.downloadBtn.onclick = this.handleDownload.bind(this);
  164. document.body.appendChild(btnWrap);
  165.  
  166. const btnWrapBackup = document.createElement("div");
  167. btnWrapBackup.innerHTML = `<button style="padding: 4px 8px;position: fixed;bottom: 30%;right: 8px;border-radius: 4px;background-color: #224466;color: #fff;">备份</button>`;
  168. const backupBtn = btnWrapBackup.querySelector("button");
  169. backupBtn.onclick = this.backUp.bind(this);
  170. document.body.appendChild(btnWrapBackup);
  171.  
  172. const btnWrapImport = document.createElement("div");
  173. btnWrapImport.innerHTML = `<button style="padding: 4px 8px;position: fixed;bottom: 40%;right: 8px;border-radius: 4px;background-color: #224466;color: #fff;">导入</button>`;
  174. const importBtn = btnWrapImport.querySelector("button");
  175. importBtn.onclick = async () => {
  176. if (
  177. !window.confirm(
  178. "The data in browser will be clear up. Please make sure you have to do this !!!"
  179. )
  180. ) {
  181. return;
  182. }
  183. const file = await readFile(".json");
  184. const reader = new FileReader();
  185.  
  186. reader.onload = (event) => {
  187. const json = JSON.parse(event.target.result);
  188. // console.log({json}, 'json')
  189. this.importFromBackUp.bind(this)(json);
  190. };
  191.  
  192. reader.readAsText(file);
  193. };
  194. document.body.appendChild(btnWrapImport);
  195. this.main();
  196. });
  197. }
  198.  
  199. async initData() {
  200. await this.legacyTaskInit();
  201. const skipSnippetKeys = await idbKeyval.keys(dbTable.skipSnippet);
  202. const responseKeys = await idbKeyval.keys(dbTable.responseProcessed);
  203. const responseValues = await idbKeyval.values(dbTable.responseProcessed);
  204. this.responds = responseValues.map((item) => ({
  205. articleId: item.articleId,
  206. snippetId: item.snippetId,
  207. createdTime: item.createdTime,
  208. }));
  209. const indexDBConfig = (await idbKeyval.entries(dbTable.config)) || [];
  210. if (indexDBConfig.length) {
  211. this.prompt1 = indexDBConfig.find((item) => item[0] === "prompt1");
  212. this.prompt2 = indexDBConfig.find((item) => item[0] === "prompt2");
  213. this.modelNum = indexDBConfig.find((item) => item[0] === "modelNum");
  214. this.prompt1 = (this.prompt1 && this.prompt1[1]) || "";
  215. this.prompt2 = (this.prompt2 && this.prompt2[1]) || "";
  216. this.modelNum = (this.modelNum && this.modelNum[1]) || 1;
  217. }
  218. const snippetSourceData = (await idbKeyval.values(dbTable.tasks)) || [];
  219. this.abstract = snippetSourceData.filter(
  220. (item) => item.type == "abstract"
  221. );
  222. const paragraphs = snippetSourceData.filter(
  223. (item) => item.type != "abstract"
  224. );
  225. this.queue = paragraphs.filter(
  226. (item) =>
  227. !(responseKeys || []).includes(`${item.article_id}-${item.id}`) &&
  228. !(skipSnippetKeys || []).includes(`${item.article_id}-${item.id}`)
  229. );
  230. if (this.queue.length !== 0) {
  231. return;
  232. }
  233. this.queue = paragraphs.filter((item) =>
  234. (skipSnippetKeys || []).includes(`${item.article_id}-${item.id}`)
  235. );
  236. const skipSnippetEntries = await idbKeyval.entries(dbTable.skipSnippet);
  237. this.queue.sort((a,b) => {
  238. const aItem = skipSnippetEntries.find(item => item[0] === `${a.article_id}-${a.id}`)
  239. const bItem = skipSnippetEntries.find(item => item[0] === `${b.article_id}-${b.id}`)
  240. return aItem[1] < bItem[1] ? -1 : 1
  241. })
  242. }
  243.  
  244. async legacyTaskInit() {
  245. const indexDBTasks = (await idbKeyval.entries(dbTable.tasks)) || [];
  246. const indexDBConfig = (await idbKeyval.entries(dbTable.config)) || [];
  247. if (indexDBConfig.length === 0) {
  248. const prompt1 = localStorage.getItem("mock_prompt1");
  249. const prompt2 = localStorage.getItem("mock_prompt2");
  250. const modelNum = +localStorage.getItem("model_number") || 1;
  251. if (!prompt1 || !prompt2) {
  252. return;
  253. }
  254. await idbKeyval.setMany(
  255. [
  256. ["prompt1", prompt1],
  257. ["prompt2", prompt2],
  258. ["modelNum", modelNum],
  259. ],
  260. dbTable.config
  261. );
  262. }
  263. if (indexDBTasks.length === 0) {
  264. const snippetSourceData = JSON.parse(
  265. localStorage.getItem("snippetSourceData") || "[]"
  266. );
  267. if (!snippetSourceData.length) {
  268. return;
  269. }
  270. const snippetSourceDataEntries = snippetSourceData.map((item) => [
  271. `${item.article_id}-${item.id}`,
  272. item,
  273. ]);
  274. await idbKeyval.setMany(snippetSourceDataEntries, dbTable.tasks);
  275. }
  276. }
  277.  
  278. async importFromBackUp(data) {
  279. const {
  280. response1,
  281. response2,
  282. responseProcessed,
  283. skipSnippet,
  284. config,
  285. tasks,
  286. } = data;
  287. if (
  288. !(
  289. response1 &&
  290. response2 &&
  291. responseProcessed &&
  292. skipSnippet &&
  293. config &&
  294. tasks
  295. )
  296. ) {
  297. alert(
  298. `[ "response1", "response2", "responseProcessed", "skipSnippet", "config", "tasks" ], all of them are required`
  299. );
  300. return;
  301. }
  302. await idbKeyval.clear(dbTable.response1);
  303. await idbKeyval.clear(dbTable.response2);
  304. await idbKeyval.clear(dbTable.responseProcessed);
  305. await idbKeyval.clear(dbTable.skipSnippet);
  306. await idbKeyval.clear(dbTable.config);
  307. await idbKeyval.clear(dbTable.tasks);
  308. await idbKeyval.setMany(response1, dbTable.response1);
  309. await idbKeyval.setMany(response2, dbTable.response2);
  310. await idbKeyval.setMany(responseProcessed, dbTable.responseProcessed);
  311. await idbKeyval.setMany(skipSnippet, dbTable.skipSnippet);
  312. await idbKeyval.setMany(config, dbTable.config);
  313. await idbKeyval.setMany(tasks, dbTable.tasks);
  314. locationReload();
  315. }
  316.  
  317. async backUp() {
  318. const response1 = (await idbKeyval.entries(dbTable.response1)) || [];
  319. const response2 = (await idbKeyval.entries(dbTable.response2)) || [];
  320. const responseProcessed =
  321. (await idbKeyval.entries(dbTable.responseProcessed)) || [];
  322. const skipSnippet = (await idbKeyval.entries(dbTable.skipSnippet)) || [];
  323. const config = (await idbKeyval.entries(dbTable.config)) || [];
  324. const tasks = (await idbKeyval.entries(dbTable.tasks)) || [];
  325.  
  326. const paragraphs = tasks.filter((item) => item[1].type != "abstract");
  327.  
  328. const articleIds = tasks.map((item) => item[1].article_id).sort();
  329.  
  330. const now = new Date();
  331. const current = `${now.getFullYear()}-${
  332. now.getMonth() + 1
  333. }-${now.getDate()}-${now.getHours()}${now.getMinutes()}${now.getSeconds()}`;
  334. downloadFile(
  335. JSON.stringify({
  336. response1,
  337. response2,
  338. responseProcessed,
  339. skipSnippet,
  340. config,
  341. tasks,
  342. }),
  343. `Article_${articleIds[0]}_${
  344. articleIds[articleIds.length - 1]
  345. }-progress_${paragraphs.length}_${
  346. responseProcessed.length
  347. }-${current}.backup.json`
  348. );
  349. }
  350.  
  351. async handleDownload() {
  352. const reactionGroups = await idbKeyval.values(dbTable.responseProcessed);
  353. if (!reactionGroups.length) {
  354. return;
  355. }
  356. const reactions = [];
  357. reactionGroups.forEach((item) => {
  358. const { articleId, snippetId, reaction } = item;
  359. const uniqReaction = Array.from(
  360. new Set(reaction.map((v) => JSON.stringify(v)))
  361. ).map((v) => JSON.parse(v));
  362. uniqReaction.forEach((data) => {
  363. const name = hashFnv32a(JSON.stringify(data));
  364. reactions.push({ articleId, snippetId, data, name });
  365. });
  366. });
  367.  
  368. const now = new Date();
  369. downloadFile(
  370. JSON.stringify(reactions),
  371. `${this.account}-${now.getFullYear()}-${
  372. now.getMonth() + 1
  373. }-${now.getDate()}-${now.getHours()}${now.getMinutes()}${now.getSeconds()}-${
  374. reactions.length
  375. }.json`
  376. );
  377. }
  378.  
  379. async report(tip = "") {
  380. await fetch("http://localhost:3000", {
  381. method: "POST",
  382. body: JSON.stringify({
  383. account: this.account,
  384. reaction_count: this.responds.length,
  385. queue_count: this.queue.length,
  386. tip: tip,
  387. }),
  388. }).catch((err) => {
  389. // console.error({ err });
  390. });
  391. }
  392.  
  393. genPrompt(content, step = 1) {
  394. return step === 1
  395. ? `${this.prompt1}
  396.  
  397. ''' ${content} ''' `
  398. : this.prompt2;
  399. }
  400.  
  401. async _updateDownloadBtnText() {
  402. if (this.downloadBtn) {
  403. const snippetSourceData = (await idbKeyval.values(dbTable.tasks)) || [];
  404. const paragraphs = snippetSourceData.filter(
  405. (item) => item.type != "abstract"
  406. );
  407. this.downloadBtn.innerText = `下载已生成结果(queue: ${
  408. this.queue.length
  409. }, res: ${this.responds.length}, skip: ${
  410. paragraphs.length - this.queue.length - this.responds.length
  411. })`;
  412. }
  413. }
  414.  
  415. _getLastRespondTime() {
  416. return Math.max.apply(
  417. null,
  418. this.responds
  419. .map((item) => item.createdTime)
  420. .filter((item) => item)
  421. .concat([0])
  422. );
  423. }
  424.  
  425. getTask() {
  426. const task = this.queue[0];
  427. const maxTime = this._getLastRespondTime();
  428. this.report(
  429. (task &&
  430. `Working on articleId: ${task.article_id}, snippetId: ${
  431. task.id
  432. }, last-update-time: ${new Date(maxTime).toLocaleString()}`) ||
  433. ""
  434. );
  435. if (!task) {
  436. console.log("任务队列为空");
  437. return async () => null;
  438. }
  439. return async () => {
  440. const { article_id, id, content } = task;
  441. const relatedAbstract =
  442. this.abstract.find((item) => item.article_id === article_id)
  443. ?.content || "";
  444. console.log(
  445. `开始触发 ${article_id}-${id}, ${new Date().toTimeString()}`
  446. );
  447. const promptContent = `
  448. ${relatedAbstract}
  449.  
  450. ${content}
  451. `;
  452. const prompt1 = this.genPrompt(promptContent, 1);
  453. const prompt2 = this.genPrompt(promptContent, 2);
  454. const result1 = await this.trigger(prompt1).catch((err) => {
  455. return null;
  456. });
  457. if (!result1) {
  458. return null;
  459. }
  460. await idbKeyval.set(
  461. `${article_id}-${id}`,
  462. {
  463. articleId: article_id,
  464. snippetId: id,
  465. reaction: result1,
  466. createdTime: new Date().valueOf(),
  467. },
  468. dbTable.response1
  469. );
  470. await this.sleep(3 * 1000);
  471. const result2 = await this.trigger(prompt2).catch((err) => {
  472. return null;
  473. });
  474. if (!result2) {
  475. return { articleId: article_id, snippetId: id, reaction: result1 };
  476. }
  477. await idbKeyval.set(
  478. `${article_id}-${id}`,
  479. {
  480. articleId: article_id,
  481. snippetId: id,
  482. reaction: result2,
  483. createdTime: new Date().valueOf(),
  484. },
  485. dbTable.response2
  486. );
  487. return { articleId: article_id, snippetId: id, reaction: result2 };
  488. };
  489. }
  490.  
  491. async rawReactionProcess(rawReactionHTML) {
  492. const ele = document.createElement("div");
  493. ele.innerHTML = rawReactionHTML;
  494. const res = Array.from(ele.querySelectorAll("code"))
  495. .map((el) => el.innerText)
  496. .map((yml) => yaml2object(yml));
  497.  
  498. if (res && res.length > 0 && res.every((s) => s !== null)) {
  499. const result = reactionObjHandler(res);
  500. return result.length > 0 ? result : null;
  501. }
  502. return null;
  503. }
  504.  
  505. async skipSnippetHandler(articleId, snippetId) {
  506. const oldVal = await idbKeyval.get(
  507. `${articleId}-${snippetId}`,
  508. dbTable.skipSnippet
  509. );
  510. await idbKeyval.set(
  511. `${articleId}-${snippetId}`,
  512. (oldVal || 0) + 1,
  513. dbTable.skipSnippet
  514. );
  515. this.queue = this.queue.filter((item) => item.id !== snippetId);
  516. }
  517.  
  518. async saveRespond(respond) {
  519. const { articleId, snippetId } = respond;
  520. const currentTimeStamp = new Date().valueOf();
  521. const reactionProcessed = await this.rawReactionProcess(respond.reaction);
  522. if (!reactionProcessed) {
  523. console.warn(`${articleId}-${snippetId} 无法解析出 reaction, 即将跳过`);
  524. await this.skipSnippetHandler(articleId, snippetId);
  525. return;
  526. }
  527. this.responds.push({
  528. articleId,
  529. snippetId,
  530. createdTime: currentTimeStamp,
  531. });
  532. this.queue = this.queue.filter((item) => item.id !== snippetId);
  533.  
  534. await idbKeyval.set(
  535. `${articleId}-${snippetId}`,
  536. {
  537. ...respond,
  538. reaction: reactionProcessed,
  539. createdTime: new Date().valueOf(),
  540. },
  541. dbTable.responseProcessed
  542. );
  543. try {
  544. await idbKeyval.del(`${articleId}-${snippetId}`, dbTable.skipSnippet);
  545. } catch (err) {}
  546. if (this.responds.length && this.responds.length % 50 === 0) {
  547. this.handleDownload.bind(this)();
  548. }
  549. }
  550.  
  551. trigger(prompt, checkInterval = this.checkInterval) {
  552. return new Promise((resolve, reject) => {
  553. const textEl = document.querySelector(this.INPUT_SELECTOR);
  554. const submitEl = document.querySelector(this.SUBMIT_BTN_SELECTOR);
  555. textEl.value = prompt;
  556. textEl.dispatchEvent(new Event("input", { bubbles: true }));
  557. setTimeout(() => {
  558. submitEl.click();
  559.  
  560. let resCache = null;
  561. let checkOutputCount = 0;
  562. (async () => {
  563. while (true) {
  564. await this.sleep(checkInterval);
  565. const result = Array.from(
  566. document.querySelectorAll(this.RESPOND_SELECTOR)
  567. );
  568. const temp = result[result.length - 1];
  569. if (!temp) {
  570. if (checkOutputCount > 0) {
  571. console.log("检查结果超时");
  572. reject(null);
  573. break;
  574. }
  575. checkOutputCount++;
  576. continue;
  577. }
  578. if (resCache === temp.innerHTML) {
  579. // console.log("匹配,resCache:", resCache);
  580. const validateResult = await this.validate(resCache).catch(
  581. (err) => {
  582. reject(null);
  583. return;
  584. }
  585. );
  586. if (validateResult === true) {
  587. resolve(resCache);
  588. break;
  589. } else if (validateResult === false) {
  590. continue;
  591. }
  592. reject(null);
  593. break;
  594. }
  595. resCache = temp.innerHTML;
  596. console.log(`${checkInterval / 1000}s后再次检查结果`);
  597. }
  598. })();
  599. }, 4000);
  600. });
  601. }
  602.  
  603. async validate(innerHTML) {
  604. const buttons = document.querySelectorAll(
  605. this.NORMAL_RESPOND_BTN_SELECTOR
  606. );
  607. const errorBtn = document.querySelectorAll(
  608. this.ERROR_RESPOND_BTN_SELECTOR
  609. );
  610. const feedbackBtns = document.querySelectorAll('main .final-completion button[class*="final-completion"]')
  611. const regenerateBtn = feedbackBtns[feedbackBtns.length - 1];
  612. // 如果触发gpt-4 3小时25次限制
  613. if (!regenerateBtn && !errorBtn[0] && innerHTML.includes("usage cap")) {
  614. console.error("触发gpt-4 3小时25次限制,等待10min后重试");
  615. await this.sleep(10 * 60 * 1000);
  616. throw new Error("触发gpt-4 3小时25次限制");
  617. }
  618. // 如果openAI服务器报错未返回结果
  619. if (errorBtn[0]) {
  620. // && innerHTML.includes("wrong")) {
  621. if (this.retrying) {
  622. this.retrying = false;
  623. return true;
  624. }
  625. errorBtn[0].click();
  626. this.retrying = true;
  627. return false;
  628. }
  629. // 如果输出结果未包含code标签
  630. if (!innerHTML.includes("</code>")) {
  631. if (this.retrying) {
  632. this.retrying = false;
  633. console.error("第二次还是未输出yaml结构");
  634. throw new Error("未返回yaml结构");
  635. }
  636. console.error("未输出yaml结构,重试一次");
  637. regenerateBtn.click();
  638. this.retrying = true;
  639. return false;
  640. }
  641. this.retrying = false;
  642. // 如果还未完全输出
  643. if (
  644. buttons.length > 1 &&
  645. !buttons[buttons.length - 1].innerText.includes("Regenerate")
  646. ) {
  647. buttons[buttons.length - 1].click();
  648. return false;
  649. }
  650. return true;
  651. }
  652.  
  653. async main(sleepTime = 5000) {
  654. let emptyCount = 0;
  655. while (true) {
  656. // {0: gpt-3.5, 1: gpt-4}
  657. const modelNum = this.modelNum;
  658. // const gpt4btn = document.querySelectorAll(
  659. // "ul > li > button.cursor-pointer"
  660. // )[modelNum];
  661.  
  662. // if (gpt4btn) {
  663. // console.log(`当前模型为:${gpt4btn.innerText}`);
  664. // gpt4btn.firstChild.click();
  665. // } else {
  666. // console.warn(`无法选择模型,2分钟后刷新`);
  667. // await this.sleep(2 * 60 * 1000);
  668. // locationReload();
  669. // }
  670. const currentModel = document.querySelector('main [aria-haspopup="menu"]')?.innerText;
  671. const isGPT4 = currentModel.trim() === "ChatGPT 4"
  672. await this.sleep(sleepTime / 2);
  673. if (
  674. modelNum === 1 && !isGPT4
  675. ) {
  676. console.log("未切换到gpt-4模式, 5分钟后重试");
  677. const maxTime = this._getLastRespondTime();
  678. const diff = new Date().valueOf() - maxTime;
  679. if (maxTime && diff > 1.5 * 60 * 60 * 1000) {
  680. console.warn("超时未刷新, 5分钟后刷新页面");
  681. await this.sleep(5 * 60 * 1000);
  682. locationReload();
  683. break;
  684. }
  685. this.report(
  686. `触发gpt-4 3小时25次限制,上次运行时间:${new Date(
  687. maxTime
  688. ).toLocaleString()}`
  689. );
  690. await this.sleep(5 * 60 * 1000);
  691. const newChatBtn = document.querySelector(
  692. this.NEW_CHART_BTN_SELECTOR
  693. );
  694. newChatBtn.click();
  695. continue;
  696. }
  697. const task = this.getTask();
  698. if (!task) {
  699. if (emptyCount > 0) {
  700. console.warn("连续两次未获取到任务,2分钟后刷新");
  701. await this.sleep(2 * 60 * 1000);
  702. locationReload();
  703. break;
  704. }
  705. emptyCount++;
  706. await this.sleep(5 * 60 * 1000);
  707. continue;
  708. }
  709.  
  710. const result = await task();
  711. if (result) {
  712. this.saveRespond(result);
  713. emptyCount = 0;
  714. } else {
  715. if (emptyCount > 0) {
  716. const task = this.queue[0];
  717. const { article_id, id } = task;
  718. console.warn(
  719. `${article_id}-${id}连续两次未获取到任务值,2分钟后刷新`
  720. );
  721. await this.skipSnippetHandler(article_id, id);
  722. await this.sleep(2 * 60 * 1000);
  723. locationReload();
  724. break;
  725. }
  726. emptyCount += 1;
  727. }
  728. console.log(`${sleepTime / 1000}s后将再次触发`);
  729. const newChatBtn = document.querySelector(this.NEW_CHART_BTN_SELECTOR);
  730. newChatBtn.click();
  731. await this.sleep(sleepTime / 2);
  732. this._updateDownloadBtnText();
  733. }
  734. }
  735. }
  736.  
  737. function secondInterval() {
  738. console.log("start secondInterval...");
  739. const sleep = (duration) => {
  740. return new Promise((resolve, reject) => {
  741. setTimeout(() => {
  742. resolve(true);
  743. }, duration);
  744. });
  745. };
  746. setInterval(async () => {
  747. const responds = await idbKeyval.values(dbTable.responseProcessed);
  748. const maxTime = Math.max.apply(
  749. null,
  750. responds
  751. .map((item) => item.createdTime)
  752. .filter((item) => item)
  753. .concat([0])
  754. );
  755. const diff = new Date().valueOf() - maxTime;
  756.  
  757. console.log(`last updated at: ${maxTime}, diff is ${diff}`);
  758. if (maxTime && diff > 30 * 60 * 1000) {
  759. console.warn("超时未刷新, 2分钟后刷新页面");
  760. await sleep(2 * 60 * 1000);
  761. locationReload();
  762. }
  763. }, 10 * 60 * 1000);
  764. }
  765.  
  766. function start() {
  767. const ACCOUNT_NAME_SELECTOR = "nav > div:last-child > div:last-child";
  768. const nameEl = document.querySelector(ACCOUNT_NAME_SELECTOR);
  769. const name = nameEl && nameEl.innerText;
  770. if (name) {
  771. new GPT_ASK_LOOP(name);
  772. secondInterval();
  773. } else {
  774. setTimeout(() => {
  775. start();
  776. }, 5000);
  777. }
  778. }
  779. start();
  780. })();