diff --git a/README.md b/README.md index df150db..921a554 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,12 @@ Some websites track user mouse movements and listen for keyup events of fn keys. [demo4-proxy.js](demo/demo4-proxy.js), Support socks5, http, https proxy. +--- + +#### ✅ Demo7: Use other puppeteer-extra plugins + +[demo7-use-plugin.js](demo/demo7-use-plugin.js), Other popular plugins can be used e.g: [puppeteer-extra-plugin-recaptcha](https://www.npmjs.com/package/puppeteer-extra-plugin-recaptcha) + ---- ## 🐱 Bot / Fingerprint detection sites diff --git a/demo/demo7-use-plugin.js b/demo/demo7-use-plugin.js new file mode 100644 index 0000000..472c64e --- /dev/null +++ b/demo/demo7-use-plugin.js @@ -0,0 +1,26 @@ +const {FakeBrowser} = require('fakebrowser') +const RecaptchaPlugin = require('puppeteer-extra-plugin-recaptcha') + +!(async () => { + const builder = new FakeBrowser.Builder() + .displayUserActionLayer(true) + .vanillaLaunchOptions({ + headless: false, + }) + .usePlugins([ + RecaptchaPlugin({ + provider: { + id: '2captcha', + token: 'XXXXXXX', // REPLACE THIS WITH YOUR OWN 2CAPTCHA API KEY ⚡ + }, + visualFeedback: true, // colorize reCAPTCHAs (violet = detected, green = solved) + }), + ]) + .userDataDir('./fakeBrowserUserData7') + + const fakeBrowser = await builder.launch() + const page = await fakeBrowser.vanillaBrowser.newPage() + await page.goto('https://google.com') + +})() + diff --git a/package.json b/package.json index dcb9c95..5f453e5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fakebrowser", - "version": "0.0.65", + "version": "0.0.66", "description": "🤖 Fake fingerprints to bypass anti-bot systems. Simulate mouse and keyboard operations to make behavior like a real person.", "repository": { "type": "git", diff --git a/src/core/BrowserBuilder.ts b/src/core/BrowserBuilder.ts index e83d6bf..e358aa8 100644 --- a/src/core/BrowserBuilder.ts +++ b/src/core/BrowserBuilder.ts @@ -11,9 +11,10 @@ import { VanillaLaunchOptions, } from './Driver.js' -import {DeviceDescriptor} from './DeviceDescriptor.js' -import {BrowserLauncher} from './BrowserLauncher' -import {FakeBrowser, kDefaultWindowsDD} from './FakeBrowser' +import { DeviceDescriptor } from './DeviceDescriptor.js' +import { BrowserLauncher } from './BrowserLauncher' +import { FakeBrowser, kDefaultWindowsDD } from './FakeBrowser' +import { PuppeteerExtraPlugin } from 'puppeteer-extra' export class BrowserBuilder { @@ -52,6 +53,7 @@ export class BrowserBuilder { 'workers', 'keyboard', ].map(e => path.resolve(__dirname, `../plugins/evasions/${e}`)), + usePlugins: [], } } @@ -119,6 +121,11 @@ export class BrowserBuilder { return this } + usePlugins(value: PuppeteerExtraPlugin[]) { + this.driverParams.usePlugins = value + return this + } + async launch(): Promise { if ('undefined' === typeof this.launchParams.maxSurvivalTime) { this.launchParams.maxSurvivalTime = FakeBrowser.globalConfig.defaultBrowserMaxSurvivalTime diff --git a/src/core/Driver.ts b/src/core/Driver.ts index 4d3c732..77b0124 100644 --- a/src/core/Driver.ts +++ b/src/core/Driver.ts @@ -1,12 +1,12 @@ -import {strict as assert} from 'assert' +import { strict as assert } from 'assert' import * as fs from 'fs-extra' -import {addExtra, PuppeteerExtra} from 'puppeteer-extra' -import {Browser, BrowserConnectOptions, BrowserLaunchArgumentOptions, ConnectOptions, LaunchOptions} from 'puppeteer' +import { addExtra, PuppeteerExtra, PuppeteerExtraPlugin } from 'puppeteer-extra' +import { Browser, BrowserConnectOptions, BrowserLaunchArgumentOptions, ConnectOptions, LaunchOptions } from 'puppeteer' -import DeviceDescriptorHelper, {DeviceDescriptor, FakeDeviceDescriptor} from './DeviceDescriptor.js' -import {UserAgentHelper} from './UserAgentHelper.js' -import {PptrPatcher} from './PptrPatcher' +import DeviceDescriptorHelper, { DeviceDescriptor, FakeDeviceDescriptor } from './DeviceDescriptor.js' +import { UserAgentHelper } from './UserAgentHelper.js' +import { PptrPatcher } from './PptrPatcher' export interface ProxyServer { proxyType: 'socks5' | 'socks4' | 'http' | 'https', @@ -29,6 +29,7 @@ export interface DriverParameters { proxy?: ProxyServer, userDataDir?: string, evasionPaths: string[], + usePlugins: PuppeteerExtraPlugin[], } export interface LaunchParameters extends DriverParameters { @@ -221,7 +222,7 @@ export default class Driver { const userDataDir = launchParams.userDataDir assert(userDataDir) - fs.mkdirSync(userDataDir, {recursive: true}) // throw exception + fs.mkdirSync(userDataDir, { recursive: true }) // throw exception args.push( `--user-data-dir=${userDataDir}`, diff --git a/src/core/PptrPatcher.ts b/src/core/PptrPatcher.ts index 28a336c..76cf78d 100644 --- a/src/core/PptrPatcher.ts +++ b/src/core/PptrPatcher.ts @@ -1,16 +1,16 @@ // noinspection JSUnusedLocalSymbols,JSUnusedGlobalSymbols import * as path from 'path' -import {strict as assert} from 'assert' +import { strict as assert } from 'assert' import axios from 'axios' -import {CDPSession, Protocol} from 'puppeteer' -import {PuppeteerExtra, PuppeteerExtraPlugin} from 'puppeteer-extra' +import { CDPSession, Protocol } from 'puppeteer' +import { PuppeteerExtra, PuppeteerExtraPlugin } from 'puppeteer-extra' -import {helper} from './helper' -import {DriverParameters} from './Driver' -import {FakeBrowser} from './FakeBrowser' -import {FakeDeviceDescriptor} from './DeviceDescriptor' +import { helper } from './helper' +import { DriverParameters } from './Driver' +import { FakeBrowser } from './FakeBrowser' +import { FakeDeviceDescriptor } from './DeviceDescriptor' interface PptrExtraEvasionOpts { browserUUID: string, @@ -49,6 +49,11 @@ export class PptrPatcher { pptr.use(plugin) } + // other plugins + for (const plugin of params.usePlugins) { + pptr.use(plugin) + } + // last, tidy up await this.patchLast(browserUUID, pptr, params, opts) } @@ -153,18 +158,18 @@ tmpVarNames.forEach(e => { if (responseHeaders && responseHeaders.length) { let body: string - ;({body, base64Encoded} = await client.send('Fetch.getResponseBody', {requestId})) + ;({ body, base64Encoded } = await client.send('Fetch.getResponseBody', { requestId })) jsContent = base64Encoded ? Buffer.from(body, 'base64').toString('utf-8') : body } else { // TODO: get through proxy - const jsResp = await axios.get(request.url, {headers: request.headers}) + const jsResp = await axios.get(request.url, { headers: request.headers }) jsContent = jsResp.data responseHeaders = Object.entries( jsResp.headers, ).map( - e => ({name: e[0], value: e[1] as string}), + e => ({ name: e[0], value: e[1] as string }), ) } @@ -181,7 +186,7 @@ tmpVarNames.forEach(e => { return true } catch (ex: any) { console.error('SW inject failed', ex) - await client.send('Fetch.failRequest', {requestId, errorReason: 'Aborted'}) + await client.send('Fetch.failRequest', { requestId, errorReason: 'Aborted' }) } return false