Clawptcha bypasser

Bypass Clawptcha - a captcha that verifies you're not a human

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name               Clawptcha bypasser
// @name:zh-CN         Clawptcha 验证绕过
// @name:zh-TW         Clawptcha 驗證繞過
// @namespace          https://greasyfork.org/users/667968-pyudng
// @version            1.1
// @description        Bypass Clawptcha - a captcha that verifies you're not a human
// @description:zh-CN  绕过 Clawptcha - 一种验证您不是人类的验证码
// @description:zh-TW  繞過 Clawptcha - 一種驗證您不是人類的驗證碼
// @author             PY-DNG
// @license            MIT
// @homepageURL        https://greasyfork.org/scripts/565015
// @supportURL         https://greasyfork.org/scripts/565015/feedback
// @match              https://clawptcha.com/*
// @require            https://update.greasyfork.org/scripts/456034/1651347/Basic%20Functions%20%28For%20userscripts%29.js
// @require            https://cdn.jsdelivr.net/npm/[email protected]/src/sha256.min.js
// @icon               https://clawptcha.com/favicon.svg
// @grant              none
// ==/UserScript==

!async function() {
    detectDom({
        selector: '#challenge-popup > #challenge-content, .clawptcha-challenge-popup .clawptcha-challenge-content',
        /**
         * @param {HTMLDivElement} container 
         */
        callback(container) {
            // 验证类型:Hash Computation
            detectDom({
                root: container,
                selector: '#hash-submit',
                /**
                 * @param {HTMLButtonElement} btnSubmit 提交答案的按钮
                 */
                async callback(btnSubmit) {
                    // 当存在"Generate"按钮时自动点击
                    $(container, '#hash-start')?.click();

                    /** @type {HTMLDivElement} 包含了需计算Hash的原始文本的div */
                    const divHash = await detectDom(container, '.hash-display, .clawptcha-hash-display');
                    await detectElement(divHash, divHash => divHash.innerText.includes('Hash this:') || divHash.innerText.includes('Hash:'), { childList: true });
                    /** @type {HTMLDivElement} */
                    const text = divHash.innerText.replace('Hash this:', '').replace('Hash:', '').trim();
                    /** @type {string} */
                    const hash = sha256(text).substring(0, 16);
                    $(container, '#hash-answer').value = hash;
                    btnSubmit.click();
                }
            });

            // 验证类型:Reaction Time
            detectDom({
                root: container,
                selector: '#reaction-target, #reaction-start',
                /**
                 * @param {HTMLButtonElement} button 需尽快点击的按钮
                 */
                callback(button) {
                    button.click();
                }
            });

            // 验证类型:Precision Timing
            // 注:这种类型验证可能失败 —— 因为即使是bot,也难以在±1ms的精度上完成点击
            // **这是设计的不合理而不是我们的能力不达标**
            detectDom({
                root: container,
                selector: '#timing-start',
                /**
                 * @param {HTMLButtonElement} btnStart 开始按钮
                 */
                callback(btnStart) {
                    const btnTiming = $(container, '#timing-click');
                    btnStart.click();
                    setTimeout(() => btnTiming.click(), 5000);
                }
            });

            // 验证类型:Crypto Chain
            detectDom({
                root: container,
                selector: '#crypto-input',
                /**
                 * @param {HTMLDivElement} divHash 包含了需计算Hash的原始文本的div
                 */
                async callback(divHash) {
                    // 当存在"Start"按钮时自动点击
                    $(container, '#crypto-start')?.click();
                    await detectElement(divHash, divHash => divHash.innerText.includes('Hash:'), { childList: true });

                    // 连续完成3次哈希计算
                    let counter = 0;
                    const observer = new MutationObserver(submit);
                    observer.observe(divHash, {
                        childList: true,
                    });
                    submit();

                    function submit() {
                        if (++counter >= 3) observer.disconnect();

                        /** @type {HTMLDivElement} */
                        const text = divHash.innerText.replace('Hash:', '').trim();
                        /** @type {string} */
                        const hash = sha256(text);
                        $(container, '#crypto-answer').value = hash;
                        $(container, '#crypto-submit').click();
                    }
                }
            });

            // 验证类型:Prime Factorization
            detectDom({
                root: container,
                selector: '#prime-submit',
                /**
                 * @param {HTMLButtonElement} btnSubmit 提交答案的按钮
                 */
                async callback(btnSubmit) {
                    /** @type {HTMLDivElement} 包含了需分解质因数的数值的div */
                    const divHash = $(container, '.hash-display, .clawptcha-prime-display');

                    // 当从API加载验证码时,需要等待验证码加载完毕
                    if (!/\d+/.test(divHash.innerText)) {
                        const { promise, resolve } = Promise.withResolvers();
                        const observer = new MutationObserver(() => {
                            if (/\d+/.test(divHash.innerText)) {
                                resolve();
                                observer.disconnect();
                            }
                        });
                        observer.observe(divHash, { childList: true });
                        await promise;
                    }

                    // 分解质因数
                    /** @type {HTMLDivElement} */
                    const text = divHash.innerText.replace('Find the prime factors of', '').trim();
                    /** @type {number} 需分解质因数的数值 */
                    const number = parseInt(text, 10);
                    const { prime1, prime2 } = primeFactorization(number);

                    // 提交结果
                    $(container, '#prime-answer').value = `${prime1},${prime2}`;
                    btnSubmit.click();

                    /**
                     * 简单分解质因数
                     * @param {number} number 一个由两个质数相乘得到的合数
                     * @returns {{prime1: number, prime2: number}} 分解结果
                     */
                    function primeFactorization(number) {
                        for (let i = 2; i <= Math.sqrt(number); i++) {
                            if (number % i === 0) {
                                return { prime1: i, prime2: number / i };
                            }
                        }
                        return { prime1: 1, prime2: number };
                    }
                }
            });
        }
    });

    /**
     * 类似{@link detectDom},监听元素变化并在给定条件符合时resolve
     * @template {HTMLElement} T
     * @param {T} element 需要监听的HTML元素
     * @param {(element: T) => boolean} condition 判断元素当前是否符合条件的无副作用的方法
     * @param {MutationObserverInit} options
     * @returns {Promise<T>}
     */
    function detectElement(element, condition, options) {
        const { promise, resolve } = Promise.withResolvers();
        const verify = () => {
            if (condition(element)) {
                resolve(element);
                observer.disconnect();
            }
        };
        const observer = new MutationObserver(verify);
        observer.observe(element, options);
        verify();
        return promise;
    }
} ();