Recaptcha Skipper (Google Sorry)

Try to skip google recaptcha error page automatically through reloading the page. If not, redirect to configurable url

// ==UserScript==
// @name         Recaptcha Skipper (Google Sorry)
// @namespace    c4ca4238a0b923820dcc509a6f75849b
// @version      0.4.0
// @description  Try to skip google recaptcha error page automatically through reloading the page. If not, redirect to configurable url
// @author       _SUDO
// @include      *://www.google.*/sorry/*
// @grant        GM_addStyle
// @compatible      chrome Chrome + Tampermonkey or Violentmonkey
// @compatible      firefox Firefox + Greasemonkey or Tampermonkey or Violentmonkey
// @compatible      opera Opera + Tampermonkey or Violentmonkey
// @compatible      edge Edge + Tampermonkey or Violentmonkey
// @compatible      safari Safari + Tampermonkey or Violentmonkey
// ==/UserScript==
/* jshint esversion: 6 */

(function (){
    'use strict';
    const config = {
        redirect_to: true, // Will redirect the actual searched query to the defined url (from ?q=[terms])
        promp_redirect: true, // Create UI to cancel redirection
    }
    const searchEngines = [
        // The list of top buttons
        // Note: the `default` sets the search engine to use for the automatic redirection.
        { name: 'DuckDuckGo', url: 'https://duckduckgo.com/?q=', icon: 'https://duckduckgo.com/favicon.ico', default: true },
        { name: 'Bing', url: 'https://bing.com/?q=', icon: 'https://www.bing.com/favicon.ico' },
        { name: 'Brave', url: 'https://search.brave.com/search?q=', icon: 'https://www.brave.com/favicon.ico' },
        { name: 'SearX', url: 'https://metasearx.com/?q=', icon: 'https://metasearx.com/favicon.ico' },
        { name: 'Yandex', url: 'https://yandex.com/search/?text=', icon: 'https://yandex.com/favicon.ico' },
        { name: 'Startpage', url: 'https://www.startpage.com/sp/search?q=', icon: 'https://www.startpage.com/favicon.ico' },
        { name: 'Ecosia', url: 'https://www.ecosia.org/search?q=', icon: 'https://www.ecosia.org/favicon.ico' },
        { name: 'Yahoo', url: 'https://search.yahoo.com/search;?p=', icon: 'https://www.yahoo.com/favicon.ico' },
    ];

    class SkipCaptcha {
        constructor() {
            this.url = false
            this.tries = 1
        }

        // Create a 3sec counter to allow the user to cancel automatic redirection
        create_Redirect_UI () {
            if (!config.promp_redirect) return this; // Will automatically allow

            let redirect_allowed = true // By default it's automatically allowed redirect

            // styles
            GM_addStyle(`
            .search-buttons {
            gap: 5px;
            display: flex;
            flex-wrap: wrap;
            padding-bottom: 10px;
        }
        .search-buttons a {
            display: flex;
            width: 40px;
            height: 40px;
            background: white;
            align-items: center;
            border-radius: 5px;
            border: 1px solid gray;
            justify-content: center;
            transition: background 0.3s, transform 0.2s;
        }
        .search-buttons a:hover {
            background: lightgray;
            transform: scale(1.1);
        }
        .search-buttons img {
            width: 24px;
            height: 24px;
        }
        #userUI {
            display: grid;
            border-radius: 5px;
            border: 1px solid gray;
            grid-template-columns: 0.3fr 1.6fr;
        }
        #redirect {
            display: flex;
            padding: 10px;
            cursor: pointer;
            align-items: center;
            justify-content: center;
            border-right: 1px solid gray;
        }
        #redirect span {
            color: black;
            font-size: 24px;
            transition: transform 0.3s ease;
        }
        #redirect:hover span {
            transform: scaleX(-1);
        }
        #UIcounter[value] {
            -webkit-appearance: none;
            -moz-appearance: none;
            appearance: none;
            border: none; /* Get rid of default border in Firefox. */

            width: 100%;
            height: 20px;
        }

        #UIcounter[value]::-moz-progress-bar {
            background-color: #09c;
            /* the stripe layer */
            background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.16) 25%,
            rgba(0, 0, 0, 0) 25%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.16) 50%,
            rgba(255, 255, 255, 0.16) 75%, rgba(0, 0, 0, 0) 75%, rgba(0, 0, 0, 0)),
            linear-gradient(to right, #09c, #f44);

            /* we also need background-size, because the stripe must repeat every 20 pixels */
            background-size: 60px 50px, 100%;
        }`)

            const UIfragmentElement = `
             <div style="position: fixed; bottom: 5px; right: 5px; z-index: 999999; padding: 10px;">
        <div class="search-buttons"></div>
        <div id="userUI">
            <a id="redirect" title="Skip Countdown"><span>►</span></a>
            <div style="cursor: pointer; padding: 10px;" role="button" title="Pause Countdown">
                <p>You will be automatically redirected...</p>
                <progress id="UIcounter" max="${100}" value="0"></progress>
            </div>
        </div>
    </div>`
            const range = document.createRange()
            const fragment = range.createContextualFragment(UIfragmentElement) //Creates a DOM object

            const searchEnginesContainer = fragment.querySelector('.search-buttons');
            let query = this.get_query()
            searchEngines.forEach(engine => {
                const button = document.createElement('a');
                button.href = engine.url + query;
                button.setAttribute('title', engine.name);

                const img = document.createElement('img');
                img.src = engine.icon;
                img.alt = `${engine.name} logo`;

                button.appendChild(img);
                searchEnginesContainer.appendChild(button);
            });

            document.body.append(fragment)

            const UI = document.getElementById('userUI')
            const UI_skipBTN = document.getElementById('redirect')
            const UI_Counter = document.getElementById('UIcounter')
            let counter = 0

            const processBar = setInterval(() => {
                if (counter >= 100) {
                    remove_event()
                    clearInterval(processBar)
                }
                counter = counter >= 100 ? 100 : counter+5
                UI_Counter.value = counter
            }, 135)

            // This event will allows us to just force skip
            const event_skip = UI_skipBTN.addEventListener('click', () => {
                clearInterval(processBar)

                this.handle_redirects()
            }, {once: true})

            const event = UI.addEventListener('click', () => {
                if (counter != 100) {
                    redirect_allowed = false
                    clearInterval(processBar)
                }
            }, {once: true})

            const remove_event = () => {
                UI.removeEventListener('click', event, true)
            }

            return new Promise(resolve => {
                setTimeout(() => {
                    resolve(redirect_allowed)
                }, 3000)
            })
        }

        checkAttemps () {
            // Will check if there's a value of previous retries on the sesion storage, if not create one
            const storage = sessionStorage
            const storage_name = 'Captch_Retries'

            // Check if it's already a value
            if (storage.getItem(storage_name)) {
                this.tries = storage.getItem(storage_name)
                if (!typeof this.tries === 'number') {
                    this.tries = parseInt(this.tries)
                }
                this.tries++
                storage.setItem(storage_name, this.tries)
            }
            else {
                storage.setItem(storage_name, this.tries)
            }
            return this
        }

        async redirectURL_to_Google () {
            // Forced await
            const response = await this.create_Redirect_UI()
            if (!response) return;

            this.handle_redirects()
            return this
        }

        handle_redirects () {
            if (!this.url) return this

            if (this.tries < 3) window.location = this.url
            else {
                if (config.redirect_to) this.redirect_to_user_defined_url()
            }

            return this
        }

        get_query () {
            // this.url is the <title> string and we have to convert to an URL to use URLSearchParams constructor
            const gen_URL_to_search = new URL(this.url)
            const getSearchedParams = new URLSearchParams(gen_URL_to_search.search).get('q') // Only get the Query searched

            return getSearchedParams
        }

        redirect_to_user_defined_url () {
            const defaultEngine = searchEngines.find(engine => engine.default) || searchEngines[0];

            // this.url is the <title> string and we have to convert to an URL to use URLSearchParams constructor
            const getSearchedParams = this.get_query()
            const newURL = defaultEngine.url + getSearchedParams

            // Send to the new url formated
            window.location = newURL

            // If works properly, a return isn't necesary
            // return this
        }

        getURL () {
            // The search url/terms are in the page url and body,
            // because I'am lazy I will get it from the title element
            const url = document.getElementsByTagName('title')[0].textContent
            if (url.lenght != 0) this.url = url

            return this
        }

        init () {
            this.getURL().checkAttemps().redirectURL_to_Google()
        }
    }

    const init = new SkipCaptcha().init()

    if (document.readyState === 'complete') {
        init
    } else {
        document.addEventListener('readystatechange', function () {
            if (document.readyState === 'complete') {
                init
            }
        })
    }
})()

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址