fakebrowser
Version:
🤖 Fake fingerprints to bypass anti-bot systems. Simulate mouse and keyboard operations to make behavior like a real person.
220 lines • 16.2 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.kDefaultLaunchOptions = exports.kDefaultTimeout = void 0;
const assert_1 = require("assert");
const fs = __importStar(require("fs-extra"));
const puppeteer_extra_1 = require("puppeteer-extra");
const DeviceDescriptor_js_1 = __importDefault(require("./DeviceDescriptor.js"));
const UserAgentHelper_js_1 = require("./UserAgentHelper.js");
const PptrPatcher_1 = require("./PptrPatcher");
exports.kDefaultTimeout = 15 * 1000;
exports.kDefaultLaunchOptions = {
headless: true,
devtools: false,
timeout: exports.kDefaultTimeout,
};
class Driver {
static checkParamsLegal(params) {
// deviceDesc must be set
const dd = params.deviceDesc;
(0, assert_1.strict)(dd, 'deviceDesc must be set');
DeviceDescriptor_js_1.default.checkLegal(dd);
// user data dir
// The userDataDir in launchParameters must be set
(0, assert_1.strict)(params.userDataDir, 'userDataDir must be set');
}
/**
* Connect to browser
* @param uuid
* @param params
*/
static async connect(uuid, params) {
// Different instances with different puppeteer configurations
const pptr = (0, puppeteer_extra_1.addExtra)(require('puppeteer'));
// patch with evasions
if (!params.doNotHook) {
await PptrPatcher_1.PptrPatcher.patch(uuid, pptr, params);
}
const fakeDD = params.fakeDeviceDesc;
(0, assert_1.strict)(!!fakeDD);
const browser = await pptr.connect(params.connectOptions);
await this.patchUAFromLaunchedBrowser(browser, fakeDD);
return {
vanillaBrowser: browser,
pptrExtra: pptr,
};
}
/**
* Launch browser
* @param uuid
* @param defaultLaunchArgs
* @param params
*/
static async launch(uuid, defaultLaunchArgs, params) {
this.checkParamsLegal(params);
if (!params.launchOptions
|| Object.keys(params.launchOptions).length === 0) {
params.launchOptions = exports.kDefaultLaunchOptions;
}
this.patchLaunchArgs(defaultLaunchArgs, params);
// Different instances with different puppeteer configurations
const pptr = (0, puppeteer_extra_1.addExtra)(require('puppeteer'));
// patch with evasions
if (!params.doNotHook) {
await PptrPatcher_1.PptrPatcher.patch(uuid, pptr, params);
}
const fakeDD = params.fakeDeviceDesc;
(0, assert_1.strict)(!!fakeDD);
const browser = await pptr.launch(params.launchOptions);
await this.patchUAFromLaunchedBrowser(browser, fakeDD);
return {
vanillaBrowser: browser,
pptrExtra: pptr,
};
}
static async patchUAFromLaunchedBrowser(browser, fakeDD) {
// read major version from the launched browser and replace dd.userAgent
const orgUA = await browser.userAgent();
const orgVersion = UserAgentHelper_js_1.UserAgentHelper.chromeVersion(orgUA);
const fakeVersion = UserAgentHelper_js_1.UserAgentHelper.chromeVersion(fakeDD.navigator.userAgent);
(0, assert_1.strict)(orgVersion);
(0, assert_1.strict)(fakeVersion);
fakeDD.navigator.userAgent = fakeDD.navigator.userAgent.replace(fakeVersion, orgVersion);
fakeDD.navigator.appVersion = fakeDD.navigator.appVersion.replace(fakeVersion, orgVersion);
}
static patchLaunchArgs(defaultLaunchArgs, launchParams) {
// args
// noinspection SuspiciousTypeOfGuard
(0, assert_1.strict)(defaultLaunchArgs instanceof Array);
const args = [
...defaultLaunchArgs,
...(launchParams.launchOptions.args || []),
];
const fakeDD = launchParams.fakeDeviceDesc;
(0, assert_1.strict)(!!fakeDD);
// Modify default options
launchParams.launchOptions = {
ignoreHTTPSErrors: true,
ignoreDefaultArgs: [
'--enable-automation',
'--enable-blink-features=IdleDetection',
],
handleSIGINT: false,
handleSIGTERM: false,
handleSIGHUP: false,
pipe: true,
defaultViewport: {
width: fakeDD.window.innerWidth,
height: fakeDD.window.innerHeight,
deviceScaleFactor: fakeDD.window.devicePixelRatio,
isMobile: UserAgentHelper_js_1.UserAgentHelper.isMobile(fakeDD.navigator.userAgent),
hasTouch: fakeDD.navigator.maxTouchPoints > 0,
isLandscape: false,
},
...launchParams.launchOptions,
args,
};
// headless
let headless = launchParams.launchOptions.headless;
if ('undefined' === typeof headless) {
headless = true;
}
if (launchParams.launchOptions.devtools) {
headless = false;
}
// proxy
if (launchParams.proxy) {
args.push(`--proxy-server=${launchParams.proxy.proxy}`);
}
// browser language
(0, assert_1.strict)(fakeDD.acceptLanguage);
args.push(`--lang=${fakeDD.acceptLanguage}`);
const userDataDir = launchParams.userDataDir;
(0, assert_1.strict)(userDataDir);
fs.mkdirSync(userDataDir, { recursive: true }); // throw exception
args.push(`--user-data-dir=${userDataDir}`);
// window position & window size
let { screenX, screenY, innerWidth, innerHeight, outerWidth, outerHeight, } = fakeDD.window;
outerWidth = outerWidth || innerWidth;
outerHeight = outerHeight || (innerHeight + 85);
args.push(`--window-position=${screenX},${screenY}`, `--window-size=${outerWidth},${outerHeight}`);
// Some options can only be used in headless.
// If you use them again in headful, you will see a plain white browser window without any content.
if (headless) {
args.push('--in-process-gpu', // https://source.chromium.org/search?q=lang:cpp+symbol:kInProcessGPU&ss=chromium
'--disable-canvas-aa', // Disable antialiasing on 2d canvas
'--disable-2d-canvas-clip-aa', // Disable antialiasing on 2d canvas clips
'--disable-gl-drawing-for-tests');
}
}
static async getPids(pid) {
if ('string' === typeof (pid)) {
pid = parseInt(pid);
}
try {
const pidtree = require('pidtree');
const pids = await pidtree(pid);
return pids.includes(pid) ? pids : [...pids, pid];
}
catch (ignored) {
return [pid];
}
}
/**
* Shutdown browser
* @param browser
*/
static async shutdown(browser) {
try {
const pages = await browser.pages();
for (const page of pages) {
await page.close();
}
}
catch (ignored) {
}
const browserProcess = browser.process();
if (browserProcess) {
const pid = browserProcess.pid;
if (pid) {
const pids = await this.getPids(pid);
pids.forEach(pid => {
try {
process.kill(pid, 'SIGKILL');
}
catch (ignored) {
}
});
}
}
try {
await browser.close();
}
catch (ignored) {
}
}
}
exports.default = Driver;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRHJpdmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvcmUvRHJpdmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxtQ0FBeUM7QUFDekMsNkNBQThCO0FBRTlCLHFEQUFnRjtBQUdoRixnRkFBc0c7QUFDdEcsNkRBQXNEO0FBQ3RELCtDQUEyQztBQW1DOUIsUUFBQSxlQUFlLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQTtBQUUzQixRQUFBLHFCQUFxQixHQUFHO0lBQ2pDLFFBQVEsRUFBRSxJQUFJO0lBQ2QsUUFBUSxFQUFFLEtBQUs7SUFDZixPQUFPLEVBQUUsdUJBQWU7Q0FDM0IsQ0FBQTtBQUVELE1BQXFCLE1BQU07SUFFZixNQUFNLENBQUMsZ0JBQWdCLENBQUMsTUFBd0I7UUFDcEQseUJBQXlCO1FBQ3pCLE1BQU0sRUFBRSxHQUFxQixNQUFNLENBQUMsVUFBVSxDQUFBO1FBQzlDLElBQUEsZUFBTSxFQUFDLEVBQUUsRUFBRSx3QkFBd0IsQ0FBQyxDQUFBO1FBRXBDLDZCQUFzQixDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUMsQ0FBQTtRQUVyQyxnQkFBZ0I7UUFDaEIsa0RBQWtEO1FBQ2xELElBQUEsZUFBTSxFQUFDLE1BQU0sQ0FBQyxXQUFXLEVBQUUseUJBQXlCLENBQUMsQ0FBQTtJQUN6RCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILE1BQU0sQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUNoQixJQUFZLEVBQ1osTUFBeUI7UUFLekIsOERBQThEO1FBQzlELE1BQU0sSUFBSSxHQUFHLElBQUEsMEJBQVEsRUFBQyxPQUFPLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQTtRQUUzQyxzQkFBc0I7UUFDdEIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUU7WUFDbkIsTUFBTSx5QkFBVyxDQUFDLEtBQUssQ0FDbkIsSUFBSSxFQUNKLElBQUksRUFDSixNQUFNLENBQ1QsQ0FB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