Universal Math CAPTCHA Solver

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

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==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();
})();