Universal Math CAPTCHA Solver

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

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

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