UNPKG

recaptcha-solver

Version:

Solve reCAPTCHA challenges by using offline speech recognition.

3 lines (2 loc) 5.45 kB
"use strict";var V=Object.create;var E=Object.defineProperty;var z=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var G=Object.getPrototypeOf,J=Object.prototype.hasOwnProperty;var K=(e,t)=>{for(var o in t)E(e,o,{get:t[o],enumerable:!0})},T=(e,t,o,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let c of j(t))!J.call(e,c)&&c!==o&&E(e,c,{get:()=>t[c],enumerable:!(a=z(t,c))||a.enumerable});return e};var m=(e,t,o)=>(o=e!=null?V(G(e)):{},T(t||!e||!e.__esModule?E(o,"default",{value:e,enumerable:!0}):o,e)),Q=e=>T(E({},"__esModule",{value:!0}),e);var oe={};K(oe,{BFRAME:()=>f,MAIN_FRAME:()=>h,NotFoundError:()=>v,exists:()=>I,solve:()=>D});module.exports=Q(oe);var N=require("child_process"),d=m(require("fs")),x=m(require("path")),W=m(require("os")),B=require("stream"),k=m(require("vosk-koffi")),q=m(require("wav"));function n(...e){process.env.VERBOSE&&console.log("[reCAPTCHA solver]",...e)}var H=m(require("path")),L=H.default.resolve(__dirname,"..","model"),S="sound.mp3",M="out.wav",h="iframe[title='reCAPTCHA']",f="iframe[src^='https://www.google.com/recaptcha/api2/bframe']:visible, iframe[src^='https://www.google.com/recaptcha/enterprise/bframe']:visible",_="body > div > div";var P=class{constructor(t="Mutex"){this.name=t;this._locked=!1;this._queue=[]}async lock(t){if(this._locked)return t&&n(`[${this.name}] ${t} waiting`),new Promise(o=>{this._queue.push(()=>{this._lock(t),o()})});this._lock(t)}unlock(t){var o;this._locked&&(t&&n(`[${this.name}] ${t} unlocked`),this._locked=!1,(o=this._queue.shift())==null||o())}_lock(t){this._locked=!0,t&&n(`[${this.name}] ${t} locked`)}};function O(e){return new Promise(t=>setTimeout(t,e))}async function I(e){return!!e.$(f)}k.default.setLogLevel(-1);var X=new k.default.Model(L),v=class extends Error{constructor(t){super(t),this.name="NotFoundError"}};async function D(e,{delay:t=64,wait:o=5e3,retry:a=3,ffmpeg:c="ffmpeg"}={}){try{await e.waitForSelector(f,{state:"attached"})}catch{throw new Error("No reCAPTCHA detected")}let w=!1,A=await e.$(f);if(A===null)throw new Error("Could not find reCAPTCHA popup iframe");let i=await A.contentFrame();if(i===null)throw new Error("Could not find reCAPTCHA popup iframe content");let l=!!await i.$(_);if(n("bframe loaded:",l),l===!1){await e.waitForSelector(h,{state:"attached"});let r=await e.$(h);if(r===null)throw new Error("Could not find reCAPTCHA iframe");let s=await r.contentFrame();if(s===null)throw new Error("Could not find reCAPTCHA iframe content");if(w=!!await s.$("div.rc-anchor-invisible"),n("invisible:",w),w===!0)return!1;{let R=await s.$("#recaptcha-anchor-label");if(R===null)throw new Error("Could not find reCAPTCHA label");await R.click(),await i.waitForSelector(_)}}let C=await i.$(_);if(C===null)throw new Error("Could not find reCAPTCHA challenge");let y=await C.evaluate(r=>!r.classList.contains("rc-footer"));if(n("action required:",y),y===!1)return!1;await i.waitForSelector("#recaptcha-audio-button",{timeout:o});let b=await i.$("#recaptcha-audio-button");if(b===null)throw new Error("Could not find reCAPTCHA audio button");let u=new P;await u.lock("init");let p=!1,F=Promise.resolve(""),$=async r=>{if(r.headers()["content-type"]==="audio/mp3")n(`got audio from ${r.url()}`),F=new Promise(s=>{te(r,c).then(s).catch(()=>{})}),u.unlock("get sound");else if(r.url().startsWith("https://www.google.com/recaptcha/api2/userverify")||r.url().startsWith("https://www.google.com/recaptcha/enterprise/userverify")){let s=(await r.body()).toString().replace(`)]}' `,"");p=JSON.parse(s)[2]===1,u.unlock("verified")}};e.on("response",$),await b.click();let U=0;for(;p===!1;){if(U++>=a)throw new Error("Could not solve reCAPTCHA");await Promise.race([u.lock("ready"),O(o).then(()=>{throw new v("No Audio Found")})]),await i.waitForSelector("#audio-source",{state:"attached",timeout:o}),await i.waitForSelector("#audio-response",{timeout:o}),n("reconized:",await F);let r=await i.$("#audio-response");if(r===null)throw new Error("Could not find reCAPTCHA audio input");await r.type(await F,{delay:t});let s=await i.$("#recaptcha-verify-button");if(s===null)throw new Error("Could not find reCAPTCHA verify button");await s.click(),await u.lock("done"),n("passed:",p)}return e.off("response",$),!0}function Y(){let e=x.default.resolve(W.default.tmpdir(),"reSOLVER-"+Math.random().toString().slice(2));return d.default.existsSync(e)&&d.default.rmSync(e,{recursive:!0}),d.default.mkdirSync(e,{recursive:!0}),e}function Z(e,t="ffmpeg"){(0,N.spawnSync)(t,["-loglevel","error","-i",S,"-acodec","pcm_s16le","-ac","1","-ar","16000",M],{cwd:e,stdio:process.env.VERBOSE?"inherit":"ignore"})}function ee(e){return new Promise(t=>{let o=d.default.createReadStream(x.default.resolve(e,M),{highWaterMark:4096}),a=new q.default.Reader,c=new B.Readable().wrap(a);a.on("format",async({audioFormat:w,sampleRate:A,channels:i})=>{if(w!=1||i!=1)throw new Error("Audio file must be WAV with mono PCM.");let l=new k.default.Recognizer({model:X,sampleRate:A});l.setMaxAlternatives(10),l.setWords(!0),l.setPartialWords(!0);for await(let C of c)if(l.acceptWaveform(C)){let b=l.result().alternatives.sort((u,p)=>p.confidence-u.confidence)[0].text;o.close(()=>t(b))}l.free()}),o.pipe(a)})}async function te(e,t="ffmpeg"){let o=Y();d.default.writeFileSync(x.default.resolve(o,S),await e.body()),Z(o,t);let a=await ee(o);return d.default.rmSync(o,{recursive:!0}),a}0&&(module.exports={BFRAME,MAIN_FRAME,NotFoundError,exists,solve});