recaptcha-solver
Version:
Solve reCAPTCHA challenges by using offline speech recognition.
3 lines (2 loc) • 4.75 kB
JavaScript
import{fileURLToPath as L}from"url";import O from"path";var I=()=>L(import.meta.url),N=()=>O.dirname(I()),u=N();import{spawnSync as q}from"child_process";import m from"fs";import R from"path";import D from"os";import{Readable as U}from"stream";import S from"vosk-koffi";import V from"wav";function n(...e){process.env.VERBOSE&&console.log("[reCAPTCHA solver]",...e)}import W from"path";var T=W.resolve(u,"..","model"),y="sound.mp3",F="out.wav",C="iframe[title='reCAPTCHA']",f="iframe[src^='https://www.google.com/recaptcha/api2/bframe']:visible, iframe[src^='https://www.google.com/recaptcha/enterprise/bframe']:visible",b="body > div > div";var E=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 $(e){return new Promise(t=>setTimeout(t,e))}async function B(e){return!!e.$(f)}S.setLogLevel(-1);var z=new S.Model(T),g=class extends Error{constructor(t){super(t),this.name="NotFoundError"}};async function j(e,{delay:t=64,wait:o=5e3,retry:c=3,ffmpeg:_="ffmpeg"}={}){try{await e.waitForSelector(f,{state:"attached"})}catch{throw new Error("No reCAPTCHA detected")}let d=!1,w=await e.$(f);if(w===null)throw new Error("Could not find reCAPTCHA popup iframe");let i=await w.contentFrame();if(i===null)throw new Error("Could not find reCAPTCHA popup iframe content");let s=!!await i.$(b);if(n("bframe loaded:",s),s===!1){await e.waitForSelector(C,{state:"attached"});let r=await e.$(C);if(r===null)throw new Error("Could not find reCAPTCHA iframe");let a=await r.contentFrame();if(a===null)throw new Error("Could not find reCAPTCHA iframe content");if(d=!!await a.$("div.rc-anchor-invisible"),n("invisible:",d),d===!0)return!1;{let k=await a.$("#recaptcha-anchor-label");if(k===null)throw new Error("Could not find reCAPTCHA label");await k.click(),await i.waitForSelector(b)}}let h=await i.$(b);if(h===null)throw new Error("Could not find reCAPTCHA challenge");let P=await h.evaluate(r=>!r.classList.contains("rc-footer"));if(n("action required:",P),P===!1)return!1;await i.waitForSelector("#recaptcha-audio-button",{timeout:o});let v=await i.$("#recaptcha-audio-button");if(v===null)throw new Error("Could not find reCAPTCHA audio button");let l=new E;await l.lock("init");let p=!1,x=Promise.resolve(""),M=async r=>{if(r.headers()["content-type"]==="audio/mp3")n(`got audio from ${r.url()}`),x=new Promise(a=>{Q(r,_).then(a).catch(()=>{})}),l.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 a=(await r.body()).toString().replace(`)]}'
`,"");p=JSON.parse(a)[2]===1,l.unlock("verified")}};e.on("response",M),await v.click();let H=0;for(;p===!1;){if(H++>=c)throw new Error("Could not solve reCAPTCHA");await Promise.race([l.lock("ready"),$(o).then(()=>{throw new g("No Audio Found")})]),await i.waitForSelector("#audio-source",{state:"attached",timeout:o}),await i.waitForSelector("#audio-response",{timeout:o}),n("reconized:",await x);let r=await i.$("#audio-response");if(r===null)throw new Error("Could not find reCAPTCHA audio input");await r.type(await x,{delay:t});let a=await i.$("#recaptcha-verify-button");if(a===null)throw new Error("Could not find reCAPTCHA verify button");await a.click(),await l.lock("done"),n("passed:",p)}return e.off("response",M),!0}function G(){let e=R.resolve(D.tmpdir(),"reSOLVER-"+Math.random().toString().slice(2));return m.existsSync(e)&&m.rmSync(e,{recursive:!0}),m.mkdirSync(e,{recursive:!0}),e}function J(e,t="ffmpeg"){q(t,["-loglevel","error","-i",y,"-acodec","pcm_s16le","-ac","1","-ar","16000",F],{cwd:e,stdio:process.env.VERBOSE?"inherit":"ignore"})}function K(e){return new Promise(t=>{let o=m.createReadStream(R.resolve(e,F),{highWaterMark:4096}),c=new V.Reader,_=new U().wrap(c);c.on("format",async({audioFormat:d,sampleRate:w,channels:i})=>{if(d!=1||i!=1)throw new Error("Audio file must be WAV with mono PCM.");let s=new S.Recognizer({model:z,sampleRate:w});s.setMaxAlternatives(10),s.setWords(!0),s.setPartialWords(!0);for await(let h of _)if(s.acceptWaveform(h)){let v=s.result().alternatives.sort((l,p)=>p.confidence-l.confidence)[0].text;o.close(()=>t(v))}s.free()}),o.pipe(c)})}async function Q(e,t="ffmpeg"){let o=G();m.writeFileSync(R.resolve(o,y),await e.body()),J(o,t);let c=await K(o);return m.rmSync(o,{recursive:!0}),c}export{f as BFRAME,C as MAIN_FRAME,g as NotFoundError,B as exists,j as solve};