Recaptcha Skipper (Google Sorry)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==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
            }
        })
    }
})()