Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
259 changes: 211 additions & 48 deletions lib/esm/module/turnstile.mjs
Original file line number Diff line number Diff line change
@@ -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);
}
})
}
});
};