GPT Auto task

自动在网页上与chat gpt对话

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

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