GPT Auto task

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

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

  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.1.9
  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 = "#prompt-textarea + button";
  144. RESPOND_SELECTOR = "main .group.text-token-text-primary";
  145. NEW_CHART_BTN_SELECTOR = "nav>div.mb-1>a:first-child";
  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. }
  237.  
  238. async legacyTaskInit() {
  239. const indexDBTasks = (await idbKeyval.entries(dbTable.tasks)) || [];
  240. const indexDBConfig = (await idbKeyval.entries(dbTable.config)) || [];
  241. if (indexDBConfig.length === 0) {
  242. const prompt1 = localStorage.getItem("mock_prompt1");
  243. const prompt2 = localStorage.getItem("mock_prompt2");
  244. const modelNum = +localStorage.getItem("model_number") || 1;
  245. if (!prompt1 || !prompt2) {
  246. return;
  247. }
  248. await idbKeyval.setMany(
  249. [
  250. ["prompt1", prompt1],
  251. ["prompt2", prompt2],
  252. ["modelNum", modelNum],
  253. ],
  254. dbTable.config
  255. );
  256. }
  257. if (indexDBTasks.length === 0) {
  258. const snippetSourceData = JSON.parse(
  259. localStorage.getItem("snippetSourceData") || "[]"
  260. );
  261. if (!snippetSourceData.length) {
  262. return;
  263. }
  264. const snippetSourceDataEntries = snippetSourceData.map((item) => [
  265. `${item.article_id}-${item.id}`,
  266. item,
  267. ]);
  268. await idbKeyval.setMany(snippetSourceDataEntries, dbTable.tasks);
  269. }
  270. }
  271.  
  272. async importFromBackUp(data) {
  273. const {
  274. response1,
  275. response2,
  276. responseProcessed,
  277. skipSnippet,
  278. config,
  279. tasks,
  280. } = data;
  281. if (
  282. !(
  283. response1 &&
  284. response2 &&
  285. responseProcessed &&
  286. skipSnippet &&
  287. config &&
  288. tasks
  289. )
  290. ) {
  291. alert(
  292. `[ "response1", "response2", "responseProcessed", "skipSnippet", "config", "tasks" ], all of them are required`
  293. );
  294. return;
  295. }
  296. await idbKeyval.clear(dbTable.response1);
  297. await idbKeyval.clear(dbTable.response2);
  298. await idbKeyval.clear(dbTable.responseProcessed);
  299. await idbKeyval.clear(dbTable.skipSnippet);
  300. await idbKeyval.clear(dbTable.config);
  301. await idbKeyval.clear(dbTable.tasks);
  302. await idbKeyval.setMany(response1, dbTable.response1);
  303. await idbKeyval.setMany(response2, dbTable.response2);
  304. await idbKeyval.setMany(responseProcessed, dbTable.responseProcessed);
  305. await idbKeyval.setMany(skipSnippet, dbTable.skipSnippet);
  306. await idbKeyval.setMany(config, dbTable.config);
  307. await idbKeyval.setMany(tasks, dbTable.tasks);
  308. }
  309.  
  310. async backUp() {
  311. const response1 = (await idbKeyval.entries(dbTable.response1)) || [];
  312. const response2 = (await idbKeyval.entries(dbTable.response2)) || [];
  313. const responseProcessed =
  314. (await idbKeyval.entries(dbTable.responseProcessed)) || [];
  315. const skipSnippet = (await idbKeyval.entries(dbTable.skipSnippet)) || [];
  316. const config = (await idbKeyval.entries(dbTable.config)) || [];
  317. const tasks = (await idbKeyval.entries(dbTable.tasks)) || [];
  318.  
  319. const paragraphs = tasks.filter((item) => item[1].type != "abstract");
  320.  
  321. const articleIds = tasks.map((item) => item[1].article_id).sort();
  322.  
  323. const now = new Date();
  324. const current = `${now.getFullYear()}-${
  325. now.getMonth() + 1
  326. }-${now.getDate()}-${now.getHours()}${now.getMinutes()}${now.getSeconds()}`;
  327. downloadFile(
  328. JSON.stringify({
  329. response1,
  330. response2,
  331. responseProcessed,
  332. skipSnippet,
  333. config,
  334. tasks,
  335. }),
  336. `Article_${articleIds[0]}_${
  337. articleIds[articleIds.length - 1]
  338. }-progress_${paragraphs.length}_${
  339. responseProcessed.length
  340. }-${current}.backup.json`
  341. );
  342. }
  343.  
  344. async handleDownload() {
  345. const reactionGroups = await idbKeyval.values(dbTable.responseProcessed);
  346. if (!reactionGroups.length) {
  347. return;
  348. }
  349. const reactions = [];
  350. reactionGroups.forEach((item) => {
  351. const { articleId, snippetId, reaction } = item;
  352. const uniqReaction = Array.from(
  353. new Set(reaction.map((v) => JSON.stringify(v)))
  354. ).map((v) => JSON.parse(v));
  355. uniqReaction.forEach((data) => {
  356. const name = hashFnv32a(JSON.stringify(data));
  357. reactions.push({ articleId, snippetId, data, name });
  358. });
  359. });
  360.  
  361. const now = new Date();
  362. downloadFile(
  363. JSON.stringify(reactions),
  364. `${this.account}-${now.getFullYear()}-${
  365. now.getMonth() + 1
  366. }-${now.getDate()}-${now.getHours()}${now.getMinutes()}${now.getSeconds()}-${
  367. reactions.length
  368. }.json`
  369. );
  370. }
  371.  
  372. async report(tip = "") {
  373. await fetch("https://gpt-hit.deno.dev/api/update", {
  374. method: "POST",
  375. body: JSON.stringify({
  376. account: this.account,
  377. reaction_count: this.responds.length,
  378. queue_count: this.queue.length,
  379. tip: tip,
  380. }),
  381. }).catch((err) => {
  382. // console.error({ err });
  383. });
  384. }
  385.  
  386. genPrompt(content, step = 1) {
  387. return step === 1
  388. ? `${this.prompt1}
  389. ''' ${content} ''' `
  390. : this.prompt2;
  391. }
  392.  
  393. async _updateDownloadBtnText() {
  394. if (this.downloadBtn) {
  395. const snippetSourceData = (await idbKeyval.values(dbTable.tasks)) || [];
  396. const paragraphs = snippetSourceData.filter(
  397. (item) => item.type != "abstract"
  398. );
  399. this.downloadBtn.innerText = `下载已生成结果(queue: ${
  400. this.queue.length
  401. }, res: ${this.responds.length}, skip: ${
  402. paragraphs.length - this.queue.length - this.responds.length
  403. })`;
  404. }
  405. }
  406.  
  407. _getLastRespondTime() {
  408. return Math.max.apply(
  409. null,
  410. this.responds
  411. .map((item) => item.createdTime)
  412. .filter((item) => item)
  413. .concat([0])
  414. );
  415. }
  416.  
  417. getTask() {
  418. const task = this.queue[0];
  419. const maxTime = this._getLastRespondTime();
  420. this.report(
  421. (task &&
  422. `Working on articleId: ${task.article_id}, snippetId: ${
  423. task.id
  424. }, last-update-time: ${new Date(maxTime).toLocaleString()}`) ||
  425. ""
  426. );
  427. if (!task) {
  428. console.log("任务队列为空");
  429. return async () => null;
  430. }
  431. return async () => {
  432. const { article_id, id, content } = task;
  433. const relatedAbstract =
  434. this.abstract.find((item) => item.article_id === article_id)
  435. ?.content || "";
  436. console.log(
  437. `开始触发 ${article_id}-${id}, ${new Date().toTimeString()}`
  438. );
  439. const promptContent = `
  440. ${relatedAbstract}
  441. ${content}
  442. `;
  443. const prompt1 = this.genPrompt(promptContent, 1);
  444. const prompt2 = this.genPrompt(promptContent, 2);
  445. const result1 = await this.trigger(prompt1).catch((err) => {
  446. return null;
  447. });
  448. if (!result1) {
  449. return null;
  450. }
  451. await idbKeyval.set(
  452. `${article_id}-${id}`,
  453. {
  454. articleId: article_id,
  455. snippetId: id,
  456. reaction: result1,
  457. createdTime: new Date().valueOf(),
  458. },
  459. dbTable.response1
  460. );
  461. await this.sleep(3 * 1000);
  462. const result2 = await this.trigger(prompt2).catch((err) => {
  463. return null;
  464. });
  465. if (!result2) {
  466. return { articleId: article_id, snippetId: id, reaction: result1 };
  467. }
  468. await idbKeyval.set(
  469. `${article_id}-${id}`,
  470. {
  471. articleId: article_id,
  472. snippetId: id,
  473. reaction: result2,
  474. createdTime: new Date().valueOf(),
  475. },
  476. dbTable.response2
  477. );
  478. return { articleId: article_id, snippetId: id, reaction: result2 };
  479. };
  480. }
  481.  
  482. async rawReactionProcess(rawReactionHTML) {
  483. const ele = document.createElement("div");
  484. ele.innerHTML = rawReactionHTML;
  485. const res = Array.from(ele.querySelectorAll("code"))
  486. .map((el) => el.innerText)
  487. .map((yml) => yaml2object(yml));
  488.  
  489. if (res && res.length > 0 && res.every((s) => s !== null)) {
  490. const result = reactionObjHandler(res);
  491. return result.length > 0 ? result : null;
  492. }
  493. return null;
  494. }
  495.  
  496. async skipSnippetHandler(articleId, snippetId) {
  497. const oldVal = await idbKeyval.get(
  498. `${articleId}-${snippetId}`,
  499. dbTable.skipSnippet
  500. );
  501. await idbKeyval.set(
  502. `${articleId}-${snippetId}`,
  503. (oldVal || 0) + 1,
  504. dbTable.skipSnippet
  505. );
  506. this.queue = this.queue.filter((item) => item.id !== snippetId);
  507. }
  508.  
  509. async saveRespond(respond) {
  510. const { articleId, snippetId } = respond;
  511. const currentTimeStamp = new Date().valueOf();
  512. const reactionProcessed = await this.rawReactionProcess(respond.reaction);
  513. if (!reactionProcessed) {
  514. console.warn(`${articleId}-${snippetId} 无法解析出 reaction, 即将跳过`);
  515. await this.skipSnippetHandler(articleId, snippetId);
  516. return;
  517. }
  518. this.responds.push({
  519. articleId,
  520. snippetId,
  521. createdTime: currentTimeStamp,
  522. });
  523. this.queue = this.queue.filter((item) => item.id !== snippetId);
  524.  
  525. await idbKeyval.set(
  526. `${articleId}-${snippetId}`,
  527. {
  528. ...respond,
  529. reaction: reactionProcessed,
  530. createdTime: new Date().valueOf(),
  531. },
  532. dbTable.responseProcessed
  533. );
  534. try {
  535. await idbKeyval.del(`${articleId}-${snippetId}`, dbTable.skipSnippet);
  536. } catch (err) {}
  537. if (this.responds.length && this.responds.length % 50 === 0) {
  538. this.handleDownload.bind(this)();
  539. }
  540. }
  541.  
  542. trigger(prompt, checkInterval = this.checkInterval) {
  543. return new Promise((resolve, reject) => {
  544. const textEl = document.querySelector(this.INPUT_SELECTOR);
  545. const submitEl = document.querySelector(this.SUBMIT_BTN_SELECTOR);
  546. textEl.value = prompt;
  547. textEl.dispatchEvent(new Event("input", { bubbles: true }));
  548. setTimeout(() => {
  549. submitEl.click();
  550.  
  551. let resCache = null;
  552. let checkOutputCount = 0;
  553. (async () => {
  554. while (true) {
  555. await this.sleep(checkInterval);
  556. const result = Array.from(
  557. document.querySelectorAll(this.RESPOND_SELECTOR)
  558. );
  559. const temp = result[result.length - 1];
  560. if (!temp) {
  561. if (checkOutputCount > 0) {
  562. console.log("检查结果超时");
  563. reject(null);
  564. break;
  565. }
  566. checkOutputCount++;
  567. continue;
  568. }
  569. if (resCache === temp.innerHTML) {
  570. // console.log("匹配,resCache:", resCache);
  571. const validateResult = await this.validate(resCache).catch(
  572. (err) => {
  573. reject(null);
  574. return;
  575. }
  576. );
  577. if (validateResult === true) {
  578. resolve(resCache);
  579. break;
  580. } else if (validateResult === false) {
  581. continue;
  582. }
  583. reject(null);
  584. break;
  585. }
  586. resCache = temp.innerHTML;
  587. console.log(`${checkInterval / 1000}s后再次检查结果`);
  588. }
  589. })();
  590. }, 4000);
  591. });
  592. }
  593.  
  594. async validate(innerHTML) {
  595. const buttons = document.querySelectorAll(
  596. this.NORMAL_RESPOND_BTN_SELECTOR
  597. );
  598. const errorBtn = document.querySelectorAll(
  599. this.ERROR_RESPOND_BTN_SELECTOR
  600. );
  601. // 如果触发gpt-4 3小时25次限制
  602. if (!buttons[0] && !errorBtn[0] && innerHTML.includes("usage cap")) {
  603. console.error("触发gpt-4 3小时25次限制,等待10min后重试");
  604. await this.sleep(10 * 60 * 1000);
  605. throw new Error("触发gpt-4 3小时25次限制");
  606. }
  607. // 如果openAI服务器报错未返回结果
  608. if (errorBtn[0]) {
  609. // && innerHTML.includes("wrong")) {
  610. if (this.retrying) {
  611. this.retrying = false;
  612. return true;
  613. }
  614. errorBtn[0].click();
  615. this.retrying = true;
  616. return false;
  617. }
  618. // 如果输出结果未包含code标签
  619. if (!innerHTML.includes("</code>")) {
  620. if (this.retrying) {
  621. this.retrying = false;
  622. console.error("第二次还是未输出yaml结构");
  623. throw new Error("未返回yaml结构");
  624. }
  625. console.error("未输出yaml结构,重试一次");
  626. buttons[0].click();
  627. this.retrying = true;
  628. return false;
  629. }
  630. this.retrying = false;
  631. // 如果还未完全输出
  632. if (
  633. buttons.length > 1 &&
  634. !buttons[buttons.length - 1].innerText.includes("Regenerate")
  635. ) {
  636. buttons[buttons.length - 1].click();
  637. return false;
  638. }
  639. return true;
  640. }
  641.  
  642. async main(sleepTime = 5000) {
  643. let emptyCount = 0;
  644. while (true) {
  645. // {0: gpt-3.5, 1: gpt-4}
  646. const modelNum = this.modelNum;
  647. const gpt4btn = document.querySelectorAll(
  648. "ul > li > button.cursor-pointer"
  649. )[modelNum];
  650.  
  651. if (gpt4btn) {
  652. console.log(`当前模型为:${gpt4btn.innerText}`);
  653. gpt4btn.firstChild.click();
  654. } else {
  655. console.warn(`无法选择模型,2分钟后刷新`);
  656. await this.sleep(2 * 60 * 1000);
  657. locationReload();
  658. }
  659. await this.sleep(sleepTime / 2);
  660. if (
  661. modelNum === 1 &&
  662. !(
  663. location.href.endsWith("gpt-4") ||
  664. gpt4btn.firstChild.className?.includes("shadow")
  665. )
  666. ) {
  667. console.log("未切换到gpt-4模式, 5分钟后重试");
  668. const maxTime = this._getLastRespondTime();
  669. const diff = new Date().valueOf() - maxTime;
  670. if (maxTime && diff > 1.5 * 60 * 60 * 1000) {
  671. console.warn("超时未刷新, 5分钟后刷新页面");
  672. await this.sleep(5 * 60 * 1000);
  673. locationReload();
  674. break;
  675. }
  676. this.report(
  677. `触发gpt-4 3小时25次限制,上次运行时间:${new Date(
  678. maxTime
  679. ).toLocaleString()}`
  680. );
  681. await this.sleep(5 * 60 * 1000);
  682. const newChatBtn = document.querySelector(
  683. this.NEW_CHART_BTN_SELECTOR
  684. );
  685. newChatBtn.click();
  686. continue;
  687. }
  688. const task = this.getTask();
  689. if (!task) {
  690. if (emptyCount > 0) {
  691. console.warn("连续两次未获取到任务,2分钟后刷新");
  692. await this.sleep(2 * 60 * 1000);
  693. locationReload();
  694. break;
  695. }
  696. emptyCount++;
  697. await this.sleep(5 * 60 * 1000);
  698. continue;
  699. }
  700.  
  701. const result = await task();
  702. if (result) {
  703. this.saveRespond(result);
  704. emptyCount = 0;
  705. } else {
  706. if (emptyCount > 0) {
  707. const task = this.queue[0];
  708. const { article_id, id } = task;
  709. console.warn(
  710. `${article_id}-${id}连续两次未获取到任务值,2分钟后刷新`
  711. );
  712. await this.skipSnippetHandler(article_id, id);
  713. await this.sleep(2 * 60 * 1000);
  714. locationReload();
  715. break;
  716. }
  717. emptyCount += 1;
  718. }
  719. console.log(`${sleepTime / 1000}s后将再次触发`);
  720. const newChatBtn = document.querySelector(this.NEW_CHART_BTN_SELECTOR);
  721. newChatBtn.click();
  722. await this.sleep(sleepTime / 2);
  723. this._updateDownloadBtnText();
  724. }
  725. }
  726. }
  727.  
  728. function secondInterval() {
  729. console.log("start secondInterval...");
  730. const sleep = (duration) => {
  731. return new Promise((resolve, reject) => {
  732. setTimeout(() => {
  733. resolve(true);
  734. }, duration);
  735. });
  736. };
  737. setInterval(async () => {
  738. const responds = await idbKeyval.values(dbTable.responseProcessed);
  739. const maxTime = Math.max.apply(
  740. null,
  741. responds
  742. .map((item) => item.createdTime)
  743. .filter((item) => item)
  744. .concat([0])
  745. );
  746. const diff = new Date().valueOf() - maxTime;
  747.  
  748. console.log(`last updated at: ${maxTime}, diff is ${diff}`);
  749. if (maxTime && diff > 30 * 60 * 1000) {
  750. console.warn("超时未刷新, 2分钟后刷新页面");
  751. await sleep(2 * 60 * 1000);
  752. locationReload();
  753. }
  754. }, 10 * 60 * 1000);
  755. }
  756.  
  757. function start() {
  758. const ACCOUNT_NAME_SELECTOR = "nav > div:last-child > div:last-child";
  759. const nameEl = document.querySelector(ACCOUNT_NAME_SELECTOR);
  760. const name = nameEl && nameEl.innerText;
  761. if (name) {
  762. new GPT_ASK_LOOP(name);
  763. secondInterval();
  764. } else {
  765. setTimeout(() => {
  766. start();
  767. }, 5000);
  768. }
  769. }
  770. start();
  771. })();