147 lines
4.1 KiB
JavaScript
147 lines
4.1 KiB
JavaScript
function randomSleep(minMs = 2000, maxMs = 5000) {
|
|
const delay = Math.floor(Math.random() * (maxMs - minMs + 1)) + minMs;
|
|
return new Promise((res) => setTimeout(res, delay));
|
|
}
|
|
|
|
async function humanLikeMouseMovement(page, steps = 10) {
|
|
// Simulate human-like mouse movements across the page
|
|
const viewport = await page.viewport();
|
|
const width = viewport.width;
|
|
const height = viewport.height;
|
|
|
|
for (let i = 0; i < steps; i++) {
|
|
const x = Math.floor(Math.random() * width);
|
|
const y = Math.floor(Math.random() * height);
|
|
|
|
await page.mouse.move(x, y, { steps: Math.floor(Math.random() * 10) + 5 });
|
|
await randomSleep(100, 500);
|
|
}
|
|
}
|
|
|
|
async function randomScroll(page, scrollCount = 3) {
|
|
// Perform random scrolling to simulate human behavior
|
|
for (let i = 0; i < scrollCount; i++) {
|
|
const scrollAmount = Math.floor(Math.random() * 300) + 100;
|
|
await page.evaluate((amount) => {
|
|
window.scrollBy(0, amount);
|
|
}, scrollAmount);
|
|
await randomSleep(800, 1500);
|
|
}
|
|
}
|
|
|
|
async function simulateHumanBehavior(page, options = {}) {
|
|
// Combined function to simulate various human-like behaviors
|
|
const { mouseMovements = 5, scrolls = 2, randomClicks = false } = options;
|
|
|
|
// Random mouse movements
|
|
if (mouseMovements > 0) {
|
|
await humanLikeMouseMovement(page, mouseMovements);
|
|
}
|
|
|
|
// Random scrolling
|
|
if (scrolls > 0) {
|
|
await randomScroll(page, scrolls);
|
|
}
|
|
|
|
// Optional: Random clicks on non-interactive elements
|
|
if (randomClicks) {
|
|
try {
|
|
await page.evaluate(() => {
|
|
const elements = document.querySelectorAll("div, span, p");
|
|
if (elements.length > 0) {
|
|
const randomElement =
|
|
elements[Math.floor(Math.random() * elements.length)];
|
|
const rect = randomElement.getBoundingClientRect();
|
|
// Just move to it, don't actually click to avoid triggering actions
|
|
}
|
|
});
|
|
} catch (err) {
|
|
// Ignore errors from random element selection
|
|
}
|
|
}
|
|
|
|
await randomSleep(500, 1000);
|
|
}
|
|
|
|
async function withRetry(fn, options = {}) {
|
|
const {
|
|
maxRetries = 3,
|
|
initialDelay = 2000,
|
|
maxDelay = 30000,
|
|
shouldRetry = (error) => true,
|
|
} = options;
|
|
|
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
try {
|
|
return await fn();
|
|
} catch (error) {
|
|
const isLastAttempt = attempt === maxRetries - 1;
|
|
|
|
// Check if we should retry this error
|
|
if (!shouldRetry(error) || isLastAttempt) {
|
|
throw error;
|
|
}
|
|
|
|
// Calculate exponential backoff delay: 2s, 4s, 8s, 16s, 30s (capped)
|
|
const exponentialDelay = Math.min(
|
|
initialDelay * Math.pow(2, attempt),
|
|
maxDelay
|
|
);
|
|
|
|
// Add jitter (randomize ±20%) to avoid thundering herd
|
|
const jitter = exponentialDelay * (0.8 + Math.random() * 0.4);
|
|
const delay = Math.floor(jitter);
|
|
|
|
console.log(
|
|
`Retry attempt ${attempt + 1}/${maxRetries} after ${delay}ms delay...`
|
|
);
|
|
console.log(`Error: ${error.message || error}`);
|
|
|
|
await randomSleep(delay, delay);
|
|
}
|
|
}
|
|
}
|
|
|
|
async function handleRateLimitedRequest(page, requestFn, context = "") {
|
|
return withRetry(requestFn, {
|
|
maxRetries: 5,
|
|
initialDelay: 2000,
|
|
maxDelay: 60000,
|
|
shouldRetry: (error) => {
|
|
// Retry on rate limit (429) or temporary errors
|
|
if (error.status === 429 || error.statusCode === 429) {
|
|
console.log(`Rate limited (429) ${context}. Backing off...`);
|
|
return true;
|
|
}
|
|
|
|
// Retry on 5xx server errors
|
|
if (error.status >= 500 || error.statusCode >= 500) {
|
|
console.log(
|
|
`Server error (${
|
|
error.status || error.statusCode
|
|
}) ${context}. Retrying...`
|
|
);
|
|
return true;
|
|
}
|
|
|
|
// Retry on network errors
|
|
if (error.code === "ECONNRESET" || error.code === "ETIMEDOUT") {
|
|
console.log(`Network error (${error.code}) ${context}. Retrying...`);
|
|
return true;
|
|
}
|
|
|
|
// Don't retry on client errors (4xx except 429)
|
|
return false;
|
|
},
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
randomSleep,
|
|
humanLikeMouseMovement,
|
|
randomScroll,
|
|
simulateHumanBehavior,
|
|
withRetry,
|
|
handleRateLimitedRequest,
|
|
};
|