import fs from 'fs-extra'; import { ElementHandle, Protocol } from 'puppeteer'; import path from 'path'; import { getDevtoolsUrl, safeLaunchBrowser, safeNewPage } from '../common/puppeteer'; import { config, CONFIG_DIR } from '../common/config'; import L from '../common/logger'; const HCAPTCHA_ACCESSIBILITY_CACHE_FILE = path.join( CONFIG_DIR, 'hcaptcha-accessibility-cache.json' ); const CACHE_BUFFER_MS = 5 * 60 * 1000; // 5 minutes const getCookieCache = async (): Promise<Protocol.Network.Cookie[] | null> => { try { await fs.access(HCAPTCHA_ACCESSIBILITY_CACHE_FILE, fs.constants.O_RDWR); const cookieData: Protocol.Network.Cookie[] = await fs.readJSON( HCAPTCHA_ACCESSIBILITY_CACHE_FILE ); const cookieExpiryString = cookieData.find((c) => c.name === 'hc_accessibility')?.expires; if (!cookieExpiryString) return null; if (new Date(cookieExpiryString * 1000).getTime() < Date.now() + CACHE_BUFFER_MS) return null; return cookieData; } catch (err) { return null; } }; const setCookieCache = async (cookies: Protocol.Network.Cookie[]): Promise<void> => { await fs.writeJSON(HCAPTCHA_ACCESSIBILITY_CACHE_FILE, cookies); }; export const getHcaptchaCookies = async (): Promise<Protocol.Network.Cookie[]> => { const { hcaptchaAccessibilityUrl } = config; if (!hcaptchaAccessibilityUrl) { L.debug( 'hcaptchaAccessibilityUrl not configured, captchas are less likely to be bypassed. Follow this guide to set it up: https://github.com/claabs/epicgames-freegames-node#hcaptcha-accessibility-cookies' ); return []; } let cookieData = await getCookieCache(); let browser; if (!cookieData) { try { L.debug('Setting hCaptcha accessibility cookies'); browser = await safeLaunchBrowser(L); const page = await safeNewPage(browser, L); L.trace(getDevtoolsUrl(page)); L.trace(`Navigating to ${hcaptchaAccessibilityUrl}`); await Promise.all([ page.goto(hcaptchaAccessibilityUrl), page.waitForNavigation({ waitUntil: 'networkidle0' }), ]); L.trace(`Waiting for setAccessibilityCookie button`); const setCookieButton = (await page.waitForSelector( `button[data-cy='setAccessibilityCookie']:not([disabled])` )) as ElementHandle<HTMLButtonElement>; L.trace(`Clicking setAccessibilityCookie button`); await setCookieButton.click({ delay: 100 }); try { const getCookieResp = await page.waitForResponse( (res) => res.url() === 'https://accounts.hcaptcha.com/accessibility/get_cookie' && res.request().method() === 'POST' ); const getCookieStatus = getCookieResp.status(); if (getCookieStatus !== 200) { const errorBody = await getCookieResp.json(); L.debug( { status: getCookieStatus, errorBody }, 'Error from hCaptcha get_cookie request, continuing without hCaptcha accessibility cookies' ); } } catch (err) { L.debug(err); L.warn( 'No get cookie response recieved, continuing without hCaptcha accessibility cookies' ); } L.trace(`Saving new cookies`); const cdpClient = await page.target().createCDPSession(); const currentUrlCookies = (await cdpClient.send('Network.getAllCookies')) as { cookies: Protocol.Network.Cookie[]; }; await browser.close(); cookieData = currentUrlCookies.cookies; await setCookieCache(cookieData); } catch (err) { L.warn(err); L.warn( 'Setting the hCaptcha accessibility cookies encountered an error. Continuing without them...' ); if (browser) await browser.close(); return []; } } return cookieData; };