const axios = require("axios"); const PLAY_AUDIO_BTN_ID = ".audio-captcha-play-button" const wget = require('node-wget'); const crypto = require('crypto'); const fs = require("fs"); const homedir = require('os').homedir(); const dest_dir = homedir + "/wavs/" const AUDIO_CAPTCHA_TRACK = ".audio-captcha-track"; const CAPTCHA_CONTAINER = "#captcha-container"; const WAV_URL_REGEX = "https:.+.wav"; const re = new RegExp(WAV_URL_REGEX); function delay(delayInMs) { return new Promise(resolve => { setTimeout(() => { resolve(2); }, delayInMs); }); } function getRandom() { return Math.floor(Math.random() * 3); } function getRandomWaitTime() { return getRandom() * 1000 } async function sendRequest(fileName, callback) { const fileStream = fs.createReadStream(fileName); let response = await axios.post('http://127.0.0.1:8000', fileStream, { headers: { 'Content-Type': 'wav' } }) if (response.status === 200) { let result = response.data; console.log(result); await callback(result); console.log("will delete wav file: " + fileName); await fs.unlink(fileName, (err) => { if (err) throw err; console.log(fileName + ' was deleted'); }) } else { console.log("error"); await callback([]); console.log("will delete wav file: " + fileName); } } class GeoCaptchaSolver { constructor(page, device, isTerminated) { this.page = page; this.isTerminated = isTerminated this.device = device; } async solve(onResult) { console.log("solve() called.") console.log("play audio") let iframeHandler = await this.page.frameLocator("body > iframe"); let playAudioBtn = await iframeHandler.locator(PLAY_AUDIO_BTN_ID); playAudioBtn.click(); await delay(4000 + getRandomWaitTime()); let captcha_container = await iframeHandler.locator(CAPTCHA_CONTAINER) let html = await captcha_container.innerHTML() console.log("audio_tag: " + html); if (!html.includes("You have been blocked")) { // find wav from html await this.findTextFromWavFile(html, async (number_list) => { console.log("number_list size is " + number_list.length) for (let i = 1; i <= number_list.length; i++) { let selector = "#captcha__audio > div.audio-captcha-input-container > input:nth-child(" + i + ")" // console.log("selector is " + selector) try { await iframeHandler.locator(selector).focus() await iframeHandler.locator(selector).fill("" + number_list[i - 1]) } catch (e) { console.log(e) } // fieldInputs[i].value = number_list[i]; await delay(500 + getRandomWaitTime()); } if (!this.page.isClosed()) { try { let content = await captcha_container.innerHTML() console.log("inner container is " + content) try { if (number_list.length === 0) { onResult(false) } else { onResult(true) } } catch (e) { console.log(e) } } catch (e) { console.log(e) } } } ) } else { await this.device.screenshot({path: crypto.randomUUID() + '.png'}); await this.resetBrowser() onResult(false) } } async findTextFromWavFile(html, callback) { let result = re.exec(html); //create the directory if not exist await fs.promises.mkdir(dest_dir, {recursive: true}); if (result) { let audioUrl = result[0]; let fileName = crypto.randomUUID() + ".wav"; wget({url: audioUrl, dest: dest_dir + fileName}, async function (error, response, body) { if (error) { console.log('--- error:'); console.log(error); // error encountered } else { console.log('download the file successfully'); // send request to get numbers await sendRequest(dest_dir + fileName, callback); } }) } } async resetBrowser() { console.log("will reset browser") await this.device.shell("pm clear com.android.chrome") await delay(1000) await this.device.shell("am set-debug-app --persistent com.android.chrome") await delay(1000) await this.device.shell("pm am start -n com.android.chrome/com.google.android.apps.chrome.Main") await delay(1000) } } module.exports = GeoCaptchaSolver // sendRequest("/Users/lpan/wavs/42f90d53-17c0-4a9b-a585-59eaa737a80d.wav").then(r => { // console.log(r) // })