// ==UserScript== // @name mathtrainer.ai auto solve // @namespace http://tampermonkey.net/ // @version 1 // @description :p // @author elias // @match https://mathtrainer.ai/* // @grant none // ==/UserScript== // made by elias @ 15 sep. 2025 (function() { 'use strict'; let lastExpression = ''; let isProcessing = false; let InputEvents = {}; function hook(element) { element.addEventListener = new Proxy(element.addEventListener, { apply(target, _this, args) { const [type, listener] = args; if (_this.tagName === 'SECTION' && _this.classList.contains('answer')) { InputEvents[type] = listener; } return target.apply(_this, args); } }); } hook(HTMLElement.prototype); function keypress(target, key) { const event = new KeyboardEvent('keydown', { key: key, code: key.length === 1 ? `Digit${key}` : (key === 'Enter' ? 'Enter' : 'Unknown'), bubbles: true, cancelable: true }); if (InputEvents.keydown) { InputEvents.keydown({ target: target, key: key, type: 'keydown', preventDefault: () => {}, stopPropagation: () => {} }); } target.dispatchEvent(event); } function evaluateMath(expression) { if (!expression) return null; expression = expression .replace(/\\times/g, '*') .replace(/×/g, '*') .replace(/÷/g, '/') .replace(/−/g, '-') .replace(/\\div/g, '/') .replace(/\\sqrt\{(\d+)\}/g, 'Math.sqrt($1)') .replace(/\\sqrt\[(\d+)\]\{(\d+)\}/g, 'Math.pow($2, 1/$1)') .replace(/\^/g, '**') .replace(/\s+/g, ''); try { const result = Function('"use strict"; return (' + expression + ')')(); if (Number.isFinite(result)) { if (Math.abs(result - Math.round(result)) < 1e-10) { return Math.round(result); // round near-integers (39.99999.. -> 40) } return result; } return null; } catch (e) { return null; } } function randomDelay(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } async function inputAnswer(answerSection, result) { const resultStr = String(result); await new Promise(resolve => setTimeout(resolve, randomDelay(300, 500))); for (let i = 0; i < resultStr.length; i++) { keypress(answerSection, resultStr[i]); if (i < resultStr.length - 1) { await new Promise(resolve => setTimeout(resolve, randomDelay(300, 500))); } } await new Promise(resolve => setTimeout(resolve, randomDelay(300, 500))); } async function processQuestion() { if (isProcessing) return; isProcessing = true; try { const annotation = document.querySelector('annotation[encoding="application/x-tex"]'); if (!annotation) { isProcessing = false; return; } const expression = annotation.textContent.trim(); if (!expression || expression === lastExpression) { isProcessing = false; return; } lastExpression = expression; const result = evaluateMath(expression); if (result === null) { isProcessing = false; return; } // console.log("Detected:", expression); // console.log("Answer:", result); const answerSection = document.querySelector('section.answer'); if (!answerSection) { isProcessing = false; return; } await inputAnswer(answerSection, result); // keypress(answerSection, 'Enter'); // not needed as it gets auto-entered isProcessing = false; } catch (e) { isProcessing = false; } } const observer = new MutationObserver(() => { processQuestion(); }); observer.observe(document.body, { childList: true, subtree: true, attributes: true }); // sometimes observer doesnt catch it :( // use interval loop as fallback setInterval(() => { if (!isProcessing) { processQuestion(); } }, 500); })();