diff --git a/epic-games.js b/epic-games.js index c27f310..6de53be 100644 --- a/epic-games.js +++ b/epic-games.js @@ -3,6 +3,11 @@ import path from 'path'; import { dirs, jsonDb, datetime, stealth, filenamify } from './util.js'; import { existsSync, writeFileSync } from 'fs'; +import prompts from 'prompts'; // alternatives: enquirer, inquirer +// import enquirer from 'enquirer'; const { prompt } = enquirer; +// single prompt that just returns the non-empty value instead of an object - why name things if there's just one? +const prompt = async o => (await prompts({name: 'name', type: 'text', message: 'Enter value', validate: s => s.length, ...o})).name; + const debug = process.env.PWDEBUG == '1'; // runs non-headless and opens https://playwright.dev/docs/inspector const URL_CLAIM = 'https://store.epicgames.com/en-US/free-games'; @@ -61,14 +66,28 @@ try { // Accept cookies to get rid of banner to save space on screen. Clicking this did not always work since the message was animated in too slowly. // page.click('button:has-text("Accept All Cookies")').catch(_ => { }); // not needed anymore since we set the cookie above - while (await page.locator('a[role="button"]:has-text("Sign In")').count() > 0) { // TODO also check alternative for signed-in state + while (await page.locator('a[role="button"]:has-text("Sign In")').count() > 0) { console.error("Not signed in anymore. Please login and then navigate to the 'Free Games' page. If using docker, open http://localhost:6080"); context.setDefaultTimeout(0); // give user time to log in without timeout await page.goto(URL_LOGIN, { waitUntil: 'domcontentloaded' }); - // after login it just reloads the login page... + + const email = process.env.EMAIL || await prompt({message: 'Enter email'}); + const password = process.env.PASSWORD || await prompt({type: 'password', message: 'Enter password'}); + if (email && password) { + await page.click('text=Sign in with Epic Games'); + await page.fill('#email', email); + await page.fill('#password', password); + await page.click('button[type="submit"]'); + // TODO alternatively wait for redirectUrl + await page.waitForNavigation({ url: '**/id/login/mfa**'}).then(async () => { + console.log('Enter the security code to continue - This appears to be a new device, browser or location. A security code has been sent to your email address at ...'); + const otp = await prompt({type: 'number', message: 'Enter two-factor sign in code', validate: n => n.toString().length == 6 || 'The code must be 6 digits!'}); + await page.type('input[name="code-input-0"]', otp); + await page.click('button[type="submit"]'); + }); + } await page.waitForNavigation({ url: URL_CLAIM }); context.setDefaultTimeout(TIMEOUT); - // process.exit(1); } const user = await page.locator('#user span').first().innerHTML(); console.log(`Signed in as ${user}`); diff --git a/package-lock.json b/package-lock.json index b9647eb..93508d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "cross-env": "^7.0.3", "lowdb": "^4.0.0", "playwright": "^1.27.1", + "prompts": "^2.4.2", "puppeteer-extra-plugin-stealth": "^2.11.1" } }, @@ -280,6 +281,15 @@ "node": ">=0.10.0" } }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/lazy-cache": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", @@ -413,6 +423,19 @@ "node": ">=14" } }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/puppeteer-extra-plugin": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin/-/puppeteer-extra-plugin-3.2.2.tgz", @@ -591,6 +614,12 @@ "node": ">=8" } }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, "node_modules/steno": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/steno/-/steno-3.0.0.tgz", @@ -846,6 +875,12 @@ "is-buffer": "^1.1.5" } }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, "lazy-cache": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", @@ -941,6 +976,16 @@ "integrity": "sha512-9EmeXDncC2Pmp/z+teoVYlvmPWUC6ejSSYZUln7YaP89Z6lpAaiaAnqroUt/BoLo8tn7WYShcfaCh+xofZa44Q==", "dev": true }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, "puppeteer-extra-plugin": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin/-/puppeteer-extra-plugin-3.2.2.tgz", @@ -1040,6 +1085,12 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, "steno": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/steno/-/steno-3.0.0.tgz", diff --git a/package.json b/package.json index 29965e2..4abd28a 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "cross-env": "^7.0.3", "lowdb": "^4.0.0", "playwright": "^1.27.1", + "prompts": "^2.4.2", "puppeteer-extra-plugin-stealth": "^2.11.1" }, "type": "module"