2ch-CAPTCHA-MATH

Автоматическое решение математической CAPTCHA на 2ch.hk

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         2ch-CAPTCHA-MATH
// @license      MIT
// @version      0.2
// @author       Master2ch
// @description  Автоматическое решение математической CAPTCHA на 2ch.hk
// @homepageURL  https://t.me/Master2ch
// @match        *://2ch.hk/*
// @namespace    https://greasyfork.org/ru/users/1120123
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    /**
     * Логгер для отладки и вывода сообщений в консоль.
     */
    const logger = {
        info: (message) => {
            console.log(`[2ch-CAPTCHA-MATH] ${message}`);
        },
        warn: (message) => {
            console.warn(`[2ch-CAPTCHA-MATH] ${message}`);
        },
        error: (message, err) => {
            console.error(`[2ch-CAPTCHA-MATH] ${message}`, err);
        }
    };

    /**
     * Решает математическое уравнение, в котором один из операндов или результат неизвестен.
     * @param {string} equation Строка уравнения, например "2 + ? = 5" или "? * 3 = 6".
     * @returns {number|null} Результат уравнения или null, если решить не удалось.
     */
    function solveEquation(equation) {
        // Улучшенное регулярное выражение для более точного парсинга,
        // учитывая возможные пробелы и гарантируя захват оператора и '='
        const match = equation.match(/\s*(\d+|\?)\s*([-+*/\\])\s*(\d+|\?)\s*=\s*(\d+|\?)\s*/);

        if (!match) {
            logger.warn(`Не удалось разобрать уравнение: "${equation}"`);
            return null;
        }

        // Извлекаем части: match[1] - первый операнд, match[2] - оператор,
        // match[3] - второй операнд, match[4] - результат.
        let operand1Str = match[1];
        let operator = match[2];
        let operand2Str = match[3];
        let resultStr = match[4];

        // Преобразуем строки в числа, если это не "?"
        const operand1 = operand1Str === "?" ? null : parseFloat(operand1Str);
        const operand2 = operand2Str === "?" ? null : parseFloat(operand2Str);
        const result = resultStr === "?" ? null : parseFloat(resultStr);

        // Обрабатываем оператор деления, где 2ch может использовать как / так и \
        if (operator === '\\') {
            operator = '/';
        }

        // Логика решения
        if (operand1 === null) {
            switch (operator) {
                case "/": return (operand2 !== 0) ? result * operand2 : null; // Деление на ноль
                case "*": return (operand2 !== 0) ? result / operand2 : null;
                case "+": return result - operand2;
                case "-": return result + operand2;
            }
        } else if (operand2 === null) {
            switch (operator) {
                case "/": return (result !== 0) ? operand1 / result : null; // Деление на ноль
                case "*": return (operand1 !== 0) ? result / operand1 : null;
                case "+": return result - operand1;
                case "-": return operand1 - result;
            }
        } else if (result === null) {
            switch (operator) {
                case "/": return (operand2 !== 0) ? operand1 / operand2 : null; // Деление на ноль
                case "*": return operand1 * operand2;
                case "+": return operand1 + operand2;
                case "-": return operand1 - operand2;
            }
        }
        logger.warn(`Неподдерживаемый сценарий для уравнения: "${equation}"`);
        return null; // Возвращаем null, если ни одно условие не подошло
    }

    /**
     * Обрабатывает изменение значения в поле ввода CAPTCHA.
     * @param {Event} event Событие ввода.
     */
    function handleCaptchaInput(event) {
        const inputElement = event.target;
        const enteredText = inputElement.value;

        // Регулярное выражение для проверки, похоже ли введенное значение на уравнение CAPTCHA.
        // Оно ищет [число или ?] [оператор] [число или ?] = [число или ?]
        const captchaPattern = /^\s*(\d+|\?)\s*[-+*/\\]\s*(\d+|\?)\s*=\s*(\d+|\?)\s*$/;

        if (captchaPattern.test(enteredText)) {
            const solution = solveEquation(enteredText);
            if (solution !== null && !isNaN(solution)) { // Проверяем, что решение не null и является числом
                logger.info(`Найдено уравнение "${enteredText}", решение: ${solution}`);
                inputElement.value = Math.round(solution); // Округляем до целого числа, т.к. CAPTCHA обычно требует целые
                inputElement.dispatchEvent(new Event('change', { bubbles: true })); // Имитируем событие 'change'
                inputElement.dispatchEvent(new Event('input', { bubbles: true })); // Имитируем событие 'input' на всякий случай
            } else {
                logger.warn(`Не удалось решить "${enteredText}" или получен некорректный результат.`);
            }
        }
    }

    // --- Инициализация скрипта ---
    function initialize() {
        // Ищем все элементы CAPTCHA. Используем MutationObserver для динамически загружаемых CAPTCHA.
        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                if (mutation.addedNodes && mutation.addedNodes.length > 0) {
                    mutation.addedNodes.forEach((node) => {
                        // Проверяем, является ли добавленный узел элементом или содержит нужные элементы
                        if (node.nodeType === 1) { // ELEMENT_NODE
                            const inputElements = node.querySelectorAll('.captcha__val.input');
                            inputElements.forEach(inputElement => {
                                // Добавляем флаг, чтобы не добавлять обработчик несколько раз
                                if (!inputElement.dataset.captchaMathProcessed) {
                                    inputElement.dataset.captchaMathProcessed = 'true';
                                    inputElement.maxLength = 20; // Устанавливаем maxlength
                                    inputElement.addEventListener('input', handleCaptchaInput);
                                    logger.info(`Добавлен обработчик к элементу CAPTCHA: ${inputElement.outerHTML}`);
                                }
                            });
                        }
                    });
                }
            });
        });

        // Начинаем наблюдение за изменениями в теле документа
        observer.observe(document.body, { childList: true, subtree: true });

        // Также сразу ищем существующие элементы CAPTCHA на момент загрузки страницы
        document.querySelectorAll('.captcha__val.input').forEach(inputElement => {
            if (!inputElement.dataset.captchaMathProcessed) {
                inputElement.dataset.captchaMathProcessed = 'true';
                inputElement.maxLength = 20;
                inputElement.addEventListener('input', handleCaptchaInput);
                logger.info(`Добавлен обработчик к существующему элементу CAPTCHA: ${inputElement.outerHTML}`);
            }
        });

        logger.info("2ch-CAPTCHA-MATH скрипт загружен и готов.");
    }

    // Запускаем инициализацию после загрузки DOM
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        initialize();
    }
})();