Universal Math CAPTCHA Solver

Solves ALL basic math CAPTCHAs (+, -, *, /) in any format

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         Universal Math CAPTCHA Solver
// @namespace    http://tampermonkey.net/
// @version      3.0
// @description  Solves ALL basic math CAPTCHAs (+, -, *, /) in any format
// @author       ChrisN40
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const config = {
        checkInterval: 1000,
        maxAttempts: 3,
        retryDelay: 1000,
        mathPatterns: [
            // Standard equation formats
            /(?:solve|calculate|what is|answer)\s*(?:the )?(?:following )?(?:math )?(?:problem|function|question)?[:]?\s*([\d+\-*\/=. ]+)/i,
            /([\d]+\s*[\+\-\*\/]\s*[\d]+)/,

            // Word problem formats
            /first number is (\d+)[^\d]+(\d+)[^\d]+([\+\-\*\/])/i,
            /sum of (\d+) and (\d+)/i,
            /add (\d+) (?:and|to) (\d+)/i,
            /subtract (\d+) (?:from|and) (\d+)/i,
            /product of (\d+) and (\d+)/i,
            /multiply (\d+) (?:and|by) (\d+)/i,
            /divide (\d+) (?:by|and) (\d+)/i,
            /(\d+) plus (\d+)/i,
            /(\d+) minus (\d+)/i,
            /(\d+) times (\d+)/i,
            /(\d+) multiplied by (\d+)/i,
            /(\d+) divided by (\d+)/i,

            // Special cases
            /how much is (\d+) ([\+\-\*\/]) (\d+)/i
        ]
    };

    class UniversalMathSolver {
        constructor() {
            this.attempts = new WeakMap();
            this.init();
        }

        init() {
            console.log('Universal Math Solver initialized');
            this.setupMutationObserver();
            this.checkMathCaptchas();
            setInterval(() => this.checkMathCaptchas(), config.checkInterval);
        }

        setupMutationObserver() {
            const observer = new MutationObserver(mutations => {
                mutations.forEach(mutation => {
                    mutation.addedNodes.forEach(node => {
                        if (node.nodeType === 1 && this.isMathCaptcha(node.textContent)) {
                            this.processMathElement(node);
                        }
                    });
                });
            });

            observer.observe(document.body, {
                childList: true,
                subtree: true,
                characterData: true
            });
        }

        checkMathCaptchas() {
            const textElements = document.querySelectorAll('div, p, span, label, td, li, b, strong, h1, h2, h3, h4, h5, h6');

            textElements.forEach(element => {
                if (this.shouldProcessElement(element)) {
                    this.processMathElement(element);
                }
            });
        }

        shouldProcessElement(element) {
            if (!this.attempts.has(element)) {
                this.attempts.set(element, 0);
            }
            return this.attempts.get(element) < config.maxAttempts &&
                   this.isMathCaptcha(element.textContent);
        }

        isMathCaptcha(text) {
            if (!text) return false;
            return config.mathPatterns.some(pattern => pattern.test(text));
        }

        processMathElement(element) {
            const attempts = this.attempts.get(element);
            this.attempts.set(element, attempts + 1);

            try {
                const mathProblem = this.extractMathProblem(element.textContent);
                if (mathProblem) {
                    const solution = this.solveMathProblem(mathProblem);
                    if (solution !== null) {
                        this.fillSolution(solution, element);
                    }
                }
            } catch (error) {
                console.error('Math CAPTCHA solve error:', error);
                if (attempts < config.maxAttempts - 1) {
                    setTimeout(() => this.processMathElement(element), config.retryDelay);
                }
            }
        }

        extractMathProblem(text) {
            for (const pattern of config.mathPatterns) {
                const match = text.match(pattern);
                if (match) {
                    // Handle word problems
                    if (match[1] && match[2] && match[3]) {
                        const num1 = parseFloat(match[1]);
                        const num2 = parseFloat(match[2]);
                        const operator = this.normalizeOperator(match[3]);

                        // Handle subtraction word order ("subtract A from B" = B - A)
                        if (match[0].includes('subtract') && operator === '-') {
                            return `${num2}${operator}${num1}`;
                        }
                        return `${num1}${operator}${num2}`;
                    }
                    // Handle standard equations
                    else if (match[1]) {
                        return match[1].replace(/\s/g, '');
                    }
                }
            }
            return null;
        }

        normalizeOperator(op) {
            const opMap = {
                'plus': '+',
                'add': '+',
                'sum': '+',
                'minus': '-',
                'subtract': '-',
                'times': '*',
                'multiplied': '*',
                'product': '*',
                'divide': '/',
                'divided': '/'
            };
            return opMap[op.toLowerCase()] || op;
        }

        solveMathProblem(problem) {
            try {
                // Clean and validate the expression
                const cleanExpr = problem.replace(/[^\d+\-*\/.]/g, '');
                if (!/^[\d+\-*\/.]+$/.test(cleanExpr)) return null;

                // Split into tokens (numbers and operators)
                const tokens = cleanExpr.split(/([\+\-\*\/])/).filter(x => x);

                // Convert to numbers and operators
                const elements = [];
                for (const token of tokens) {
                    if (['+', '-', '*', '/'].includes(token)) {
                        elements.push({ type: 'operator', value: token });
                    } else {
                        elements.push({ type: 'number', value: parseFloat(token) });
                    }
                }

                // First pass: multiplication and division
                for (let i = 0; i < elements.length; i++) {
                    const el = elements[i];
                    if (el.type === 'operator' && (el.value === '*' || el.value === '/')) {
                        const left = elements[i-1].value;
                        const right = elements[i+1].value;
                        let result;

                        if (el.value === '*') {
                            result = left * right;
                        } else {
                            if (right === 0) return '∞'; // Division by zero
                            result = left / right;
                        }

                        // Replace the three elements (left, op, right) with result
                        elements.splice(i-1, 3, { type: 'number', value: result });
                        i -= 2; // Adjust index after replacement
                    }
                }

                // Second pass: addition and subtraction
                let result = elements[0].value;
                for (let i = 1; i < elements.length; i += 2) {
                    const operator = elements[i].value;
                    const number = elements[i+1].value;

                    if (operator === '+') {
                        result += number;
                    } else {
                        result -= number;
                    }
                }

                // Round to 2 decimal places if needed
                return result % 1 === 0 ? result : parseFloat(result.toFixed(2));
            } catch (e) {
                console.error('Math solving failed:', e);
                return null;
            }
        }

        fillSolution(solution, referenceElement) {
            const input = this.findInputField(referenceElement);
            if (!input) return false;

            // Try different methods to set the value
            const methods = [
                () => { input.value = solution; return input.value == solution; },
                () => {
                    input.focus();
                    input.value = solution;
                    this.triggerEvents(input);
                    return input.value == solution;
                },
                () => {
                    const setter = Object.getOwnPropertyDescriptor(
                        HTMLInputElement.prototype, 'value').set;
                    setter.call(input, solution);
                    this.triggerEvents(input);
                    return input.value == solution;
                }
            ];

            for (const method of methods) {
                try {
                    if (method()) {
                        console.log(`Solved: ${solution}`);
                        return true;
                    }
                } catch (e) {
                    console.debug('Method failed:', e);
                }
            }
            return false;
        }

        findInputField(referenceElement) {
            const selectors = [
                'input[type="text"]',
                'input[type="number"]',
                'input:not([type])',
                'textarea',
                '#answer',
                '#solution',
                '#captcha',
                '#mathAnswer'
            ];

            // Check in parent containers
            let container = referenceElement;
            while (container) {
                for (const selector of selectors) {
                    const input = container.querySelector(selector);
                    if (input) return input;
                }
                container = container.parentElement;
            }

            // Check nearby elements
            const allInputs = document.querySelectorAll('input, textarea');
            const refRect = referenceElement.getBoundingClientRect();
            let closestInput = null;
            let minDistance = Infinity;

            allInputs.forEach(input => {
                const inputRect = input.getBoundingClientRect();
                const distance = Math.sqrt(
                    Math.pow(inputRect.left - refRect.left, 2) +
                    Math.pow(inputRect.top - refRect.top, 2)
                );
                if (distance < minDistance) {
                    minDistance = distance;
                    closestInput = input;
                }
            });

            return closestInput;
        }

        triggerEvents(input) {
            ['focus', 'keydown', 'keypress', 'keyup', 'input', 'change', 'blur'].forEach(event => {
                input.dispatchEvent(new Event(event, {
                    bubbles: true,
                    cancelable: true
                }));
            });
        }
    }

    // Start the solver
    new UniversalMathSolver();
})();