import { AxiosInstance } from 'axios'; import { randomStr, sleep } from './index'; import { CreateAxiosProxy } from './proxyAgent'; import { Page } from 'puppeteer'; import CDP from 'chrome-remote-interface'; import { Config } from './config'; import fs from 'fs'; import puppeteer from 'puppeteer-extra'; import { closeOtherPages } from './puppeteer'; class CaptchaSolver { private readonly apiKey: string; private client: AxiosInstance; constructor(apiKey: string) { this.apiKey = apiKey; this.client = CreateAxiosProxy({ baseURL: 'http://2captcha.com' }, false); } async sendCaptcha(base64Image: string): Promise { const response = await this.client.post(`/in.php`, { method: 'base64', key: this.apiKey, body: base64Image, json: 1, phrase: 1, min_len: 6, max_len: 6, regsense: 1, textinstructions: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', }); const data = response.data; if (data.status !== 1) { throw new Error(`Failed to send captcha: ${data.request}`); } return data.request; // This will return the captcha ID } async getCaptchaResult(captchaId: string): Promise { let attempts = 0; while (attempts < 10) { // for example, trying 10 times before giving up const response = await this.client.get(`/res.php`, { params: { key: this.apiKey, action: 'get', id: captchaId, json: 1, }, }); const data = response.data; if (data.status === 1) { return data.request; } if (data.request !== 'CAPCHA_NOT_READY') { throw new Error(`Failed to retrieve captcha result: ${data.request}`); } await sleep(10000); attempts++; } throw new Error('Max attempts reached.'); } } export async function getCaptchaCode(base64: string) { if (!process.env.CAPTCHA2_APIKEY) { throw new Error('not config CAPTCHA2_APIKEY in env'); } const solver = new CaptchaSolver(process.env.CAPTCHA2_APIKEY); try { const captchaId = await solver.sendCaptcha(base64); const captchaResult = await solver.getCaptchaResult(captchaId); return captchaResult.replace(/[^a-zA-Z0-9]/g, ''); } catch (error: any) { console.error('Error:', error.message); return ''; } } export async function ifCF(page: Page) { try { await page.waitForSelector('#challenge-running', { timeout: 2 * 1000 }); return true; } catch (e) { console.log('no cf'); return false; } } export async function handleCF( page: Page, debug: boolean = false, ): Promise { if (!(await ifCF(page))) { return page; } const browser = page.browser(); const url = page.url(); const pageIdx = (await browser.pages()).findIndex((v) => v === page); const wsEndpoint = browser.wsEndpoint(); browser.disconnect(); console.log('handle cf start'); const client: CDP.Client = await CDP({ target: wsEndpoint, }); try { const targets = await client.Target.getTargets(); await sleep(10000); const target = targets.targetInfos.find((v) => v.url.indexOf(url) > -1); if (!target) { throw new Error('not found target'); } const { sessionId } = await client.Target.attachToTarget({ targetId: target.targetId, flatten: true, }); // 设置页面尺寸 await client.Page.enable(sessionId); await client.Page.setDeviceMetricsOverride( { width: 1280, height: 720, deviceScaleFactor: 1, mobile: false, }, sessionId, ); await client.Runtime.enable(sessionId); await client.DOM.enable(sessionId); const iframeExpression = `document.querySelector('iframe')`; const { result: iframeResult } = await client.Runtime.evaluate( { expression: iframeExpression, }, sessionId, ); const { objectId: iframeObjectId } = iframeResult; const { model: iframeModel } = await client.DOM.getBoxModel( { objectId: iframeObjectId, }, sessionId, ); const iframeCoordinates = iframeModel.border; // iframe 的坐标 let [x, y] = iframeCoordinates; x = x + 9 + 14 + 8; y = y + 10 + 14 + 8; if (debug) { await client.Runtime.enable(sessionId); await client.Runtime.evaluate( { expression: `const dot = document.createElement('div'); dot.style.width = '100px'; dot.style.height = '100px'; dot.style.background = 'red'; dot.style.position = 'fixed'; dot.style.left = ${x} + 'px'; dot.style.top = ${y} + 'px'; document.body.appendChild(dot);`, }, sessionId, ); await client.Page.enable(sessionId); const res = await client.Page.captureScreenshot( { format: 'png' }, sessionId, ); fs.writeFileSync(`./run/png/${randomStr(6)}.png`, res.data, 'base64'); } await client.Input.dispatchMouseEvent( { type: 'mousePressed', x, y, button: 'left', clickCount: 1, }, sessionId, ); await client.Input.dispatchMouseEvent( { type: 'mouseReleased', x, y, button: 'left', clickCount: 1, }, sessionId, ); await sleep(20 * 1000); } catch (e) { await client.Browser.close(); throw e; } console.log('handle cf end'); const newB = await puppeteer.connect({ browserWSEndpoint: wsEndpoint }); return (await newB.pages()).find( (v) => v.url().indexOf('blank') === -1, ) as Page; }