cf / cloudflare-solver.js
XBotz's picture
Update cloudflare-solver.js
3c0e9c6 unverified
Raw
History Blame Contribute Delete
7.5 kB
'use strict';
const {
connect
} = require('puppeteer-real-browser');
const fs = require('fs');
const WIDTH = 1280;
const HEIGHT = 800;
const ChallengePlatform = {
JAVASCRIPT: 'non-interactive',
MANAGED: 'managed',
INTERACTIVE: 'interactive',
};
const FALLBACK_UA =
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ' +
'(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
function loadLines(fp) {
try {
return fs.readFileSync(fp, 'utf8').split('\n').map(l => l.trim()).filter(Boolean);
} catch {
return [];
}
}
function randomChoice(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
function getChromeUA() {
const agents = loadLines('data/useragents.txt');
return agents.length ? randomChoice(agents) : FALLBACK_UA;
}
function parseProxy(url) {
const u = new URL(url);
return {
serverUrl: `${u.protocol}//${u.hostname}${u.port ? ':' + u.port : ''}`,
username: u.username || null,
password: u.password || null,
};
}
function extractClearance(cookies) {
return cookies.find(c => c.name === 'cf_clearance') || null;
}
function buildCookieString(cookies) {
return cookies.map(c => `${c.name}=${c.value}`).join('; ');
}
async function detectChallenge(page) {
try {
const html = await page.content();
for (const val of Object.values(ChallengePlatform)) {
if (html.includes(`cType: '${val}'`)) return val;
}
return null;
} catch {
return null;
}
}
async function findCFFrame(page) {
try {
const frame = page.frames().find(f => {
try {
return f.url().includes('challenges.cloudflare.com');
} catch {
return false;
}
});
if (frame) return frame;
} catch {
/* ignore */ }
try {
const el = await page.$('iframe[src*="challenges.cloudflare.com"]');
if (el) return await el.contentFrame();
} catch {
/* ignore */ }
return null;
}
async function clickCFFrame(page, frame) {
await sleep(1500);
try {
const cb = await frame.$('input[type="checkbox"]').catch(() => null);
if (cb) {
const box = await cb.boundingBox().catch(() => null);
if (box) {
await cb.click();
return 'checkbox';
}
}
const label = await frame.$('label').catch(() => null);
if (label) {
const box = await label.boundingBox().catch(() => null);
if (box) {
await label.click();
return 'label';
}
}
const widget = await frame.$('.cf-turnstile-content, #cf-stage, .ctp-checkbox-label').catch(() => null);
if (widget) {
const box = await widget.boundingBox().catch(() => null);
if (box) {
await widget.click();
return 'widget';
}
}
const body = await frame.$('body').catch(() => null);
if (body) {
const box = await body.boundingBox().catch(() => null);
if (box && box.width > 0) {
await body.click();
return 'body';
}
}
} catch (e) {}
return null;
}
async function solveChallenge(page, timeoutSecs = 30) {
const deadline = Date.now() + timeoutSecs * 1000;
let attempt = 0;
let lastClicked = 0;
while (Date.now() < deadline) {
attempt++;
const cookies = await page.cookies().catch(() => []);
if (extractClearance(cookies)) {
return;
}
const platform = await detectChallenge(page);
if (!platform) {
return;
}
const frame = await findCFFrame(page);
if (!frame) {
await sleep(500);
continue;
}
if (Date.now() - lastClicked < 5000) {
await sleep(500);
continue;
}
const result = await clickCFFrame(page, frame);
if (result) {
lastClicked = Date.now();
await sleep(2500);
} else {
await sleep(500);
}
}
}
async function solveCloudflare({
url,
headless = true,
proxy = null,
proxyFile = null,
timeout = 30,
} = {}) {
const proxies = proxyFile ? loadLines(proxyFile) : [];
const chosenProxy = proxy || (proxies.length ? randomChoice(proxies) : null);
const userAgent = getChromeUA();
const parsed = chosenProxy ? parseProxy(chosenProxy) : null;
const args = [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-quic',
`--window-size=${WIDTH},${HEIGHT}`,
];
if (parsed) args.push(`--proxy-server=${parsed.serverUrl}`);
const {
browser,
page
} = await connect({
headless,
args,
turnstile: false,
connectOption: {},
disableXvfb: false,
ignoreAllFlags: false,
});
try {
await page.setViewport({
width: WIDTH,
height: HEIGHT
});
await page.setUserAgent(userAgent);
if (parsed?.username) {
await page.authenticate({
username: parsed.username,
password: parsed.password
});
}
try {
await page.goto(url, {
waitUntil: 'domcontentloaded',
timeout: timeout * 1000
});
} catch (e) {
await browser.close();
return {
url,
success: false,
error: `Navigation error: ${e.message}`
};
}
await sleep(2000);
let cookies = await page.cookies().catch(() => []);
let clearance = extractClearance(cookies);
if (!clearance) {
const platform = await detectChallenge(page);
if (!platform) {
await browser.close();
return {
url,
success: false,
error: 'No Cloudflare challenge detected'
};
}
await solveChallenge(page, timeout);
cookies = await page.cookies().catch(() => []);
clearance = extractClearance(cookies);
}
const finalUA = await page.evaluate(() => navigator.userAgent).catch(() => userAgent);
await browser.close();
if (!clearance) {
return {
url,
success: false,
error: 'Failed to obtain cf_clearance cookie'
};
}
const cookieString = buildCookieString(cookies);
const unixTimestamp = Math.floor(clearance.expires - 365 * 24 * 3600);
const timestamp = new Date(unixTimestamp * 1000).toISOString();
return {
success: true,
url,
proxy: chosenProxy,
user_agent: finalUA,
cf_clearance: clearance.value,
all_cookies: cookies,
cookie_string: cookieString,
unix_timestamp: unixTimestamp,
timestamp,
domain: clearance.domain,
};
} catch (err) {
await browser.close().catch(() => {});
throw err;
}
}
module.exports = {
solveCloudflare
};