Fix Cloudflare resolver

This commit is contained in:
ngosang 2021-10-16 19:32:52 +02:00
parent 5dd563e003
commit ca3f84f458

View File

@ -4,7 +4,7 @@ import {Page, Response} from 'puppeteer'
/** /**
* This class contains the logic to solve protections provided by CloudFlare * This class contains the logic to solve protections provided by CloudFlare
**/ **/
const CHALLENGE_SELECTORS = ['#trk_jschal_js', '.ray_id', '.attack-box', '#cf-please-wait']; const CHALLENGE_SELECTORS = ['#trk_jschal_js', '.ray_id', '.attack-box', '#cf-please-wait'];
const TOKEN_INPUT_NAMES = ['g-recaptcha-response', 'h-captcha-response']; const TOKEN_INPUT_NAMES = ['g-recaptcha-response', 'h-captcha-response'];
@ -18,79 +18,80 @@ export default async function resolveChallenge(url: string, page: Page, response
} }
log.info('Cloudflare detected'); log.info('Cloudflare detected');
if (await page.$('span[data-translate="error"]') || (await page.content()).includes('error code: 1020')) { if (await page.$('span[data-translate="error"]')) {
throw new Error('Cloudflare has blocked this request. Probably your IP is banned for this site, check in your web browser.') throw new Error('Cloudflare has blocked this request. Probably your IP is banned for this site, check in your web browser.')
} }
let selectorFoundCount = 0; let selectorFound = false;
if (response.status() > 400) { if (response.status() > 400) {
// detect cloudflare wait 5s
for (const selector of CHALLENGE_SELECTORS) {
const cfChallengeElem = await page.$(selector)
if (cfChallengeElem) {
selectorFoundCount++
log.debug(`Javascript challenge element '${selector}' detected.`)
log.debug('Waiting for Cloudflare challenge...')
while (true) { // find Cloudflare selectors
try { let selector: string = await findAnySelector(page, CHALLENGE_SELECTORS)
// catch Execution context was destroyed if (selector) {
const cfChallengeElem = await page.$(selector) selectorFound = true;
if (!cfChallengeElem) { log.debug(`Javascript challenge element '${selector}' detected.`)
// solved! log.debug('Waiting for Cloudflare challenge...')
log.debug('Challenge element not found.')
while (true) {
try {
selector = await findAnySelector(page, CHALLENGE_SELECTORS)
if (!selector) {
// solved!
log.debug('Challenge element not found.')
break
} else {
log.debug(`Javascript challenge element '${selector}' detected.`)
// new Cloudflare Challenge #cf-please-wait
const displayStyle = await page.evaluate((selector) => {
return getComputedStyle(document.querySelector(selector)).getPropertyValue("display");
}, selector);
if (displayStyle == "none") {
// spinner is hidden, could be a captcha or not
log.debug('Challenge element is hidden.')
// wait until redirecting disappears
while (true) {
try {
await page.waitFor(1000)
const displayStyle2 = await page.evaluate(() => {
return getComputedStyle(document.querySelector('#cf-spinner-redirecting')).getPropertyValue("display");
});
if (displayStyle2 == "none") {
break // hCaptcha detected
}
} catch (error) {
break // redirection completed
}
}
break break
} else { } else {
// new Cloudflare Challenge #cf-please-wait log.debug('Challenge element is visible.')
const displayStyle = await page.evaluate((selector) => {
return getComputedStyle(document.querySelector(selector)).getPropertyValue("display");
}, selector);
if (displayStyle == "none") {
// spinner is hidden, could be a captcha or not
log.debug('Challenge element is hidden.')
// wait until redirecting disappears
while (true) {
try {
await page.waitFor(1000)
const displayStyle2 = await page.evaluate(() => {
return getComputedStyle(document.querySelector('#cf-spinner-redirecting')).getPropertyValue("display");
});
if (displayStyle2 == "none") {
break // hCaptcha detected
}
} catch (error) {
break // redirection completed
}
}
break
} else {
log.debug('Challenge element is visible.')
}
}
log.debug('Found challenge element again.')
} catch (error)
{
log.debug("Unexpected error: " + error);
if (!error.toString().includes("Execution context was destroyed")) {
break
} }
} }
log.debug('Found challenge element again.')
log.debug('Waiting for Cloudflare challenge...') } catch (error)
await page.waitFor(1000) {
log.debug("Unexpected error: " + error);
if (!error.toString().includes("Execution context was destroyed")) {
break
}
} }
log.debug('Validating HTML code...') log.debug('Waiting for Cloudflare challenge...')
break await page.waitFor(1000)
} else {
log.debug(`No '${selector}' challenge element detected.`)
} }
log.debug('Validating HTML code...')
} else {
log.debug(`No challenge element detected.`)
} }
log.debug("Javascript challenge selectors found: " + selectorFoundCount + ", total selectors: " + CHALLENGE_SELECTORS.length)
} else { } else {
// some sites use cloudflare but there is no challenge // some sites use cloudflare but there is no challenge
log.debug(`Javascript challenge not detected. Status code: ${response.status()}`); log.debug(`Javascript challenge not detected. Status code: ${response.status()}`);
selectorFoundCount = 1; selectorFound = true;
} }
// it seems some captcha pages return 200 sometimes // it seems some captcha pages return 200 sometimes
@ -180,17 +181,33 @@ export default async function resolveChallenge(url: string, page: Page, response
throw new Error('Captcha detected but no automatic solver is configured.'); throw new Error('Captcha detected but no automatic solver is configured.');
} }
} else { } else {
if (selectorFoundCount == 0) if (!selectorFound)
{ {
throw new Error('No challenge selectors found, unable to proceed') throw new Error('No challenge selectors found, unable to proceed')
} else { } else {
// reload the page to make sure we get the real response // reload the page to make sure we get the real response
// do not use page.reload() to avoid #162 #143 // do not use page.reload() to avoid #162 #143
response = await page.goto(url, { waitUntil: 'domcontentloaded' }) response = await page.goto(url, { waitUntil: 'domcontentloaded' })
await page.content() await page.content()
// log.info(response.headers())
// while (response.headers() == null) {
// await page.waitFor(1000)
// }
log.info('Challenge solved.'); log.info('Challenge solved.');
} }
} }
return response; return response;
} }
async function findAnySelector(page: Page, selectors: string[]) {
for (const selector of selectors) {
const cfChallengeElem = await page.$(selector)
if (cfChallengeElem) {
return selector;
}
}
return null;
}