From 7a9f31df7c28296d85e3a958375b82c39061d39b Mon Sep 17 00:00:00 2001 From: nocci Date: Wed, 31 Dec 2025 12:25:07 +0000 Subject: [PATCH] feat: add optional new epic claimer mode --- README.md | 42 +++++--- epic-claimer-new.js | 206 ++++++++++++++++++++++++++++++++++++ epic-games.js | 6 ++ package-lock.json | 252 ++++++++++++++++++++++++++++++++++++++------ package.json | 1 + src/config.js | 1 + 6 files changed, 464 insertions(+), 44 deletions(-) create mode 100644 epic-claimer-new.js diff --git a/README.md b/README.md index a8fae1f..ecb406b 100644 --- a/README.md +++ b/README.md @@ -19,35 +19,45 @@ Quickstart (Docker Run) ``` docker run --rm -it \ -p 6080:6080 \ - -v fgc:/fgc/data \ + -v fgc-data:/fgc/data \ + -v fgc-browser:/home/fgc/.cache/browser \ + -v fgc-playwright:/home/fgc/.cache/ms-playwright \ -e SHOW=1 \ - git.sky-net.it/nocci/free-games-claimer:latest \ - node prime-gaming.js + git.sky-net.it/nocci/free-games-claimer:dev \ + bash -c "node prime-gaming; node gog; ./keep-alive.sh" ``` - Ports 6080/5900: noVNC/VNC (only needed with `SHOW=1`) -- Data/configs are stored in volume `fgc` under `/fgc/data` +- Volumes persist profile + Playwright-Browser, damit Logins/Downloads bleiben. -Docker Compose Example ----------------------- +Docker Compose Example (persistent volumes) +------------------------------------------- ```yaml services: - fgc: - image: git.sky-net.it/nocci/free-games-claimer:latest + free-games-claimer: + image: git.sky-net.it/nocci/free-games-claimer:dev container_name: fgc environment: - - SHOW=1 # show browser via VNC/noVNC + - SHOW=1 # show browser via VNC/noVNC # - PG_EMAIL=... # - PG_PASSWORD=... # - PG_OTPKEY=... + - BROWSER_DIR=/fgc/data/browser + - LOGIN_VISIBLE_TIMEOUT=20 # optional: faster login detection + - KEEP_ALIVE_SECONDS=86400 # optional: keep container alive after runs volumes: - - fgc:/fgc/data + - fgc-data:/fgc/data + - fgc-browser:/home/fgc/.cache/browser + - fgc-playwright:/home/fgc/.cache/ms-playwright ports: - - "6080:6080" # noVNC - # - "5900:5900" # VNC optional - command: bash -c "node epic-games; node prime-gaming; node gog" + - "6080:6080" # noVNC + # - "5900:5900" # VNC optional + command: bash -c "node prime-gaming; node gog; ./keep-alive.sh" volumes: - fgc: + fgc-data: + fgc-browser: + fgc-playwright: ``` +Hinweis: Das Image läuft auf `dev`; bei Bedarf `:latest` wählen. Configuration (Environment Variables) ------------------------------------- @@ -55,6 +65,7 @@ Common options: - `SHOW=0/1` (0 = headless, 1 = UI) - `WIDTH`, `HEIGHT` (browser size) - `TIMEOUT`, `LOGIN_TIMEOUT` (seconds) +- Epic: `EG_MODE=legacy|new` (legacy Playwright flow or neuer API-getriebener Claimer), `EG_PARENTALPIN`, `EG_EMAIL`, `EG_PASSWORD`, `EG_OTPKEY` - Login: `EMAIL`, `PASSWORD` global; per store `EG_EMAIL`, `EG_PASSWORD`, `EG_OTPKEY`, `PG_EMAIL`, `PG_PASSWORD`, `PG_OTPKEY`, `GOG_EMAIL`, `GOG_PASSWORD` - Prime Gaming: `PG_REDEEM=1` (auto-redeem keys, experimental), `PG_CLAIMDLC=1`, `PG_TIMELEFT=` to skip long-remaining offers - Screenshots: `SCREENSHOTS_DIR` (default `data/screenshots`) @@ -66,6 +77,9 @@ Common options: - Directories: `SCREENSHOTS_DIR`, `BROWSER_DIR`, `DATA_DIR` (prefix for data; default under `data/`) - VNC/noVNC: `VNC_PASSWORD` (for Docker entrypoint), `NOVNC_PORT`/`VNC_PORT` (Docker) - General timeouts: `TIMEOUT` (per action), `LOGIN_TIMEOUT` (extra time for login) +- Login detection: `LOGIN_VISIBLE_TIMEOUT` (ms) to abort sooner when login buttons not present +- Keep-alive: `KEEP_ALIVE_SECONDS` (default 86400) for `keep-alive.sh` +- Repo banner: `REPO_URL` for log output You can place a `data/config.env`; it is loaded via dotenv and is overridden by explicitly set environment variables. diff --git a/epic-claimer-new.js b/epic-claimer-new.js new file mode 100644 index 0000000..91da99f --- /dev/null +++ b/epic-claimer-new.js @@ -0,0 +1,206 @@ +import axios from 'axios'; +import { firefox } from 'playwright-firefox'; +import { authenticator } from 'otplib'; +import chalk from 'chalk'; +import { resolve, jsonDb, datetime, stealth, filenamify, prompt, notify, html_game_list, handleSIGINT } from './src/util.js'; +import { cfg } from './src/config.js'; + +const URL_CLAIM = 'https://store.epicgames.com/en-US/free-games'; +const URL_LOGIN = 'https://www.epicgames.com/id/login?lang=en-US&noHostRedirect=true&redirectUrl=' + URL_CLAIM; + +const screenshot = (...a) => resolve(cfg.dir.screenshots, 'epic-games', ...a); + +const fetchFreeGamesAPI = async () => { + const resp = await axios.get('https://store-site-backend-static-ipv4.ak.epicgames.com/freeGamesPromotions', { + params: { locale: 'en-US', country: 'US', allowCountries: 'US,DE,AT,CH,GB' }, + }); + return resp.data?.Catalog?.searchStore?.elements + ?.filter(g => g.promotions?.promotionalOffers?.[0]) + ?.map(g => { + const offer = g.promotions.promotionalOffers[0].promotionalOffers[0]; + const mapping = g.catalogNs?.mappings?.[0]; + return { + title: g.title, + namespace: mapping?.pageSlug ? mapping.id : g.catalogNs?.mappings?.[0]?.id, + pageSlug: mapping?.pageSlug || g.urlSlug, + offerId: offer?.offerId, + }; + }) || []; +}; + +const ensureLoggedIn = async (page, context) => { + while (await page.locator('egs-navigation').getAttribute('isloggedin') != 'true') { + console.error('Not signed in anymore. Please login in the browser or here in the terminal.'); + if (cfg.novnc_port) console.info(`Open http://localhost:${cfg.novnc_port} to login inside the docker container.`); + if (!cfg.debug) context.setDefaultTimeout(cfg.login_timeout); + console.info(`Login timeout is ${cfg.login_timeout / 1000} seconds!`); + await page.goto(URL_LOGIN, { waitUntil: 'domcontentloaded' }); + if (cfg.eg_email && cfg.eg_password) console.info('Using email and password from environment.'); + else console.info('Press ESC to skip the prompts if you want to login in the browser (not possible in headless mode).'); + + const notifyBrowserLogin = async () => { + console.log('Waiting for you to login in the browser.'); + await notify('epic-games: no longer signed in and not enough options set for automatic login.'); + if (cfg.headless) { + console.log('Run `SHOW=1 node epic-games` to login in the opened browser.'); + await context.close(); + process.exit(1); + } + }; + + const email = cfg.eg_email || await prompt({ message: 'Enter email' }); + if (!email) { + await notifyBrowserLogin(); + await page.waitForURL(URL_CLAIM); + if (!cfg.debug) context.setDefaultTimeout(cfg.timeout); + continue; + } + + await page.fill('#email', email); + const password = cfg.eg_password || await prompt({ type: 'password', message: 'Enter password' }); + if (password) { + await page.fill('#password', password); + await page.click('button[type="submit"]'); + } else { + await notifyBrowserLogin(); + await page.waitForURL(URL_CLAIM); + if (!cfg.debug) context.setDefaultTimeout(cfg.timeout); + continue; + } + + const watchMfaStep = async () => { + try { + await page.waitForURL('**/id/login/mfa**', { timeout: cfg.login_timeout }); + console.log('Enter the security code to continue - security code sent to your email/device.'); + const otp = cfg.eg_otpkey && authenticator.generate(cfg.eg_otpkey) || await prompt({ type: 'text', message: 'Enter two-factor sign in code', validate: n => n.toString().length == 6 || 'The code must be 6 digits!' }); + await page.locator('input[name="code-input-0"]').pressSequentially(otp.toString()); + await page.click('button[type="submit"]'); + } catch { + return; + } + }; + watchMfaStep(); + + await page.waitForURL(URL_CLAIM); + if (!cfg.debug) context.setDefaultTimeout(cfg.timeout); + } + const user = await page.locator('egs-navigation').getAttribute('displayname'); + console.log(`Signed in as ${user}`); + return user; +}; + +export const claimEpicGamesNew = async () => { + console.log('Starting Epic Games claimer (new mode)'); + const db = await jsonDb('epic-games.json', {}); + + const freeGames = await fetchFreeGamesAPI(); + console.log('Free games via API:', freeGames.map(g => g.pageSlug)); + + const context = await firefox.launchPersistentContext(cfg.dir.browser, { + headless: cfg.headless, + viewport: { width: cfg.width, height: cfg.height }, + userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0', + locale: 'en-US', + recordVideo: cfg.record ? { dir: 'data/record/', size: { width: cfg.width, height: cfg.height } } : undefined, + recordHar: cfg.record ? { path: `data/record/eg-${filenamify(datetime())}.har` } : undefined, + handleSIGINT: false, + args: [], + }); + handleSIGINT(context); + await stealth(context); + if (!cfg.debug) context.setDefaultTimeout(cfg.timeout); + + const page = context.pages().length ? context.pages()[0] : await context.newPage(); + await page.setViewportSize({ width: cfg.width, height: cfg.height }); + + const notify_games = []; + let user; + + try { + await context.addCookies([ + { name: 'OptanonAlertBoxClosed', value: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString(), domain: '.epicgames.com', path: '/' }, + { name: 'HasAcceptedAgeGates', value: 'USK:9007199254740991,general:18,EPIC SUGGESTED RATING:18', domain: 'store.epicgames.com', path: '/' }, + ]); + + await page.goto(URL_CLAIM, { waitUntil: 'domcontentloaded' }); + user = await ensureLoggedIn(page, context); + db.data[user] ||= {}; + + for (const game of freeGames) { + const purchaseUrl = `https://store.epicgames.com/purchase?namespace=${game.namespace}&offers=${game.offerId}`; + console.log('Processing', chalk.blue(game.title), purchaseUrl); + const notify_game = { title: game.title, url: purchaseUrl, status: 'failed' }; + notify_games.push(notify_game); + + await page.goto(purchaseUrl, { waitUntil: 'domcontentloaded' }); + + const purchaseBtn = page.locator('button[data-testid="purchase-cta-button"]').first(); + await purchaseBtn.waitFor({ timeout: cfg.timeout }); + const btnText = (await purchaseBtn.innerText()).toLowerCase(); + + if (btnText.includes('library')) { + console.log(' Already in library.'); + notify_game.status = 'existed'; + db.data[user][game.offerId] = { title: game.title, time: datetime(), url: purchaseUrl, status: 'existed' }; + continue; + } + if (cfg.dryrun) { + console.log(' DRYRUN=1 -> Skip order!'); + notify_game.status = 'skipped'; + db.data[user][game.offerId] = { title: game.title, time: datetime(), url: purchaseUrl, status: 'skipped' }; + continue; + } + + await purchaseBtn.click({ delay: 10 }); + await page.waitForSelector('#webPurchaseContainer iframe'); + const iframe = page.frameLocator('#webPurchaseContainer iframe'); + + if (cfg.eg_parentalpin) { + try { + await iframe.locator('.payment-pin-code').waitFor({ timeout: 10000 }); + await iframe.locator('input.payment-pin-code__input').first().pressSequentially(cfg.eg_parentalpin); + await iframe.locator('button:has-text("Continue")').click({ delay: 11 }); + } catch { + // no PIN needed + } + } + + try { + await iframe.locator('button:has-text("Place Order"):not(:has(.payment-loading--loading))').click({ delay: 11 }); + const btnAgree = iframe.locator('button:has-text("I Accept")'); + try { + await btnAgree.waitFor({ timeout: 10000 }); + await btnAgree.click(); + } catch { + // not required + } + await page.locator('text=Thanks for your order!').waitFor({ state: 'attached', timeout: cfg.timeout }); + notify_game.status = 'claimed'; + db.data[user][game.offerId] = { title: game.title, time: datetime(), url: purchaseUrl, status: 'claimed' }; + console.log(' Claimed successfully!'); + } catch (e) { + console.error(' Failed to claim:', e.message); + notify_game.status = 'failed'; + db.data[user][game.offerId] = { title: game.title, time: datetime(), url: purchaseUrl, status: 'failed' }; + const p = screenshot('failed', `${game.offerId}_${filenamify(datetime())}.png`); + await page.screenshot({ path: p, fullPage: true }).catch(() => {}); + } + } + } catch (error) { + process.exitCode ||= 1; + console.error('--- Exception:'); + console.error(error); + if (error.message && process.exitCode != 130) notify(`epic-games (new) failed: ${error.message.split('\n')[0]}`); + } finally { + await db.write(); + if (notify_games.filter(g => g.status == 'claimed' || g.status == 'failed').length) { + notify(`epic-games (new ${user}):
${html_game_list(notify_games)}`); + } + } + if (cfg.debug && context) { + console.log(JSON.stringify(await context.cookies(), null, 2)); + } + await context.close(); +}; + +export default claimEpicGamesNew; diff --git a/epic-games.js b/epic-games.js index 941383f..ce14eab 100644 --- a/epic-games.js +++ b/epic-games.js @@ -13,6 +13,12 @@ const URL_LOGIN = 'https://www.epicgames.com/id/login?lang=en-US&noHostRedirect= console.log(datetime(), 'started checking epic-games'); +if (cfg.eg_mode === 'new') { + const { claimEpicGamesNew } = await import('./epic-claimer-new.js'); + await claimEpicGamesNew(); + process.exit(0); +} + const db = await jsonDb('epic-games.json', {}); if (cfg.time) console.time('startup'); diff --git a/package-lock.json b/package-lock.json index 9ab1fff..1f14566 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.4.0", "license": "AGPL-3.0-only", "dependencies": { + "axios": "^1.7.9", "chalk": "^5.4.1", "cross-env": "^7.0.3", "dotenv": "^16.5.0", @@ -446,6 +447,23 @@ "node": ">=0.10.0" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -527,7 +545,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -628,6 +645,18 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -752,6 +781,15 @@ "node": ">=0.10.0" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -793,7 +831,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -843,7 +880,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -853,7 +889,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -863,7 +898,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -872,6 +906,21 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -1325,6 +1374,26 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -1346,6 +1415,43 @@ "node": ">=0.10.0" } }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -1390,7 +1496,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1410,7 +1515,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -1435,7 +1539,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -1495,7 +1598,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -1523,7 +1625,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -1532,11 +1633,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -1867,7 +1982,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2241,6 +2355,12 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -3180,6 +3300,21 @@ "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==" }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "axios": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "requires": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -3232,7 +3367,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "requires": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -3290,6 +3424,14 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3369,6 +3511,11 @@ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -3392,7 +3539,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "requires": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -3428,24 +3574,32 @@ "es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" }, "es-errors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" }, "es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "requires": { "es-errors": "^1.3.0" } }, + "es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "requires": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + } + }, "escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -3746,6 +3900,11 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, + "follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==" + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -3759,6 +3918,33 @@ "for-in": "^1.0.1" } }, + "form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "dependencies": { + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + } + } + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -3789,8 +3975,7 @@ "function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" }, "generative-bayesian-network": { "version": "2.1.66", @@ -3805,7 +3990,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "requires": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -3823,7 +4007,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "requires": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -3860,8 +4043,7 @@ "gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" }, "graceful-fs": { "version": "4.2.11", @@ -3877,14 +4059,20 @@ "has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" + }, + "has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "requires": { + "has-symbols": "^1.0.3" + } }, "hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "requires": { "function-bind": "^1.1.2" } @@ -4117,8 +4305,7 @@ "math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" }, "media-typer": { "version": "1.1.0", @@ -4363,6 +4550,11 @@ "ipaddr.js": "1.9.1" } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/package.json b/package.json index 0487277..cf2b41b 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "dotenv": "^16.5.0", "enquirer": "^2.4.1", "fingerprint-injector": "^2.1.66", + "axios": "^1.7.9", "lowdb": "^7.0.1", "otplib": "^12.0.1", "playwright-firefox": "^1.52.0", diff --git a/src/config.js b/src/config.js index 0df7511..7702984 100644 --- a/src/config.js +++ b/src/config.js @@ -15,6 +15,7 @@ export const cfg = { get headless() { return !this.debug && !this.show; }, + eg_mode: process.env.EG_MODE || 'legacy', // epic-games: legacy playwright flow or 'new' API-driven flow width: Number(process.env.WIDTH) || 1920, // width of the opened browser height: Number(process.env.HEIGHT) || 1080, // height of the opened browser timeout: (Number(process.env.TIMEOUT) || 60) * 1000, // default timeout for playwright is 30s