@0xtld/tair-node
Version:
A Node.js package for Tair functionality with configuration, core, and helper modules.
154 lines (123 loc) • 4.14 kB
text/typescript
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;