Recaptcha Skipper (Google Sorry)

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

  1. // ==UserScript==
  2. // @name Recaptcha Skipper (Google Sorry)
  3. // @namespace c4ca4238a0b923820dcc509a6f75849b
  4. // @version 0.4.0
  5. // @description Try to skip google recaptcha error page automatically through reloading the page. If not, redirect to configurable url
  6. // @author _SUDO
  7. // @include *://www.google.*/sorry/*
  8. // @grant GM_addStyle
  9. // @compatible chrome Chrome + Tampermonkey or Violentmonkey
  10. // @compatible firefox Firefox + Greasemonkey or Tampermonkey or Violentmonkey
  11. // @compatible opera Opera + Tampermonkey or Violentmonkey
  12. // @compatible edge Edge + Tampermonkey or Violentmonkey
  13. // @compatible safari Safari + Tampermonkey or Violentmonkey
  14. // ==/UserScript==
  15. /* jshint esversion: 6 */
  16.  
  17. (function (){
  18. 'use strict';
  19. const config = {
  20. redirect_to: true, // Will redirect the actual searched query to the defined url (from ?q=[terms])
  21. promp_redirect: true, // Create UI to cancel redirection
  22. }
  23. const searchEngines = [
  24. // The list of top buttons
  25. // Note: the `default` sets the search engine to use for the automatic redirection.
  26. { name: 'DuckDuckGo', url: 'https://duckduckgo.com/?q=', icon: 'https://duckduckgo.com/favicon.ico', default: true },
  27. { name: 'Bing', url: 'https://bing.com/?q=', icon: 'https://www.bing.com/favicon.ico' },
  28. { name: 'Brave', url: 'https://search.brave.com/search?q=', icon: 'https://www.brave.com/favicon.ico' },
  29. { name: 'SearX', url: 'https://metasearx.com/?q=', icon: 'https://metasearx.com/favicon.ico' },
  30. { name: 'Yandex', url: 'https://yandex.com/search/?text=', icon: 'https://yandex.com/favicon.ico' },
  31. { name: 'Startpage', url: 'https://www.startpage.com/sp/search?q=', icon: 'https://www.startpage.com/favicon.ico' },
  32. { name: 'Ecosia', url: 'https://www.ecosia.org/search?q=', icon: 'https://www.ecosia.org/favicon.ico' },
  33. { name: 'Yahoo', url: 'https://search.yahoo.com/search;?p=', icon: 'https://www.yahoo.com/favicon.ico' },
  34. ];
  35.  
  36. class SkipCaptcha {
  37. constructor() {
  38. this.url = false
  39. this.tries = 1
  40. }
  41.  
  42. // Create a 3sec counter to allow the user to cancel automatic redirection
  43. create_Redirect_UI () {
  44. if (!config.promp_redirect) return this; // Will automatically allow
  45.  
  46. let redirect_allowed = true // By default it's automatically allowed redirect
  47.  
  48. // styles
  49. GM_addStyle(`
  50. .search-buttons {
  51. gap: 5px;
  52. display: flex;
  53. flex-wrap: wrap;
  54. padding-bottom: 10px;
  55. }
  56. .search-buttons a {
  57. display: flex;
  58. width: 40px;
  59. height: 40px;
  60. background: white;
  61. align-items: center;
  62. border-radius: 5px;
  63. border: 1px solid gray;
  64. justify-content: center;
  65. transition: background 0.3s, transform 0.2s;
  66. }
  67. .search-buttons a:hover {
  68. background: lightgray;
  69. transform: scale(1.1);
  70. }
  71. .search-buttons img {
  72. width: 24px;
  73. height: 24px;
  74. }
  75. #userUI {
  76. display: grid;
  77. border-radius: 5px;
  78. border: 1px solid gray;
  79. grid-template-columns: 0.3fr 1.6fr;
  80. }
  81. #redirect {
  82. display: flex;
  83. padding: 10px;
  84. cursor: pointer;
  85. align-items: center;
  86. justify-content: center;
  87. border-right: 1px solid gray;
  88. }
  89. #redirect span {
  90. color: black;
  91. font-size: 24px;
  92. transition: transform 0.3s ease;
  93. }
  94. #redirect:hover span {
  95. transform: scaleX(-1);
  96. }
  97. #UIcounter[value] {
  98. -webkit-appearance: none;
  99. -moz-appearance: none;
  100. appearance: none;
  101. border: none; /* Get rid of default border in Firefox. */
  102.  
  103. width: 100%;
  104. height: 20px;
  105. }
  106.  
  107. #UIcounter[value]::-moz-progress-bar {
  108. background-color: #09c;
  109. /* the stripe layer */
  110. background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.16) 25%,
  111. rgba(0, 0, 0, 0) 25%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.16) 50%,
  112. rgba(255, 255, 255, 0.16) 75%, rgba(0, 0, 0, 0) 75%, rgba(0, 0, 0, 0)),
  113. linear-gradient(to right, #09c, #f44);
  114.  
  115. /* we also need background-size, because the stripe must repeat every 20 pixels */
  116. background-size: 60px 50px, 100%;
  117. }`)
  118.  
  119. const UIfragmentElement = `
  120. <div style="position: fixed; bottom: 5px; right: 5px; z-index: 999999; padding: 10px;">
  121. <div class="search-buttons"></div>
  122. <div id="userUI">
  123. <a id="redirect" title="Skip Countdown"><span>►</span></a>
  124. <div style="cursor: pointer; padding: 10px;" role="button" title="Pause Countdown">
  125. <p>You will be automatically redirected...</p>
  126. <progress id="UIcounter" max="${100}" value="0"></progress>
  127. </div>
  128. </div>
  129. </div>`
  130. const range = document.createRange()
  131. const fragment = range.createContextualFragment(UIfragmentElement) //Creates a DOM object
  132.  
  133. const searchEnginesContainer = fragment.querySelector('.search-buttons');
  134. let query = this.get_query()
  135. searchEngines.forEach(engine => {
  136. const button = document.createElement('a');
  137. button.href = engine.url + query;
  138. button.setAttribute('title', engine.name);
  139.  
  140. const img = document.createElement('img');
  141. img.src = engine.icon;
  142. img.alt = `${engine.name} logo`;
  143.  
  144. button.appendChild(img);
  145. searchEnginesContainer.appendChild(button);
  146. });
  147.  
  148. document.body.append(fragment)
  149.  
  150. const UI = document.getElementById('userUI')
  151. const UI_skipBTN = document.getElementById('redirect')
  152. const UI_Counter = document.getElementById('UIcounter')
  153. let counter = 0
  154.  
  155. const processBar = setInterval(() => {
  156. if (counter >= 100) {
  157. remove_event()
  158. clearInterval(processBar)
  159. }
  160. counter = counter >= 100 ? 100 : counter+5
  161. UI_Counter.value = counter
  162. }, 135)
  163.  
  164. // This event will allows us to just force skip
  165. const event_skip = UI_skipBTN.addEventListener('click', () => {
  166. clearInterval(processBar)
  167.  
  168. this.handle_redirects()
  169. }, {once: true})
  170.  
  171. const event = UI.addEventListener('click', () => {
  172. if (counter != 100) {
  173. redirect_allowed = false
  174. clearInterval(processBar)
  175. }
  176. }, {once: true})
  177.  
  178. const remove_event = () => {
  179. UI.removeEventListener('click', event, true)
  180. }
  181.  
  182. return new Promise(resolve => {
  183. setTimeout(() => {
  184. resolve(redirect_allowed)
  185. }, 3000)
  186. })
  187. }
  188.  
  189. checkAttemps () {
  190. // Will check if there's a value of previous retries on the sesion storage, if not create one
  191. const storage = sessionStorage
  192. const storage_name = 'Captch_Retries'
  193.  
  194. // Check if it's already a value
  195. if (storage.getItem(storage_name)) {
  196. this.tries = storage.getItem(storage_name)
  197. if (!typeof this.tries === 'number') {
  198. this.tries = parseInt(this.tries)
  199. }
  200. this.tries++
  201. storage.setItem(storage_name, this.tries)
  202. }
  203. else {
  204. storage.setItem(storage_name, this.tries)
  205. }
  206. return this
  207. }
  208.  
  209. async redirectURL_to_Google () {
  210. // Forced await
  211. const response = await this.create_Redirect_UI()
  212. if (!response) return;
  213.  
  214. this.handle_redirects()
  215. return this
  216. }
  217.  
  218. handle_redirects () {
  219. if (!this.url) return this
  220.  
  221. if (this.tries < 3) window.location = this.url
  222. else {
  223. if (config.redirect_to) this.redirect_to_user_defined_url()
  224. }
  225.  
  226. return this
  227. }
  228.  
  229. get_query () {
  230. // this.url is the <title> string and we have to convert to an URL to use URLSearchParams constructor
  231. const gen_URL_to_search = new URL(this.url)
  232. const getSearchedParams = new URLSearchParams(gen_URL_to_search.search).get('q') // Only get the Query searched
  233.  
  234. return getSearchedParams
  235. }
  236.  
  237. redirect_to_user_defined_url () {
  238. const defaultEngine = searchEngines.find(engine => engine.default) || searchEngines[0];
  239.  
  240. // this.url is the <title> string and we have to convert to an URL to use URLSearchParams constructor
  241. const getSearchedParams = this.get_query()
  242. const newURL = defaultEngine.url + getSearchedParams
  243.  
  244. // Send to the new url formated
  245. window.location = newURL
  246.  
  247. // If works properly, a return isn't necesary
  248. // return this
  249. }
  250.  
  251. getURL () {
  252. // The search url/terms are in the page url and body,
  253. // because I'am lazy I will get it from the title element
  254. const url = document.getElementsByTagName('title')[0].textContent
  255. if (url.lenght != 0) this.url = url
  256.  
  257. return this
  258. }
  259.  
  260. init () {
  261. this.getURL().checkAttemps().redirectURL_to_Google()
  262. }
  263. }
  264.  
  265. const init = new SkipCaptcha().init()
  266.  
  267. if (document.readyState === 'complete') {
  268. init
  269. } else {
  270. document.addEventListener('readystatechange', function () {
  271. if (document.readyState === 'complete') {
  272. init
  273. }
  274. })
  275. }
  276. })()