Skip to content

Instantly share code, notes, and snippets.

@Hricha-Shandily
Created May 15, 2025 18:02
Show Gist options
  • Select an option

  • Save Hricha-Shandily/8510495f597fc70a8d7d4294c3120878 to your computer and use it in GitHub Desktop.

Select an option

Save Hricha-Shandily/8510495f597fc70a8d7d4294c3120878 to your computer and use it in GitHub Desktop.
/**
* Browser Bot Script
* -----------------
*
* This script uses Puppeteer to simulate real browser behavior by:
* - Using random viewport sizes and user agents
* - Simulating human-like scrolling (50% chance)
* - Clicking FAQ links (30% chance)
*
* Usage:
*
* TARGET_URL=https://example.com node index.js
*
*/
const puppeteer = require("puppeteer");
// Common user agents for modern browsers
const USER_AGENTS = [
// "PostmanRuntime/7.43.4",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15",
];
// Common screen resolutions
// Function to add random variation to viewport dimensions
function addRandomVariation(size) {
const variation = () => Math.floor(Math.random() * 81) - 40; // Random number between -40 and +40
return {
width: size.width + variation(),
height: size.height + variation(),
};
}
const BASE_VIEWPORT_SIZES = [
{ width: 1920, height: 1080 },
{ width: 1366, height: 768 },
{ width: 1536, height: 864 },
{ width: 1440, height: 900 },
{ width: 1280, height: 720 },
];
// Common languages and regions
const LANGUAGES = [
"en-US,en;q=0.9",
"en-GB,en;q=0.9",
"es-ES,es;q=0.9,en;q=0.8",
"fr-FR,fr;q=0.9,en;q=0.8",
"de-DE,de;q=0.9,en;q=0.8",
];
function getRandomElement(array) {
return array[Math.floor(Math.random() * array.length)];
}
async function visitPage(url) {
let browser;
try {
// Launch the browser with specific configurations
browser = await puppeteer.launch({
headless: "new",
args: [
"--no-sandbox",
"--disable-setuid-sandbox",
"--disable-blink-features=AutomationControlled",
"--disable-infobars",
"--window-position=0,0",
"--ignore-certifcate-errors",
"--ignore-certifcate-errors-spki-list",
"--disable-notifications",
"--disable-geolocation",
],
});
// Get random configurations
const userAgent = getRandomElement(USER_AGENTS);
const baseViewport = getRandomElement(BASE_VIEWPORT_SIZES);
const viewport = addRandomVariation(baseViewport);
const language = getRandomElement(LANGUAGES);
// Create a new page
const page = await browser.newPage();
// Configure browser fingerprint
await page.setUserAgent(userAgent);
await page.setViewport({
...viewport,
deviceScaleFactor: 1,
hasTouch: false,
isLandscape: true,
});
// Set common headers
await page.setExtraHTTPHeaders({
"Accept-Language": language,
Accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate, br",
DNT: "1",
Connection: "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
});
// Mask webdriver
await page.evaluateOnNewDocument(() => {
// Overwrite the navigator
Object.defineProperty(navigator, "webdriver", {
get: () => false,
});
// Overwrite chrome properties
window.chrome = {
runtime: {},
};
// Overwrite permissions
const originalQuery = window.navigator.permissions.query;
window.navigator.permissions.query = (parameters) =>
parameters.name === "notifications"
? Promise.resolve({ state: Notification.permission })
: originalQuery(parameters);
});
console.log(`Navigating to ${url} with User-Agent: ${userAgent}`);
// Navigate to the URL with extended options
await page.goto(url, {
waitUntil: ["networkidle0", "domcontentloaded", "load"],
timeout: 30000,
});
// Add random delay to simulate human behavior
await new Promise((resolve) =>
setTimeout(resolve, Math.floor(Math.random() * 2000) + 1000),
);
console.log("Page loaded successfully!");
const title = await page.title();
const metrics = await page.metrics();
// 50% chance of random scrolling
if (Math.random() < 0.5) {
console.log("Starting random scrolling...");
const startTime = Date.now();
while (Date.now() - startTime < 2000) {
const scrollAmount = Math.floor(Math.random() * 100) - 30; // Random scroll between -30 and +70
await page.evaluate((scrollY) => {
window.scrollBy(0, scrollY);
}, scrollAmount);
await new Promise((resolve) => setTimeout(resolve, 100)); // Small delay between scrolls
}
} else {
console.log("Skipping scrolling for this session");
}
// 30% chance of clicking FAQ link
if (Math.random() < 0.3) {
console.log("Looking for FAQ link...");
try {
// Wait for any FAQ link to be available
await page.waitForSelector('a[href="/faq"], a[href="#/faq"]', {
timeout: 5000,
});
// Click the first matching FAQ link
await page.click('a[href="/faq"], a[href="#/faq"]');
console.log("Clicked FAQ link successfully");
// Wait for navigation or content change
await new Promise((resolve) => setTimeout(resolve, 2000));
} catch (error) {
console.log("Could not find or click FAQ link:", error.message);
}
} else {
console.log("Skipping FAQ click for this session");
}
console.log("Page Title:", title);
console.log("Page Metrics:", {
JSHeapUsedSize: Math.round(metrics.JSHeapUsedSize / 1024 / 1024) + "MB",
Nodes: metrics.Nodes,
Scripts: metrics.Scripts,
});
} catch (error) {
console.error("An error occurred:", error);
} finally {
// Cleanup
if (browser) {
await browser.close();
console.log("Browser closed.");
}
}
}
// Export the visitPage function
module.exports = {
visitPage,
};
// Run directly if this is the main module
if (require.main === module) {
const targetUrl = process.env.TARGET_URL || "http://localhost:4321";
visitPage(targetUrl).catch(console.error);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment