diff --git a/lib/esm/module/turnstile.mjs b/lib/esm/module/turnstile.mjs index 4d6e23f..0273dfc 100644 --- a/lib/esm/module/turnstile.mjs +++ b/lib/esm/module/turnstile.mjs @@ -1,62 +1,225 @@ -export const checkTurnstile = ({ page }) => { + +const CLOUDFLARE_CHALLENGE_URL = 'challenges.cloudflare.com/cdn-cgi/challenge-platform'; +const MONITORING_INTERVAL = 2000; +const CLICK_X = 30; +const CLICK_Y = 30; +const DEFAULT_TIMEOUT = 30000; + +export const checkTurnstile = ({ page, timeout = DEFAULT_TIMEOUT } = {}) => { return new Promise(async (resolve, reject) => { - var waitInterval = setTimeout(() => { clearInterval(waitInterval); resolve(false) }, 5000); + if (!page) { + return reject(new Error('Page object is required')); + } - try { - const elements = await page.$$('[name="cf-turnstile-response"]'); - if (elements.length <= 0) { - - const coordinates = await page.evaluate(() => { - let coordinates = []; - document.querySelectorAll('div').forEach(item => { - try { - let itemCoordinates = item.getBoundingClientRect() - let itemCss = window.getComputedStyle(item) - if (itemCss.margin == "0px" && itemCss.padding == "0px" && itemCoordinates.width > 290 && itemCoordinates.width <= 310 && !item.querySelector('*')) { - coordinates.push({ x: itemCoordinates.x, y: item.getBoundingClientRect().y, w: item.getBoundingClientRect().width, h: item.getBoundingClientRect().height }) - } - } catch (err) { } - }); + const browser = page.browser(); + let monitoringActive = true; + let activeTargets = new Set(); + let targetFrameFound = false; + const startTime = Date.now(); - if (coordinates.length <= 0) { - document.querySelectorAll('div').forEach(item => { - try { - let itemCoordinates = item.getBoundingClientRect() - if (itemCoordinates.width > 290 && itemCoordinates.width <= 310 && !item.querySelector('*')) { - coordinates.push({ x: itemCoordinates.x, y: item.getBoundingClientRect().y, w: item.getBoundingClientRect().width, h: item.getBoundingClientRect().height }) - } - } catch (err) { } + console.log("Starting Turnstile automation..."); + + const sleep = (ms) => new Promise(r => setTimeout(r, ms)); + + const setupMobileEmulation = async (targetId, cdpSession) => { + try { + const result = await cdpSession.send('Runtime.evaluate', { + expression: ` + (() => { + const mobileUserAgent = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); + const hasTouchSupport = ('ontouchstart' in window); + + return { + isMobile: mobileUserAgent, + userAgent: navigator.userAgent, + innerWidth: window.innerWidth, + touchSupport: hasTouchSupport, + maxTouchPoints: navigator.maxTouchPoints + }; + })() + `, + returnByValue: true + }); + + if (result?.result?.value) { + const deviceInfo = result.result.value; + console.log("Device detection:", deviceInfo); + + if (deviceInfo.isMobile) { + console.log("Mobile device detected, enabling touch events"); + await cdpSession.send('Emulation.setEmitTouchEventsForMouse', { + enabled: true, + configuration: 'mobile' }); + console.log("Mobile touch events enabled for target:", targetId); + } else { + console.log("Desktop device detected, using standard mouse events"); + } + } + } catch (error) { + console.log("Could not detect device type, defaulting to desktop mode:", error.message); + } + }; + + const performMouseClick = async (targetId, cdpSession, x, y) => { + console.log("Performing mouse click at coordinates:", x, y); + + try { + await cdpSession.send('Input.dispatchMouseEvent', { + type: 'touchStart',///type: "mousePressed", [[Chrome is very forgiving internally. Even if you send a touch-like type to dispatchMouseEvent, it might still trigger a DOM event (pointerdown / pointerup]] + x: x, + y: y, + button: 'left', + clickCount: 1 + }); + + console.log("Mouse pressed at:", x, y); + // await sleep(100);///LMAO DOESNT NOT NEED THIS + + await cdpSession.send('Input.dispatchMouseEvent', { + type: 'touchEnd',/// type: "mouseReleased", + x: x, + y: y, + button: 'left', + clickCount: 1 + }); + console.log("Mouse released. Click completed successfully!"); + activeTargets.delete(targetId); + targetFrameFound = true; + } catch (error) { + console.error("Mouse event failed:", error.message); + activeTargets.delete(targetId); + throw error; + } + }; + + const attachToTarget = async (target) => { + const targetId = target._targetId; + const url = target.url(); + + if (activeTargets.has(targetId)) { + return; + } + + const isCloudflareChallenge = url.includes(CLOUDFLARE_CHALLENGE_URL); + if (!isCloudflareChallenge) { + return; + } + + console.log("Found Cloudflare challenge frame:", url); + activeTargets.add(targetId); + + try { + let cdpSession; + + if (target.type() === 'page') { + const targetPage = await target.page(); + if (targetPage) { + cdpSession = await targetPage._client(); + console.log("Got CDP session from page"); + } + } else { + const browserClient = await page._client(); + const result = await browserClient.send('Target.attachToTarget', { + targetId: targetId, + flatten: true + }); + console.log("Created new CDP session:", result.sessionId); + + let connection = browserClient.connection?.() || browserClient.connection || browserClient._connection; + + cdpSession = connection?.sessions?.get(result.sessionId) || + connection?._sessions?.get(result.sessionId) || + connection?._sessionMap?.get(result.sessionId); + + if (!cdpSession) { + console.log("Using fallback session wrapper"); + cdpSession = { + _sessionId: result.sessionId, + _browserClient: browserClient, + send: async function(method, params = {}) { + console.log(`Sending command: ${method}`, params); + try { + return await this._browserClient.send(method, params); + } catch (error) { + console.error(`Error sending ${method}:`, error.message); + throw error; + } + } + }; } + } + + console.log("Successfully attached to target:", targetId); + + await cdpSession.send('Runtime.enable'); + console.log("Runtime enabled for target:", targetId); - return coordinates - }) + await setupMobileEmulation(targetId, cdpSession); + await sleep(500); + await performMouseClick(targetId, cdpSession, CLICK_X, CLICK_Y); + + } catch (error) { + console.error("Failed to attach to target:", targetId, error.message); + activeTargets.delete(targetId); + } + }; - for (const item of coordinates) { + const checkForToken = async () => { + try { + const token = await page.evaluate(() => { try { - let x = item.x + 30; - let y = item.y + item.h / 2; - await page.mouse.click(x, y); - } catch (err) { } - } - return resolve(true) + let item = document.querySelector('[name="cf-turnstile-response"]')?.value; + return item && item.length > 20 ? item : null; + } catch (e) { + return null; + } + }); + return token; + } catch (e) { + return null; } + }; + + const monitor = async () => { + while (monitoringActive) { + if (Date.now() - startTime > timeout) { + monitoringActive = false; + return reject(new Error('Turnstile challenge timeout')); + } - for (const element of elements) { try { - const parentElement = await element.evaluateHandle(el => el.parentElement); - const box = await parentElement.boundingBox(); - let x = box.x + 30; - let y = box.y + box.height / 2; - await page.mouse.click(x, y); - } catch (err) { } + const targets = await browser.targets(); + + for (const target of targets) { + const type = target.type(); + const url = target.url(); + + if ((type === 'page' || type === 'iframe' || type === 'other') && + url && url.includes('challenges.cloudflare.com')) { + await attachToTarget(target); + } + } + + const token = await checkForToken(); + if (token) { + console.log('Cloudflare Turnstile Token obtained:', token); + monitoringActive = false; + return resolve(token); + } + } catch (error) { + console.error("Error during monitoring:", error.message); + } + + await sleep(MONITORING_INTERVAL); } - clearInterval(waitInterval) - resolve(true) - } catch (err) { - clearInterval(waitInterval) - resolve(false) + }; + + try { + await monitor(); + } catch (error) { + reject(error); } - }) -} \ No newline at end of file + }); +};