Skip to content

Commit

Permalink
run eslint --fix .
Browse files Browse the repository at this point in the history
  • Loading branch information
vogler committed Nov 8, 2023
1 parent 011eddf commit 0832ae5
Show file tree
Hide file tree
Showing 10 changed files with 273 additions and 277 deletions.
6 changes: 4 additions & 2 deletions config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ export const cfg = {
dryrun: process.env.DRYRUN == '1', // don't claim anything
interactive: process.env.INTERACTIVE == '1', // confirm to claim, default skip
show: process.env.SHOW == '1', // run non-headless
get headless() { return !this.debug && !this.show },
get headless() {
return !this.debug && !this.show;
},
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
Expand All @@ -23,7 +25,7 @@ export const cfg = {
return {
browser: process.env.BROWSER_DIR || dataDir('browser'), // for multiple accounts or testing
screenshots: process.env.SCREENSHOTS_DIR || dataDir('screenshots'), // set to 0 to disable screenshots
}
};
},
// auth epic-games
eg_email: process.env.EG_EMAIL || process.env.EMAIL,
Expand Down
19 changes: 9 additions & 10 deletions epic-games.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const context = await firefox.launchPersistentContext(cfg.dir.browser, {
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.83 Safari/537.36', // see replace of Headless in util.newStealthContext. TODO Windows UA enough to avoid 'device not supported'? update if browser is updated?
// userAgent firefox (macOS): Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:106.0) Gecko/20100101 Firefox/106.0
// userAgent firefox (docker): Mozilla/5.0 (X11; Linux aarch64; rv:109.0) Gecko/20100101 Firefox/115.0
locale: "en-US", // ignore OS locale to be sure to have english text for locators
locale: 'en-US', // ignore OS locale to be sure to have english text for locators
recordVideo: cfg.record ? { dir: 'data/record/', size: { width: cfg.width, height: cfg.height } } : undefined, // will record a .webm video for each page navigated; without size, video would be scaled down to fit 800x800
recordHar: cfg.record ? { path: `data/record/eg-${datetime()}.har` } : undefined, // will record a HAR file with network requests and responses; can be imported in Chrome devtools
handleSIGINT: false, // have to handle ourselves and call context.close(), otherwise recordings from above won't be saved
Expand Down Expand Up @@ -64,7 +64,7 @@ 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: '/'}]); // Accept cookies to get rid of banner to save space on screen. Set accept time to 5 days ago.
await context.addCookies([{ name: 'OptanonAlertBoxClosed', value: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString(), domain: '.epicgames.com', path: '/' }]); // Accept cookies to get rid of banner to save space on screen. Set accept time to 5 days ago.

await page.goto(URL_CLAIM, { waitUntil: 'domcontentloaded' }); // 'domcontentloaded' faster than default 'load' https://playwright.dev/docs/api/class-page#page-goto

