GPT Auto task

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

目前為 2023-06-27 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @namespace https://greasyfork.org/zh-CN/users/1106595-ikkem-lin
  3. // @name GPT Auto task
  4. // @author Mark
  5. // @description 根据缓存中的task_queue自动在网页上与chat gpt对话
  6. // @homepageURL https://github.com/IKKEM-Lin/gpt-auto-task
  7. // @version 0.0.10
  8. // @match *chat.openai.com/*
  9. // @run-at document-idle
  10. // ==/UserScript==
  11. (function () {
  12. "use strict";
  13. class GPT_ASK_LOOP {
  14. queue = [];
  15. abstract = [];
  16. responds = [];
  17. checkInterval = 20000;
  18. account = "";
  19. downloadBtn = null;
  20. retrying = false;
  21. defaultMode = 2;
  22. constructor(account) {
  23. this.responds = JSON.parse(
  24. localStorage.getItem("reaction_responds") || "[]"
  25. );
  26. const queueCache = JSON.parse(localStorage.getItem("task_queue") || "[]");
  27. this.abstract = JSON.parse(localStorage.getItem("task_abstract") || "[]");
  28. const resSnippetIds = this.responds.map((respond) => respond.snippetId);
  29. this.queue = queueCache.filter((item) => !resSnippetIds.includes(item.id));
  30. this.account = account || Math.ceil(Math.random() * 1e10).toString(32);
  31. const btnWrap = document.createElement("div");
  32. 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>`;
  33. this.downloadBtn = btnWrap.querySelector("button");
  34. this.downloadBtn.onclick = this.handleDownload.bind(this);
  35. document.body.appendChild(btnWrap);
  36. this.main();
  37. }
  38. handleDownload() {
  39. const respond = JSON.parse(
  40. localStorage.getItem("reaction_responds") || "[]"
  41. );
  42. if (!respond.length) {
  43. return;
  44. }
  45. const result = respond.map((item) => {
  46. const ele = document.createElement("div");
  47. ele.innerHTML = item.reaction;
  48. const res = Array.from(ele.querySelectorAll("code")).map(
  49. (el) => el.innerText
  50. );
  51. return { ...item, reaction: res };
  52. });
  53. const now = new Date();
  54. this.downloadFile(
  55. JSON.stringify(result),
  56. `${now.getFullYear()}-${now.getMonth() + 1
  57. }-${now.getDate()}-${now.getHours()}${now.getMinutes()}${now.getSeconds()}.json`
  58. );
  59. }
  60. downloadFile(data, fileName) {
  61. const a = document.createElement("a");
  62. document.body.appendChild(a);
  63. a.style = "display: none";
  64. const blob = new Blob([data], {
  65. type: "application/octet-stream",
  66. });
  67. const url = window.URL.createObjectURL(blob);
  68. a.href = url;
  69. a.download = fileName;
  70. a.click();
  71. window.URL.revokeObjectURL(url);
  72. }
  73. async report() {
  74. await fetch("https://gpt-hit.deno.dev/api/update", {
  75. method: "POST",
  76. body: JSON.stringify({
  77. account: this.account,
  78. reaction_count: this.responds.length,
  79. queue_count: this.queue.length,
  80. }),
  81. }).catch((err) => {
  82. console.error({ err });
  83. });
  84. }
  85. genPrompt(content, step = 1) {
  86. return step === 1 ? `${localStorage.getItem("mock_prompt"+step)}
  87. ''' ${content} ''' ` : localStorage.getItem("mock_prompt"+step);
  88. }
  89. getTask() {
  90. this.report();
  91. if (this.downloadBtn) {
  92. this.downloadBtn.innerText = `下载已生成结果(queue: ${this.queue.length}, res: ${this.responds.length})`;
  93. }
  94. const task = this.queue[0];
  95. if (!task) {
  96. console.log("任务队列为空");
  97. return;
  98. }
  99. return async () => {
  100. const { article_id, id, content } = task;
  101. const relatedAbstract = this.abstract.find((item) => item.article_id === article_id)?.content || "";
  102. console.log(`开始触发 ${article_id}-${id}, ${new Date().toTimeString()}`);
  103. const promptContent = `
  104. ${relatedAbstract}
  105.  
  106. ${content}
  107. `
  108. const prompt1 = this.genPrompt(promptContent, 1);
  109. const prompt2 = this.genPrompt(promptContent, 2);
  110. const result1 = await this.trigger(prompt1).catch((err) => {
  111. return null
  112. });
  113. if (!result1) {
  114. return null;
  115. }
  116. const result2 = await this.trigger(prompt2).catch((err) => {
  117. return null
  118. });
  119. if (!result2) {
  120. return { articleId: article_id, snippetId: id, reaction: result1 };
  121. }
  122. return { articleId: article_id, snippetId: id, reaction: result2 };
  123. // console.log("result:", result);
  124. };
  125. }
  126. saveRespond(respond) {
  127. const { snippetId } = respond;
  128. this.responds.push(respond);
  129. this.queue = this.queue.filter((item) => item.id !== snippetId);
  130. localStorage.setItem("task_queue", JSON.stringify(this.queue));
  131. localStorage.setItem("reaction_responds", JSON.stringify(this.responds));
  132. }
  133. sleep(duration) {
  134. return new Promise((resolve, reject) => {
  135. setTimeout(() => {
  136. resolve(true);
  137. }, duration);
  138. });
  139. }
  140. trigger(prompt, checkInterval = this.checkInterval) {
  141. return new Promise((resolve, reject) => {
  142. const textEl = document.querySelector("#prompt-textarea");
  143. const submitEl = document.querySelector("#prompt-textarea + button");
  144. textEl.value = prompt; //`你好, 帮我算下${Math.floor(Math.random() * 10000)}开平方的结果`;
  145. textEl.dispatchEvent(new Event("input", { bubbles: true }));
  146. setTimeout(() => {
  147. submitEl.click();
  148. let resCache = null;
  149. (async () => {
  150. while (true) {
  151. await this.sleep(checkInterval);
  152. const result = Array.from(document.querySelectorAll("main .group"));
  153. const temp = result[result.length - 1];
  154. if (!temp) {
  155. continue;
  156. }
  157. if (resCache === temp.innerHTML) {
  158. // console.log("匹配,resCache:", resCache);
  159. const validateResult = await this.validate(resCache).catch(err => {
  160. reject(null);
  161. return;
  162. })
  163. if (validateResult === true) {
  164. resolve(resCache);
  165. break;
  166. } else if (validateResult === false) {
  167. continue;
  168. }
  169. reject(null);
  170. break;
  171. }
  172. resCache = temp.innerHTML;
  173. console.log(`${checkInterval / 1000}s后再次检查结果`);
  174. }
  175. })();
  176. }, 4000);
  177. });
  178. }
  179. async validate(innerHTML) {
  180. const buttons = document.querySelectorAll("form div button.btn-neutral");
  181. const errorBtn = document.querySelectorAll("form div button.btn-primary");
  182. // 如果触发gpt-4 3小时25次限制
  183. if (!buttons[0] && !errorBtn[0] && innerHTML.includes("usage cap")) {
  184. console.error("触发gpt-4 3小时25次限制,等待10min后重试")
  185. await this.sleep(10 * 60 * 1000);
  186. throw new Error("触发gpt-4 3小时25次限制");
  187. }
  188. // 如果openAI服务器报错未返回结果
  189. if (errorBtn[0] && innerHTML.includes("wrong")) {
  190. if (this.retrying) {
  191. this.retrying = false;
  192. return true;
  193. }
  194. errorBtn[0].click();
  195. this.retrying = true;
  196. return false;
  197. }
  198. // 如果输出结果未包含code标签
  199. if (!innerHTML.includes("</code>")) {
  200. if (this.retrying) {
  201. this.retrying = false;
  202. return true;
  203. }
  204. console.error("未输出yaml结构,重试一次")
  205. buttons[0].click();
  206. this.retrying = true;
  207. return false;
  208. }
  209. this.retrying = false;
  210. // 如果还未完全输出
  211. if (buttons.length > 1) {
  212. buttons[buttons.length - 1].click();
  213. return false;
  214. }
  215. return true;
  216. }
  217. async main(sleepTime = 5000) {
  218. while (true) {
  219. // {0: gpt-3.5, 1: gpt-4, 2: gpt-4 mobile}
  220. const modelNum = +localStorage.getItem("model_number") || this.defaultMode;
  221. const gpt4btn = document.querySelectorAll("ul > li > button.cursor-pointer")[modelNum];
  222. console.log(`当前模型为:${gpt4btn.innerText}`);
  223. if (gpt4btn) {
  224. gpt4btn.firstChild.click()
  225. }
  226. await this.sleep(sleepTime/2);
  227. if (modelNum===1 && !location.href.endsWith("gpt-4")) {
  228. console.log("未切换到gpt-4模式, 5分钟后重试");
  229. await this.sleep(5 * 60 * 1000);
  230. const newChatBtn = document.querySelector("nav>div.mb-1>a:first-child");
  231. newChatBtn.click();
  232. continue;
  233. }
  234. const task = this.getTask();
  235. if (!task) {
  236. await this.sleep(5 * 60 * 1000);
  237. continue;
  238. }
  239. const result = await task();
  240. if (result) {
  241. this.saveRespond(result);
  242. }
  243. console.log(`${sleepTime / 1000}s后将再次触发`);
  244. const newChatBtn = document.querySelector("nav>div.mb-1>a:first-child");
  245. newChatBtn.click();
  246. await this.sleep(sleepTime/2);
  247. }
  248. }
  249. }
  250. function start() {
  251. const nameEl = document.querySelector(
  252. "nav > div:last-child > div:last-child"
  253. )
  254. const name = nameEl && nameEl.innerText;
  255. if (name) {
  256. new GPT_ASK_LOOP(name);
  257. } else {
  258. setTimeout(() => {
  259. start();
  260. }, 5000);
  261. }
  262. }
  263. start();
  264. })();