| '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 { |
| } |
|
|
| try { |
| const el = await page.$('iframe[src*="challenges.cloudflare.com"]'); |
| if (el) return await el.contentFrame(); |
| } catch { |
| } |
|
|
| 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 |
| }; |
|
|