Expand All @@ -77,12 +77,12 @@ try {
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); // give user some extra time to log in
console.info(`Login timeout is ${cfg.login_timeout/1000} seconds!`);
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 email = cfg.eg_email || await prompt({message: 'Enter email'});
const password = email && (cfg.eg_password || await prompt({type: 'password', message: 'Enter password'}));
const email = cfg.eg_email || await prompt({ message: 'Enter email' });
const password = email && (cfg.eg_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);
Expand All @@ -100,7 +100,7 @@ try {
page.waitForURL('**/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 ...');
// TODO locator for text (email or app?)
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!'}); // can't use type: 'number' since it strips away leading zeros and codes sometimes have them
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!' }); // can't use type: 'number' since it strips away leading zeros and codes sometimes have them
await page.locator('input[name="code-input-0"]').pressSequentially(otp.toString());
await page.click('button[type="submit"]');
}).catch(_ => { });
Expand Down Expand Up @@ -134,7 +134,7 @@ try {
// clicking on `game_sel` sometimes led to a 404, see https://github.com/vogler/free-games-claimer/issues/25
// debug showed that in those cases the href was still correct, so we `goto` the urls instead of clicking.
// Alternative: parse the json loaded to build the page https://store-site-backend-static-ipv4.ak.epicgames.com/freeGamesPromotions
// filter data.Catalog.searchStore.elements for .promotions.promotionalOffers being set and build URL with .catalogNs.mappings[0].pageSlug or .urlSlug if not set to some wrong id like it was the case for spirit-of-the-north-f58a66 - this is also what's done here: https://github.com/claabs/epicgames-freegames-node/blob/938a9653ffd08b8284ea32cf01ac8727d25c5d4c/src/puppet/free-games.ts#L138-L213
// i.e. filter data.Catalog.searchStore.elements for .promotions.promotionalOffers being set and build URL with .catalogNs.mappings[0].pageSlug or .urlSlug if not set to some wrong id like it was the case for spirit-of-the-north-f58a66 - this is also what's done here: https://github.com/claabs/epicgames-freegames-node/blob/938a9653ffd08b8284ea32cf01ac8727d25c5d4c/src/puppet/free-games.ts#L138-L213
const urlSlugs = await Promise.all((await game_loc.elementHandles()).map(a => a.getAttribute('href')));
const urls = urlSlugs.map(s => 'https://store.epicgames.com' + s);
console.log('Free games:', urls);
Expand Down Expand Up @@ -235,7 +235,7 @@ try {
// console.info(' Saved a screenshot of hcaptcha challenge to', p);
// console.error(' Got hcaptcha challenge. To avoid it, get a link from https://www.hcaptcha.com/accessibility'); // TODO save this link in config and visit it daily to set accessibility cookie to avoid captcha challenge?
}).catch(_ => { }); // may time out if not shown
await page.locator('text=Thanks for your order!').waitFor({state: 'attached'});
await page.locator('text=Thanks for your order!').waitFor({ state: 'attached' });
db.data[user][game_id].status = 'claimed';
db.data[user][game_id].time = datetime(); // claimed time overwrites failed/dryrun time
console.log(' Claimed successfully!');
Expand All @@ -260,8 +260,7 @@ try {
process.exitCode ||= 1;
console.error('--- Exception:');
console.error(error); // .toString()?
if (error.message && process.exitCode != 130)
notify(`epic-games failed: ${error.message.split('\n')[0]}`);
if (error.message && process.exitCode != 130) notify(`epic-games failed: ${error.message.split('\n')[0]}`);
} finally {
await db.write(); // write out json db
if (notify_games.filter(g => g.status == 'claimed' || g.status == 'failed').length) { // don't notify if all have status 'existed', 'manual', 'requires base game', 'unavailable-in-region', 'skipped'
Expand Down
25 changes: 12 additions & 13 deletions gog.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const db = await jsonDb('gog.json', {});
const context = await firefox.launchPersistentContext(cfg.dir.browser, {
headless: cfg.headless,
viewport: { width: cfg.width, height: cfg.height },
locale: "en-US", // ignore OS locale to be sure to have english text for locators -> done via /en in URL
locale: 'en-US', // ignore OS locale to be sure to have english text for locators -> done via /en in URL
recordVideo: cfg.record ? { dir: 'data/record/', size: { width: cfg.width, height: cfg.height } } : undefined, // will record a .webm video for each page navigated; without size, video would be scaled down to fit 800x800
recordHar: cfg.record ? { path: `data/record/gog-${datetime()}.har` } : undefined, // will record a HAR file with network requests and responses; can be imported in Chrome devtools
handleSIGINT: false, // have to handle ourselves and call context.close(), otherwise recordings from above won't be saved
Expand All @@ -31,7 +31,7 @@ const notify_games = [];
let user;

try {
await context.addCookies([{name: 'CookieConsent', value: '{stamp:%274oR8MJL+bxVlG6g+kl2we5+suMJ+Tv7I4C5d4k+YY4vrnhCD+P23RQ==%27%2Cnecessary:true%2Cpreferences:true%2Cstatistics:true%2Cmarketing:true%2Cmethod:%27explicit%27%2Cver:1%2Cutc:1672331618201%2Cregion:%27de%27}', domain: 'www.gog.com', path: '/'}]); // to not waste screen space when non-headless
await context.addCookies([{ name: 'CookieConsent', value: '{stamp:%274oR8MJL+bxVlG6g+kl2we5+suMJ+Tv7I4C5d4k+YY4vrnhCD+P23RQ==%27%2Cnecessary:true%2Cpreferences:true%2Cstatistics:true%2Cmarketing:true%2Cmethod:%27explicit%27%2Cver:1%2Cutc:1672331618201%2Cregion:%27de%27}', domain: 'www.gog.com', path: '/' }]); // to not waste screen space when non-headless

await page.goto(URL_CLAIM, { waitUntil: 'domcontentloaded' }); // default 'load' takes forever

Expand All @@ -45,11 +45,11 @@ try {
await page.waitForSelector('#GalaxyAccountsFrameContainer iframe'); // TODO needed?
const iframe = page.frameLocator('#GalaxyAccountsFrameContainer iframe');
if (!cfg.debug) context.setDefaultTimeout(cfg.login_timeout); // give user some extra time to log in
console.info(`Login timeout is ${cfg.login_timeout/1000} seconds!`);
console.info(`Login timeout is ${cfg.login_timeout / 1000} seconds!`);
if (cfg.gog_email && cfg.gog_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 email = cfg.gog_email || await prompt({message: 'Enter email'});
const password = email && (cfg.gog_password || await prompt({type: 'password', message: 'Enter password'}));
const email = cfg.gog_email || await prompt({ message: 'Enter email' });
const password = email && (cfg.gog_password || await prompt({ type: 'password', message: 'Enter password' }));
if (email && password) {
iframe.locator('a[href="/logout"]').click().catch(_ => { }); // Click 'Change account' (email from previous login is set in some cookie)
await iframe.locator('#login_username').fill(email);
Expand All @@ -58,9 +58,9 @@ try {
// handle MFA, but don't await it
iframe.locator('form[name=second_step_authentication]').waitFor().then(async () => {
console.log('Two-Step Verification - Enter security code');
console.log(await iframe.locator('.form__description').innerText())
const otp = await prompt({type: 'text', message: 'Enter two-factor sign in code', validate: n => n.toString().length == 4 || 'The code must be 4 digits!'}); // can't use type: 'number' since it strips away leading zeros and codes sometimes have them
await iframe.locator('#second_step_authentication_token_letter_1').pressSequentially(otp.toString(), {delay: 10});
console.log(await iframe.locator('.form__description').innerText());
const otp = await prompt({ type: 'text', message: 'Enter two-factor sign in code', validate: n => n.toString().length == 4 || 'The code must be 4 digits!' }); // can't use type: 'number' since it strips away leading zeros and codes sometimes have them
await iframe.locator('#second_step_authentication_token_letter_1').pressSequentially(otp.toString(), { delay: 10 });
await iframe.locator('#second_step_authentication_send').click();
await page.waitForTimeout(1000); // TODO still needed with wait for username below?
}).catch(_ => { });
Expand All @@ -71,7 +71,7 @@ try {
notify('gog: got captcha during login. Please check.');
// TODO solve reCAPTCHA?
}).catch(_ => { });
await page.waitForSelector('#menuUsername')
await page.waitForSelector('#menuUsername');
} else {
console.log('Waiting for you to login in the browser.');
await notify('gog: no longer signed in and not enough options set for automatic login.');
Expand Down Expand Up @@ -129,7 +129,7 @@ try {
notify_games.push({ title, url, status });

if (status == 'claimed' && !cfg.gog_newsletter) {
console.log("Unsubscribe from 'Promotions and hot deals' newsletter");
console.log('Unsubscribe from \'Promotions and hot deals\' newsletter');
await page.goto('https://www.gog.com/en/account/settings/subscriptions');
await page.locator('li:has-text("Marketing communications through Trusted Partners") label').uncheck();
await page.locator('li:has-text("Promotions and hot deals") label').uncheck();
Expand All @@ -139,13 +139,12 @@ try {
process.exitCode ||= 1;
console.error('--- Exception:');
console.error(error); // .toString()?
if (error.message && process.exitCode != 130)
notify(`gog failed: ${error.message.split('\n')[0]}`);
if (error.message && process.exitCode != 130) notify(`gog failed: ${error.message.split('\n')[0]}`);
} finally {
await db.write(); // write out json db
if (notify_games.filter(g => g.status != 'existed').length) { // don't notify if all were already claimed
notify(`gog (${user}):<br>${html_game_list(notify_games)}`);
}
}
if (page.video()) console.log('Recorded video:', await page.video().path())
if (page.video()) console.log('Recorded video:', await page.video().path());
await context.close();
3 changes: 1 addition & 2 deletions migrate.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import { JSONFile } from 'lowdb/node';
import { datetime } from './util.js';

const datetime_UTCtoLocalTimezone = async file => {
if (!existsSync(file))
return console.error('File does not exist:', file);
if (!existsSync(file)) return console.error('File does not exist:', file);
const db = new Low(new JSONFile(file));
await db.read();
db.data ||= {};
Expand Down
2 changes: 1 addition & 1 deletion notify-test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable no-constant-condition */
import { delay, html_game_list, notify } from "./util.js";
import { delay, html_game_list, notify } from './util.js';

const URL_CLAIM = 'https://gaming.amazon.com/home'; // dummy URL

Expand Down
Loading

0 comments on commit 0832ae5

Please sign in to comment.