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