IXL Auto Answer (OpenAI API Requid)

Sends HTML and canvas data to GPT-4o for math problem solving with enhanced accuracy, GUI, and auto-answering functionality

当前为 2024-11-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name IXL Auto Answer (OpenAI API Requid)
  3. // @namespace http://tampermonkey.net/
  4. // @version 5.5
  5. // @license CC-BY NC
  6. // @description Sends HTML and canvas data to GPT-4o for math problem solving with enhanced accuracy, GUI, and auto-answering functionality
  7. // @match https://ca.ixl.com/*
  8. // @grant GM_xmlhttpRequest
  9. // @grant GM_addStyle
  10. // ==/UserScript==
  11. (function() {
  12. 'use strict';
  13. let API_KEY = localStorage.getItem("gpt4o-api-key") || ""; // Load API key from storage
  14. const API_URL = "https://api.openai.com/v1/chat/completions";
  15. let selectedModel = "gpt-4o";
  16. let autoAnswerModeEnabled = false;
  17. let autoSubmitEnabled = false;
  18. let language = localStorage.getItem("gpt4o-language") || "en"; // Default to English and load stored language
  19. // Prompt for API key if not set
  20. if (!API_KEY) {
  21. API_KEY = prompt("Please enter your OpenAI API key:");
  22. if (API_KEY) {
  23. localStorage.setItem("gpt4o-api-key", API_KEY); // Save API key
  24. } else {
  25. alert("API key is required to use this tool.");
  26. return;
  27. }
  28. }
  29. // Text content for different languages
  30. const langText = {
  31. en: {
  32. startAnswering: "Start Answering",
  33. autoAnsweringMode: "Enable Auto Answer Mode",
  34. autoSubmit: "Enable Auto Submit",
  35. language: "Language",
  36. statusWaiting: "Status: Waiting for input",
  37. statusFetching: "Status: Retrieving HTML structure...",
  38. statusSubmitting: "Status: Code executed",
  39. logLanguageSet: "Language set to",
  40. logModelSwitch: "Model switched to",
  41. logAutoAnswer: "Auto answer mode is",
  42. logAutoSubmit: "Auto submit is",
  43. logCanvasDetected: "Detected canvas element, capturing image...",
  44. logGeneratedCode: "Generated code",
  45. logExecutionError: "Execution error",
  46. logAnswerSubmitted: "Answer submitted automatically",
  47. logSubmitNotFound: "Submit button not found",
  48. logHtmlFetched: "Captured HTML structure",
  49. closeButton: "Close",
  50. },
  51. zh: {
  52. startAnswering: "开始答题",
  53. autoAnsweringMode: "启用自动答题模式",
  54. autoSubmit: "启用自动提交",
  55. language: "语言",
  56. statusWaiting: "状态:等待输入",
  57. statusFetching: "状态:获取 HTML 结构...",
  58. statusSubmitting: "状态:代码已执行",
  59. logLanguageSet: "语言设置为",
  60. logModelSwitch: "模型切换为",
  61. logAutoAnswer: "自动答题模式已",
  62. logAutoSubmit: "自动提交已",
  63. logCanvasDetected: "检测到画布元素,正在捕获图像...",
  64. logGeneratedCode: "生成的代码",
  65. logExecutionError: "执行错误",
  66. logAnswerSubmitted: "答案已自动提交",
  67. logSubmitNotFound: "未找到提交按钮",
  68. logHtmlFetched: "获取的 HTML 结构",
  69. closeButton: "关闭",
  70. }
  71. };
  72. const panel = document.createElement('div');
  73. panel.id = "gpt4o-panel";
  74. panel.innerHTML = `
  75. <div id="gpt4o-header" style="cursor: move; padding: 5px; background-color: #4CAF50; color: white;">
  76. GPT-4o Answer Assistant
  77. <button id="close-button" style="float: right; background-color: #d9534f; color: white; border: none; padding: 2px 6px; cursor: pointer;">${langText[language].closeButton}</button>
  78. </div>
  79. <div style="padding: 10px;">
  80. <button id="start-answering">${langText[language].startAnswering}</button>
  81. <label>
  82. <input type="radio" name="model" value="gpt-4o" checked> GPT-4o
  83. </label>
  84. <label>
  85. <input type="radio" name="model" value="gpt-4o-mini"> GPT-4o-mini
  86. </label>
  87. <label style="display: block; margin-top: 10px;">
  88. <input type="checkbox" id="auto-answer-mode-toggle"> ${langText[language].autoAnsweringMode}
  89. </label>
  90. <label style="display: block; margin-top: 10px;">
  91. <input type="checkbox" id="auto-submit-toggle"> ${langText[language].autoSubmit}
  92. </label>
  93. <label style="display: block; margin-top: 10px;">
  94. ${langText[language].language}:
  95. <select id="language-select">
  96. <option value="en" ${language === "en" ? "selected" : ""}>English</option>
  97. <option value="zh" ${language === "zh" ? "selected" : ""}>中文</option>
  98. </select>
  99. </label>
  100. <p id="status" style="color: green;">${langText[language].statusWaiting}</p>
  101. <div id="log" style="font-size: 12px; color: #333; max-height: 300px; overflow-y: auto; border-top: 1px solid #ccc; margin-top: 10px; padding-top: 5px;"></div>
  102. </div>
  103. `;
  104. document.body.appendChild(panel);
  105. // Make the panel draggable
  106. function makeDraggable(element) {
  107. let posX = 0, posY = 0, initX = 0, initY = 0;
  108. const header = document.getElementById("gpt4o-header");
  109. header.onmousedown = function(e) {
  110. e.preventDefault();
  111. initX = e.clientX;
  112. initY = e.clientY;
  113. document.onmouseup = closeDrag;
  114. document.onmousemove = drag;
  115. };
  116. function drag(e) {
  117. e.preventDefault();
  118. posX = initX - e.clientX;
  119. posY = initY - e.clientY;
  120. initX = e.clientX;
  121. initY = e.clientY;
  122. element.style.top = (element.offsetTop - posY) + "px";
  123. element.style.left = (element.offsetLeft - posX) + "px";
  124. element.style.pointerEvents = "auto";
  125. }
  126. function closeDrag() {
  127. document.onmouseup = null;
  128. document.onmousemove = null;
  129. }
  130. }
  131. makeDraggable(panel);
  132. document.getElementById("close-button").addEventListener("click", function() {
  133. panel.style.display = "none";
  134. });
  135. document.getElementById("language-select").addEventListener("change", function() {
  136. language = this.value;
  137. localStorage.setItem("gpt4o-language", language);
  138. updateTextContent();
  139. logMessage(`${langText[language].logLanguageSet}: ${language}`);
  140. });
  141. function updateTextContent() {
  142. document.getElementById("start-answering").textContent = langText[language].startAnswering;
  143. document.getElementById("auto-answer-mode-toggle").nextSibling.textContent = langText[language].autoAnsweringMode;
  144. document.getElementById("auto-submit-toggle").nextSibling.textContent = langText[language].autoSubmit;
  145. document.getElementById("close-button").textContent = langText[language].closeButton;
  146. document.getElementById("status").textContent = langText[language].statusWaiting;
  147. }
  148. function logMessage(message) {
  149. const logDiv = document.getElementById('log');
  150. logDiv.innerHTML += `<p>${message}</p>`;
  151. logDiv.scrollTop = logDiv.scrollHeight;
  152. }
  153. function sanitizeCode(codeString) {
  154. return codeString.replace(/^```(?:js|javascript)?\s*/i, "").replace(/```$/i, "").trim();
  155. }
  156. // Capture canvas element if present in the question
  157. function captureCanvasImage(htmlElement) {
  158. const canvas = htmlElement.querySelector('canvas');
  159. if (canvas) {
  160. logMessage(langText[language].logCanvasDetected);
  161. const offscreenCanvas = document.createElement('canvas');
  162. offscreenCanvas.width = canvas.width;
  163. offscreenCanvas.height = canvas.height;
  164. const ctx = offscreenCanvas.getContext('2d');
  165. ctx.drawImage(canvas, 0, 0);
  166. return offscreenCanvas.toDataURL("image/png").split(",")[1];
  167. }
  168. return null;
  169. }
  170. function sendContentToGPT(htmlContent, canvasDataUrl) {
  171. const requestPayload = {
  172. model: selectedModel,
  173. messages: [
  174. {
  175. "role": "system",
  176. "content": "You are a math assistant. Carefully analyze the HTML structure and canvas (if provided) to produce executable JavaScript code that fills all required fields based on the question's context. Use only stable selectors like xpath, and ensure each field is filled correctly without explanations or comments."
  177. },
  178. {
  179. "role": "user",
  180. "content": `This is a math question. Use the HTML structure provided to generate JavaScript code that fills each answer field without leaving any fields empty.\n\nHTML Structure:\n${htmlContent}`
  181. }
  182. ]
  183. };
  184. if (canvasDataUrl) {
  185. requestPayload["image"] = canvasDataUrl;
  186. }
  187. GM_xmlhttpRequest({
  188. method: "POST",
  189. url: API_URL,
  190. headers: {
  191. "Content-Type": "application/json",
  192. "Authorization": `Bearer ${API_KEY}`
  193. },
  194. data: JSON.stringify(requestPayload),
  195. onload: function(response) {
  196. if (response.status === 200) {
  197. let data = JSON.parse(response.responseText);
  198. let code = sanitizeCode(data.choices[0].message.content.trim());
  199. document.getElementById('status').innerText = langText[language].statusSubmitting;
  200. logMessage(`${langText[language].logGeneratedCode}: ${code}`);
  201. try {
  202. eval(code);
  203. if (autoSubmitEnabled) submitAnswer();
  204. } catch (error) {
  205. document.getElementById('status').innerText = langText[language].logExecutionError;
  206. logMessage(`${langText[language].logExecutionError}: ${error.message}`);
  207. }
  208. } else {
  209. document.getElementById('status').innerText = "Status: GPT request failed";
  210. logMessage("GPT request error, status code: " + response.status);
  211. }
  212. },
  213. onerror: function(error) {
  214. document.getElementById('status').innerText = "Status: Request error";
  215. logMessage("Request error: " + error);
  216. }
  217. });
  218. }
  219. function submitAnswer() {
  220. const submitButton = document.evaluate('/html/body/main/div/article/section/section/div/div[1]/section/div/section/div/button', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  221. if (submitButton) {
  222. submitButton.click();
  223. logMessage(langText[language].logAnswerSubmitted);
  224. } else {
  225. logMessage(langText[language].logSubmitNotFound);
  226. }
  227. }
  228. function answerQuestion() {
  229. document.getElementById('status').innerText = langText[language].statusFetching;
  230. logMessage(langText[language].logHtmlFetched);
  231. let targetDiv = document.evaluate('/html/body/main/div/article/section/section/div/div[1]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  232. if (!targetDiv) {
  233. document.getElementById('status').innerText = langText[language].statusFetching;
  234. logMessage("Error: HTML structure not found, check XPath.");
  235. return;
  236. }
  237. let htmlContent = targetDiv.outerHTML;
  238. const canvasDataUrl = captureCanvasImage(targetDiv);
  239. sendContentToGPT(htmlContent, canvasDataUrl);
  240. }
  241. function monitorNewQuestions() {
  242. const observer = new MutationObserver(() => {
  243. if (autoAnswerModeEnabled) {
  244. logMessage("New question detected, attempting to answer...");
  245. answerQuestion();
  246. }
  247. });
  248. const targetNode = document.querySelector("main");
  249. if (targetNode) {
  250. observer.observe(targetNode, { childList: true, subtree: true });
  251. }
  252. }
  253. document.getElementById('auto-answer-mode-toggle').addEventListener('change', function() {
  254. autoAnswerModeEnabled = this.checked;
  255. logMessage(`${langText[language].logAutoAnswer} ${autoAnswerModeEnabled ? 'enabled' : 'disabled'}`);
  256. if (autoAnswerModeEnabled) {
  257. monitorNewQuestions();
  258. }
  259. });
  260. document.getElementById('auto-submit-toggle').addEventListener('change', function() {
  261. autoSubmitEnabled = this.checked;
  262. logMessage(`${langText[language].logAutoSubmit} ${autoSubmitEnabled ? 'enabled' : 'disabled'}`);
  263. });
  264. document.getElementById('start-answering').addEventListener('click', function() {
  265. answerQuestion();
  266. });
  267. GM_addStyle(`
  268. #gpt4o-panel {
  269. font-family: Arial, sans-serif;
  270. font-size: 14px;
  271. width: 300px;
  272. border-radius: 5px;
  273. box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3); /* Set semi-transparent shadow */
  274. position: absolute;
  275. top: 10px;
  276. right: 10px;
  277. z-index: 10000; /* Ensure top layer */
  278. background-color: rgba(255, 255, 255, 0.9); /* Semi-transparent background */
  279. }
  280. #gpt4o-panel button {
  281. background-color: #4CAF50;
  282. color: white;
  283. border: none;
  284. padding: 8px 16px;
  285. font-size: 14px;
  286. cursor: pointer;
  287. border-radius: 5px;
  288. }
  289. #gpt4o-panel button:hover {
  290. background-color: #45a049;
  291. }
  292. #log {
  293. font-family: monospace;
  294. }
  295. `);
  296. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址