您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Solves ALL basic math CAPTCHAs (+, -, *, /) in any format
- // ==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();
- })();