Recaptcha Skipper (Google Sorry)

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

目前为 2021-10-10 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Recaptcha Skipper (Google Sorry)
  3. // @namespace c4ca4238a0b923820dcc509a6f75849b
  4. // @version 0.3.2
  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. redirect_to_url: 'https://duckduckgo.com/?q=', // if you like DuckDuckGo for example... https://duckduckgo.com/?q=
  22. promp_redirect: true, // Create UI to cancel redirection
  23. verbose_logging: false
  24. }
  25.  
  26. const logger = (...args) => {
  27. if (!config.verbose_logging) return;
  28. console.log(...args)
  29. }
  30.  
  31. class SkipCaptcha {
  32. constructor() {
  33. this.url = false
  34. this.tries = 1
  35. }
  36.  
  37. // Create a 3sec counter to allow the user to cancel automatic redirection
  38. create_Redirect_UI () {
  39. if (!config.promp_redirect) return this; // Will automatically allow
  40.  
  41. let redirect_allowed = true // By default it's automatically allowed redirect
  42.  
  43. // Add progress bar style
  44.  
  45. GM_addStyle(`
  46. #UIcounter {
  47. box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25) inset;
  48. }
  49. #UIcounter[value] {
  50. /* Reset the default appearance */
  51. -webkit-appearance: none;
  52. -moz-appearance: none;
  53. appearance: none;
  54.  
  55. /* Get rid of default border in Firefox. */
  56. border: none;
  57.  
  58. /* Dimensions */
  59. width: 100%;
  60. height: 20px;
  61. }
  62.  
  63. #UIcounter[value]::-moz-progress-bar {
  64. background-color: #09c;
  65. background-image:
  66. /* the stripe layer */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);
  67.  
  68. /* we also need background-size, because the stripe must repeat every 20 pixels */
  69. background-size: 60px 50px, 100%;
  70. }`)
  71.  
  72. const UIfragmentElement = `
  73. <div id="userUI" style="position: fixed; bottom: 5px; right: 5px; background: #f9f9f9; z-index: 999999;border-radius: 5px; box-shadow: 0 0 5px -1px;border: 1px solid gray;display: grid;grid-template-columns: 0.3fr 1.6fr;">
  74. <a id="redirect" style="cursor: pointer; text-align: center;font-size: 24px;box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25) inset;margin: 0;padding-top: 21px;border-radius: 5px 0 0 5px;text-decoration: none;color: black;">►</a>
  75. <div>
  76. <p style="margin: 15px;">You will be automatically redirected...</p>
  77. <progress id="UIcounter" max="${100}" value="0" style="width: 100%; height: 15px;"></progress>
  78. <div>
  79. </div>`
  80. const range = document.createRange()
  81. const fragment = range.createContextualFragment(UIfragmentElement) //Creates a DOM object
  82. document.body.append(fragment)
  83.  
  84. const UI = document.getElementById('userUI')
  85. const UI_skipBTN = document.getElementById('redirect')
  86. const UI_Counter = document.getElementById('UIcounter')
  87. let counter = 0
  88.  
  89. logger('Started countdown')
  90. const processBar = setInterval(() => {
  91. if (counter >= 100) {
  92. logger('Ended countdown')
  93. remove_event()
  94. clearInterval(processBar)
  95. }
  96. counter = counter >= 100 ? 100 : counter+5
  97. UI_Counter.value = counter
  98. }, 135)
  99.  
  100. // This event will allows us to just force skip
  101. const event_skip = UI_skipBTN.addEventListener('click', () => {
  102. logger('User clicked, cancelled redirect.')
  103. clearInterval(processBar)
  104.  
  105. this.handle_redirects()
  106. }, {once: true})
  107.  
  108. const event = UI.addEventListener('click', () => {
  109. if (counter != 100) {
  110. redirect_allowed = false
  111. logger('User clicked, cancelled redirect.')
  112. clearInterval(processBar)
  113. }
  114. }, {once: true})
  115.  
  116. const remove_event = () => {
  117. logger('Removed Event Listener')
  118. UI.removeEventListener('click', event, true)
  119. }
  120.  
  121. return new Promise(resolve => {
  122. setTimeout(() => {
  123. logger('Redirect Status:', redirect_allowed)
  124. resolve(redirect_allowed)
  125. }, 3000)
  126. })
  127. }
  128.  
  129. checkAttemps () {
  130. // Will check if there's a value of previous retries on the sesion storage, if not create one
  131. const storage = sessionStorage
  132. const storage_name = 'Captch_Retries'
  133.  
  134. // Check if it's already a value
  135. if (storage.getItem(storage_name)) {
  136. this.tries = storage.getItem(storage_name)
  137. logger('Retries saved:', this.tries)
  138. if (!typeof this.tries === 'number') {
  139. logger('Value not a number, parsing to integer...')
  140. this.tries = parseInt(this.tries)
  141. }
  142. logger('Adding an attemt to saved retries...')
  143. this.tries++
  144. storage.setItem(storage_name, this.tries)
  145.  
  146. logger('New set value stored:', this.tries)
  147. }
  148. else {
  149. logger('Any previous retries saved, creating a new storage item with value of', this.tries)
  150. storage.setItem(storage_name, this.tries)
  151. }
  152.  
  153. logger('checkAttemps', this.tries)
  154.  
  155. return this
  156. }
  157.  
  158. async redirectURL_to_Google () {
  159. // Forced await
  160. const response = await this.create_Redirect_UI()
  161. if (!response) {
  162. logger('User cancelled redirect')
  163. return;
  164. }
  165. this.handle_redirects()
  166.  
  167. return this
  168. }
  169.  
  170. handle_redirects () {
  171. if (this.url) {
  172. if (this.tries < 3) {
  173. window.location = this.url
  174. } else {
  175. if (config.redirect_to) {
  176. this.redirect_to_user_defined_url()
  177. } else {
  178. logger('Not redirecting, too many retries:', this.url, this.tries)
  179. }
  180. }
  181. } else {
  182. logger('ERROR: no url finded!')
  183. }
  184.  
  185. return this
  186. }
  187.  
  188. redirect_to_user_defined_url () {
  189. logger(`Redirecting to "${config.redirect_to_url}"`)
  190.  
  191. // this.url is the <title> string and we have to convert to an URL to use URLSearchParams constructor
  192. const gen_URL_to_search = new URL(this.url)
  193. const getSearchedParams = new URLSearchParams(gen_URL_to_search.search).get('q') // Only get que Query searched
  194. const newURL = config.redirect_to_url + getSearchedParams
  195. logger('Google search params:', getSearchedParams)
  196. logger('New URL generated to redirect:', newURL)
  197.  
  198. // Send to the new url formated
  199. window.location = newURL
  200.  
  201. // If works properly, a return isn't necesary
  202. // return this
  203. }
  204.  
  205. getURL () {
  206. // The search url/terms are in the page url and body,
  207. // because I'am lazy I will get it from the title element
  208. const url = document.getElementsByTagName('title')[0].textContent
  209. if (url.lenght != 0) {
  210. this.url = url
  211. }
  212. logger('URL:', this.url)
  213.  
  214. return this
  215. }
  216.  
  217. init () {
  218. logger('Initilizing...')
  219. this.getURL().checkAttemps().redirectURL_to_Google()
  220. }
  221. }
  222.  
  223. const init = new SkipCaptcha().init()
  224.  
  225. if (document.readyState === 'complete') {
  226. init
  227. } else {
  228. document.addEventListener('readystatechange', function () {
  229. if (document.readyState === 'complete') {
  230. init
  231. }
  232. })
  233. }
  234. })()