Bloqueador de reCAPTCHA

Remove/bloqueia o widget do reCAPTCHA em todos os sites. O checkbox, iframes e scripts do Google reCAPTCHA são impedidos de carregar, e um stub inofensivo evita que os sites quebrem. Alternável pelo menu do Violentmonkey.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Bloqueador de reCAPTCHA
// @name:en      reCAPTCHA Blocker
// @name:pt-BR   Bloqueador de reCAPTCHA
// @namespace    https://greasyfork.org/en/users/1301195-luciano-inf
// @version      1.2.0
// @description  Remove/bloqueia o widget do reCAPTCHA em todos os sites. O checkbox, iframes e scripts do Google reCAPTCHA são impedidos de carregar, e um stub inofensivo evita que os sites quebrem. Alternável pelo menu do Violentmonkey.
// @description:en  Removes/blocks the reCAPTCHA widget on all websites. The checkbox, iframes and scripts from Google reCAPTCHA are prevented from loading, and a harmless stub keeps sites from breaking. Toggleable from the Violentmonkey menu.
// @description:pt-BR  Remove/bloqueia o widget do reCAPTCHA em todos os sites. O checkbox, iframes e scripts do Google reCAPTCHA são impedidos de carregar, e um stub inofensivo evita que os sites quebrem. Alternável pelo menu do Violentmonkey.
// @author       Luciano.Oliveirals
// @license      MIT
// @run-at       document-start
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_notification
// @match        *://*/*
// @icon         https://icons.iconarchive.com/icons/github/octicons/256/shield-x-16-icon.png
// @supportURL   https://greasyfork.org/scripts/587457-bloqueador-de-recaptcha
// @homepageURL  https://github.com/LucianoSkx/recaptcha-blocker
// ==/UserScript==

(function () {
    'use strict';

    const KEY_ENABLED = 'rcb_enabled';

    let enabled = true;
    try { enabled = GM_getValue(KEY_ENABLED, true) !== false; } catch (e) { enabled = true; }

    if (typeof GM_registerMenuCommand === 'function') {
        GM_registerMenuCommand('🔒 Alternar bloqueio de reCAPTCHA (atualmente ' + (enabled ? 'ATIVADO' : 'DESATIVADO') + ')', () => {
            enabled = !enabled;
            try { GM_setValue(KEY_ENABLED, enabled); } catch (e) {}
            try {
                GM_notification({
                    text: enabled
                        ? 'Bloqueio de reCAPTCHA ATIVADO — recarregue a página para aplicar.'
                        : 'Bloqueio de reCAPTCHA DESATIVADO — recarregue a página para aplicar.',
                    timeout: 4000,
                });
            } catch (e) {}
            location.reload();
        });
    }

    if (!enabled) return;

    const RECAPTCHA_DOMAINS = [
        'google.com/recaptcha',
        'google.com/js/fastbutton',
        'gstatic.com/recaptcha',
        'recaptcha.google.com',
        'recaptcha.net/recaptcha',
    ];

    const STUB_SOURCE = `(() => {
        const noop = () => {};
        const api = {
            render: () => '',
            execute: noop,
            reset: noop,
            getResponse: () => '',
            ready: noop,
            enterprise: {
                render: () => '',
                execute: noop,
                reset: noop,
                getResponse: () => '',
            },
        };
        window.__grecaptcha_cfg = window.__grecaptcha_cfg || { clients: [] };
        try {
            Object.defineProperty(window, 'grecaptcha', {
                get: () => api,
                set: () => {},
                configurable: false,
            });
        } catch (e) {
            window.grecaptcha = api;
        }
    })();`;

    function injectStub() {
        const target = document.documentElement || document.head || document.body;
        if (!target) {
            setTimeout(injectStub, 0);
            return;
        }
        const script = document.createElement('script');
        script.textContent = STUB_SOURCE;
        script.setAttribute('data-recaptcha-blocker', 'stub');
        target.appendChild(script);
    }

    function isRecaptchaElement(el) {
        if (!el || el.nodeType !== 1 || el.hasAttribute('data-recaptcha-blocker')) return false;
        const tag = el.tagName;
        if (tag === 'SCRIPT' || tag === 'IFRAME') {
            const src = (el.src || '').toLowerCase();
            if (RECAPTCHA_DOMAINS.some(d => src.includes(d))) return true;
            if (tag === 'IFRAME' && (el.title || '').toLowerCase() === 'recaptcha') return true;
            return false;
        }
        if (el.classList && (el.classList.contains('g-recaptcha') || el.classList.contains('grecaptcha-badge') || el.classList.contains('g-recaptcha-badge-visible'))) {
            return true;
        }
        return false;
    }

    function purgeElement(el) {
        el.remove();
    }

    function scanTree(root) {
        if (isRecaptchaElement(root)) {
            purgeElement(root);
            return;
        }
        if (root.querySelectorAll) {
            const nodes = root.querySelectorAll(
                'script[src*="recaptcha"], iframe[src*="recaptcha"], iframe[title="recaptcha"], .g-recaptcha, .grecaptcha-badge, .g-recaptcha-badge-visible'
            );
            for (const el of nodes) {
                if (isRecaptchaElement(el)) purgeElement(el);
            }
        }
    }

    injectStub();

    if (document.documentElement) {
        scanTree(document.documentElement);
    }

    const observer = new MutationObserver(records => {
        for (const record of records) {
            for (const node of record.addedNodes) {
                scanTree(node);
            }
        }
    });

    observer.observe(document.documentElement || document, {
        childList: true,
        subtree: true,
    });

    const lateScan = () => {
        if (document.documentElement) scanTree(document.documentElement);
    };
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', lateScan, { once: true });
    }
    window.addEventListener('load', lateScan, { once: true });
})();