UNPKG

@0xtld/tair-node

Version:

A Node.js package for Tair functionality with configuration, core, and helper modules.

154 lines (123 loc) 4.14 kB
import puppeteer, { Browser, Page } from 'puppeteer'; import UndetectableBrowser from 'undetected-browser'; // import RealBrowser from 'puppeteer-real-browser' interface PupBrowserConfigs { site_key: string; site_url: string; urls_to_block: string[]; } /** * @description A class to interact with a browser for solving reCAPTCHAs */ class PupBrowser { private browser: Browser | null; private page: Page | null; private configs: PupBrowserConfigs; constructor(configs: PupBrowserConfigs) { this.browser = null; this.page = null; if (!configs.site_key || !configs.site_url) { throw new Error('site_key and site_url are required in the configs'); } this.configs = configs; } async init(): Promise<void> { /** * @description Initialize the browser. Disable more features to prevent detection * @returns Promise<void> */ const UndetectableBMS = new UndetectableBrowser(await puppeteer.launch({ headless: true, devtools: true, args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-session-crashed-bubble', '--disable-accelerated-2d-canvas', '--no-first-run', '--no-zygote', '--single-process', '--noerrdialogs', '--disable-gpu' ], })); const browser = await UndetectableBMS.getBrowser(); // const { browser, page } = await RealBrowser.connect({ // headless: true, // customConfig: {}, // turnstile: false, // connectOption: {}, // disableXvfb: false, // ignoreAllFlags: false, // args: [ // '--no-sandbox', // '--disable-setuid-sandbox', // '--disable-dev-shm-usage', // '--disable-session-crashed-bubble', // '--disable-accelerated-2d-canvas', // '--no-first-run', // '--no-zygote', // '--single-process', // '--noerrdialogs', // '--disable-gpu' // ] // }); this.browser = browser; this.page = await this.browser.newPage(); // this.page = page as any; await this.goto(this.configs.site_url); } async goto(url: string): Promise<void> { if (!this.browser) { await this.init(); } if (!this.page) { throw new Error('Browser not initialized. Call init() first.'); } const urlBlockRequest = this.configs.urls_to_block || []; await this.page.setRequestInterception(true); this.page.on('request', (request) => { const resourceType = request.resourceType(); const blockTypes = ['image', 'media', 'font', 'websocket']; if (blockTypes.includes(resourceType)) { console.log('blocked type', resourceType, request.url()); request.abort(); return; } if (urlBlockRequest.includes(request.url())) { console.log('blocked url', request.url()); request.abort(); } else { request.continue(); } }); await this.page.goto(url, { waitUntil: 'networkidle2' }); } /** * @description Get the reCAPTCHA token for a given action. Just only support reCAPTCHA Enterprise V3 * @param action string * @returns Promise<string> */ async getRecaptchaToken(action: string = 'submit'): Promise<string> { /** * description Execute the reCAPTCHA script and return the token * @param site_key * @param action * @returns Promise<string> */ const script = async (site_key: string, action: string) => { // Wait for the reCAPTCHA script to load while (typeof (window as any).grecaptcha === 'undefined' || typeof (window as any).grecaptcha.enterprise === 'undefined') { await new Promise(resolve => setTimeout(resolve, 100)); } // Execute the reCAPTCHA and return the token const token = await (window as any).grecaptcha.enterprise.execute(site_key, { action }); return token; }; const { site_key } = this.configs; return this.page!.evaluate(script, site_key, action); } } export { PupBrowserConfigs, PupBrowser }; export default PupBrowser;