GPT Auto task

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

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

  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.22
  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.  
  23. constructor(account) {
  24. this.responds = JSON.parse(
  25. localStorage.getItem("reaction_responds") || "[]"
  26. );
  27. const queueCache = JSON.parse(localStorage.getItem("task_queue") || "[]");
  28. this.abstract = JSON.parse(localStorage.getItem("task_abstract") || "[]");
  29. const resSnippetIds = this.responds.map((respond) => respond.snippetId);
  30. this.queue = queueCache.filter((item) => !resSnippetIds.includes(item.id));
  31. this.account = account || Math.ceil(Math.random() * 1e10).toString(32);
  32. const btnWrap = document.createElement("div");
  33. 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>`;
  34. this.downloadBtn = btnWrap.querySelector("button");
  35. this.downloadBtn.onclick = this.handleDownload.bind(this);
  36. document.body.appendChild(btnWrap);
  37. this.main();
  38. }
  39.  
  40. handleDownload() {
  41. const respond = JSON.parse(
  42. localStorage.getItem("reaction_responds") || "[]"
  43. );
  44. if (!respond.length) {
  45. return;
  46. }
  47. const result = respond.map((item) => {
  48. const ele = document.createElement("div");
  49. ele.innerHTML = item.reaction;
  50. const res = Array.from(ele.querySelectorAll("code")).map(
  51. (el) => el.innerText
  52. );
  53. return { ...item, reaction: res };
  54. });
  55. const now = new Date();
  56. this.downloadFile(
  57. JSON.stringify(result),
  58. `${now.getFullYear()}-${now.getMonth() + 1
  59. }-${now.getDate()}-${now.getHours()}${now.getMinutes()}${now.getSeconds()}-${result.length}.json`
  60. );
  61. }
  62.  
  63. downloadFile(data, fileName) {
  64. const a = document.createElement("a");
  65. document.body.appendChild(a);
  66. a.style = "display: none";
  67. const blob = new Blob([data], {
  68. type: "application/octet-stream",
  69. });
  70. const url = window.URL.createObjectURL(blob);
  71. a.href = url;
  72. a.download = fileName;
  73. a.click();
  74. window.URL.revokeObjectURL(url);
  75. }
  76.  
  77. async report(tip = "") {
  78. await fetch("https://gpt-hit.deno.dev/api/update", {
  79. method: "POST",
  80. body: JSON.stringify({
  81. account: this.account,
  82. reaction_count: this.responds.length,
  83. queue_count: this.queue.length,
  84. tip: tip,
  85. }),
  86. }).catch((err) => {
  87. console.error({ err });
  88. });
  89. }
  90.  
  91. genPrompt(content, step = 1) {
  92. return step === 1 ? `${localStorage.getItem("mock_prompt"+step)}
  93.  
  94. ''' ${content} ''' ` : localStorage.getItem("mock_prompt"+step);
  95. }
  96.  
  97. getTask() {
  98. if (this.downloadBtn) {
  99. this.downloadBtn.innerText = `下载已生成结果(queue: ${this.queue.length}, res: ${this.responds.length})`;
  100. }
  101. const task = this.queue[0];
  102. this.report(task && `Working on articleId: ${task.article_id}, snippetId: ${task.id}` || "");
  103. if (!task) {
  104. console.log("任务队列为空");
  105. return () => null;
  106. }
  107. return async () => {
  108. const { article_id, id, content } = task;
  109. const relatedAbstract = this.abstract.find((item) => item.article_id === article_id)?.content || "";
  110. console.log(`开始触发 ${article_id}-${id}, ${new Date().toTimeString()}`);
  111. const promptContent = `
  112. ${relatedAbstract}
  113.  
  114. ${content}
  115. `
  116. const prompt1 = this.genPrompt(promptContent, 1);
  117. const prompt2 = this.genPrompt(promptContent, 2);
  118. const result1 = await this.trigger(prompt1).catch((err) => {
  119. return null
  120. });
  121. if (!result1) {
  122. return null;
  123. }
  124. await this.sleep(3 * 1000);
  125. const result2 = await this.trigger(prompt2).catch((err) => {
  126. return null
  127. });
  128. if (!result2) {
  129. return { articleId: article_id, snippetId: id, reaction: result1 };
  130. }
  131. return { articleId: article_id, snippetId: id, reaction: result2 };
  132. // console.log("result:", result);
  133. };
  134. }
  135.  
  136. saveRespond(respond) {
  137. const { snippetId } = respond;
  138. this.responds.push({...respond, createdTime: new Date().valueOf()});
  139. this.queue = this.queue.filter((item) => item.id !== snippetId);
  140. localStorage.setItem("task_queue", JSON.stringify(this.queue));
  141. localStorage.setItem("reaction_responds", JSON.stringify(this.responds));
  142. if (this.responds.length && ((this.responds.length % 10) === 0)) {
  143. this.handleDownload.bind(this)()
  144. }
  145. }
  146.  
  147. sleep(duration) {
  148. return new Promise((resolve, reject) => {
  149. setTimeout(() => {
  150. resolve(true);
  151. }, duration);
  152. });
  153. }
  154.  
  155. trigger(prompt, checkInterval = this.checkInterval) {
  156. return new Promise((resolve, reject) => {
  157. const textEl = document.querySelector("#prompt-textarea");
  158. const submitEl = document.querySelector("#prompt-textarea + button");
  159. textEl.value = prompt; //`你好, 帮我算下${Math.floor(Math.random() * 10000)}开平方的结果`;
  160. textEl.dispatchEvent(new Event("input", { bubbles: true }));
  161. setTimeout(() => {
  162. submitEl.click();
  163.  
  164. let resCache = null;
  165. let checkOutputCount = 0;
  166. (async () => {
  167. while (true) {
  168. await this.sleep(checkInterval);
  169. const result = Array.from(document.querySelectorAll("main .group"));
  170. const temp = result[result.length - 1];
  171. if (!temp) {
  172. if (checkOutputCount > 0) {
  173. console.log("检查结果超时");
  174. reject(null);
  175. break;
  176. }
  177. checkOutputCount++;
  178. continue;
  179. }
  180. if (resCache === temp.innerHTML) {
  181. // console.log("匹配,resCache:", resCache);
  182. const validateResult = await this.validate(resCache).catch(err => {
  183. reject(null);
  184. return;
  185. })
  186. if (validateResult === true) {
  187. resolve(resCache);
  188. break;
  189. } else if (validateResult === false) {
  190. continue;
  191. }
  192. reject(null);
  193. break;
  194. }
  195. resCache = temp.innerHTML;
  196. console.log(`${checkInterval / 1000}s后再次检查结果`);
  197. }
  198. })();
  199. }, 4000);
  200. });
  201. }
  202.  
  203. async validate(innerHTML) {
  204. const buttons = document.querySelectorAll("form div button.btn-neutral");
  205. const errorBtn = document.querySelectorAll("form div button.btn-primary");
  206. // 如果触发gpt-4 3小时25次限制
  207. if (!buttons[0] && !errorBtn[0] && innerHTML.includes("usage cap")) {
  208. console.error("触发gpt-4 3小时25次限制,等待10min后重试")
  209. await this.sleep(10 * 60 * 1000);
  210. throw new Error("触发gpt-4 3小时25次限制");
  211. }
  212. // 如果openAI服务器报错未返回结果
  213. if (errorBtn[0]) { // && innerHTML.includes("wrong")) {
  214. if (this.retrying) {
  215. this.retrying = false;
  216. return true;
  217. }
  218. errorBtn[0].click();
  219. this.retrying = true;
  220. return false;
  221. }
  222. // 如果输出结果未包含code标签
  223. if (!innerHTML.includes("</code>")) {
  224. if (this.retrying) {
  225. this.retrying = false;
  226. console.error("第二次还是未输出yaml结构")
  227. throw new Error("未返回yaml结构")
  228. }
  229. console.error("未输出yaml结构,重试一次")
  230. buttons[0].click();
  231. this.retrying = true;
  232. return false;
  233. }
  234. this.retrying = false;
  235. // 如果还未完全输出
  236. if (buttons.length > 1) {
  237. buttons[buttons.length - 1].click();
  238. return false;
  239. }
  240. return true;
  241. }
  242.  
  243. async main(sleepTime = 5000) {
  244. const emptyCount = 0;
  245. while (true) {
  246. // {0: gpt-3.5, 1: gpt-4, 2: gpt-4 mobile}
  247. const modelNum = +localStorage.getItem("model_number") || this.defaultMode;
  248. const gpt4btn = document.querySelectorAll("ul > li > button.cursor-pointer")[modelNum];
  249.  
  250. console.log(`当前模型为:${gpt4btn.innerText}`);
  251. if (gpt4btn) {
  252. gpt4btn.firstChild.click()
  253. }
  254. await this.sleep(sleepTime/2);
  255. if (modelNum===1 && !location.href.endsWith("gpt-4")) {
  256. console.log("未切换到gpt-4模式, 5分钟后重试");
  257. const maxTime = Math.max.apply(null, this.responds.map(item => item.createdTime).filter(item => item).concat([0]))
  258. const diff = new Date().valueOf() - maxTime;
  259. if (maxTime && diff > 1.5 * 60 * 60 * 1000) {
  260. console.log("超时未刷新, 5分钟后刷新页面");
  261. await this.sleep(5 * 60 * 1000);
  262. location.reload();
  263. break;
  264. }
  265. this.report(`触发gpt-4 3小时25次限制,上次运行时间:${new Date(maxTime).toLocaleString()}`);
  266. await this.sleep(5 * 60 * 1000);
  267. const newChatBtn = document.querySelector("nav>div.mb-1>a:first-child");
  268. newChatBtn.click();
  269. continue;
  270. }
  271. const task = this.getTask();
  272. if (!task) {
  273. if (emptyCount > 0) {
  274. console.log("连续两次未获取到任务,即将刷新");
  275. location.reload();
  276. break;
  277. }
  278. emptyCount++
  279. await this.sleep(5 * 60 * 1000);
  280. continue;
  281. }
  282.  
  283. const result = await task();
  284. if (result) {
  285. this.saveRespond(result);
  286. emptyCount = 0
  287. } else {
  288. if (emptyCount > 0) {
  289. console.log("连续两次未获取值");
  290. location.reload();
  291. break;
  292. }
  293. emptyCount++
  294. }
  295. console.log(`${sleepTime / 1000}s后将再次触发`);
  296. const newChatBtn = document.querySelector("nav>div.mb-1>a:first-child");
  297. newChatBtn.click();
  298. await this.sleep(sleepTime/2);
  299. }
  300. }
  301. }
  302.  
  303. function secondInterval() {
  304. console.log("start secondInterval...")
  305. setInterval(async () => {
  306. const responds = JSON.parse(
  307. localStorage.getItem("reaction_responds") || "[]"
  308. );
  309. const maxTime = Math.max.apply(null, responds.map(item => item.createdTime).filter(item => item).concat([0]))
  310. const diff = new Date().valueOf() - maxTime;
  311.  
  312. console.log(`last updated at: ${maxTime}, diff is ${diff}`)
  313. if (maxTime && (diff > 30 * 60 * 1000)) {
  314. console.log("超时未刷新, 5分钟后刷新页面");
  315. location.reload();
  316. }
  317. }, 10*60*1000)
  318. }
  319.  
  320. function start() {
  321. const nameEl = document.querySelector(
  322. "nav > div:last-child > div:last-child"
  323. )
  324. const name = nameEl && nameEl.innerText;
  325. if (name) {
  326. new GPT_ASK_LOOP(name);
  327. secondInterval();
  328. } else {
  329. setTimeout(() => {
  330. start();
  331. }, 5000);
  332. }
  333. }
  334. start();
  335.  
  336. })();