From f0bf3c167147514077c723eec3b256de4d550988 Mon Sep 17 00:00:00 2001 From: Ralf Vogler Date: Thu, 31 Mar 2022 19:41:42 +0200 Subject: [PATCH 1/5] prime-gaming: fix wait for sign in, exit if not, arg show otherwise headless --- prime-gaming.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/prime-gaming.js b/prime-gaming.js index c9c630b..8a5801a 100644 --- a/prime-gaming.js +++ b/prime-gaming.js @@ -3,6 +3,8 @@ import path from 'path'; import { __dirname, stealth } from './util.js'; const debug = process.env.PWDEBUG == '1'; // runs headful and opens https://playwright.dev/docs/inspector +const show = process.argv.includes('show', 2); +const headless = !debug && !show; // const URL_LOGIN = 'https://www.amazon.de/ap/signin'; // wrong. needs some session args to be valid? const URL_CLAIM = 'https://gaming.amazon.com/home'; @@ -10,8 +12,8 @@ const TIMEOUT = 20 * 1000; // 20s, default is 30s // https://playwright.dev/docs/auth#multi-factor-authentication const context = await chromium.launchPersistentContext(path.resolve(__dirname, 'userDataDir'), { - channel: 'chrome', // https://playwright.dev/docs/browsers#google-chrome--microsoft-edge - headless: false, + // channel: 'chrome', // https://playwright.dev/docs/browsers#google-chrome--microsoft-edge, chrome will not work on arm64 linux, only chromium which is the default + headless, viewport: { width: 1280, height: 1280 }, userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36', // see replace of Headless in newStealthContext above. TODO update if browser is updated! locale: "en-US", // ignore OS locale to be sure to have english text for locators @@ -32,9 +34,15 @@ const clickIfExists = async selector => { }; await page.goto(URL_CLAIM, {waitUntil: 'domcontentloaded'}); // default 'load' takes forever +await Promise.any(['button:has-text("Sign in")', '[data-a-target="user-dropdown-first-name-text"]'].map(s => page.waitForSelector(s))); await clickIfExists('[aria-label="Cookies usage disclaimer banner"] button:has-text("Accept Cookies")'); // to not waste screen space in --debug while (await page.locator('button:has-text("Sign in")').count() > 0) { - console.error("Not signed in anymore. Please login and then navigate to the 'Free Games' page."); + console.error('Not signed in anymore.'); + if (headless) { + console.log('Please run `node prime-gaming show` to login in the opened browser.'); + await context.close(); // not needed? + process.exit(1); + } await page.click('button:has-text("Sign in")'); if (!debug) context.setDefaultTimeout(0); // give user time to log in without timeout await page.waitForNavigation({url: 'https://gaming.amazon.com/home?signedIn=true'}); From a707acf1b887e0d13a13e813bf5094e0a62bebf4 Mon Sep 17 00:00:00 2001 From: Ralf Vogler Date: Thu, 31 Mar 2022 19:43:07 +0200 Subject: [PATCH 2/5] prime-gaming: click Games since now only placeholders until in view --- prime-gaming.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/prime-gaming.js b/prime-gaming.js index 8a5801a..13677ef 100644 --- a/prime-gaming.js +++ b/prime-gaming.js @@ -49,6 +49,7 @@ while (await page.locator('button:has-text("Sign in")').count() > 0) { if (!debug) context.setDefaultTimeout(TIMEOUT); } console.log('Signed in.'); +await page.click('button:has-text("Games")'); await page.waitForSelector('div[data-a-target="offer-list-FGWP_FULL"]'); console.log('Number of already claimed games (total):', await page.locator('div[data-a-target="offer-list-FGWP_FULL"] p:has-text("Claimed")').count()); const game_sel = 'div[data-a-target="offer-list-FGWP_FULL"] .offer__action:has-text("Claim game")'; @@ -74,7 +75,7 @@ for (const card of games) { if (!card) break; const title = await (await card.$('h3')).innerText(); console.log('Current free game:', title); - await (await card.$('button')).click(); + await (await card.$('text=Claim')).click(); // await page.waitForNavigation(); await page.click('button:has-text("Claim now")'); // TODO only Origin shows a key, check for 'Claimed' or code From fd56cac06b754331b5ac58eece69a2646bcb8984 Mon Sep 17 00:00:00 2001 From: Ralf Vogler Date: Thu, 31 Mar 2022 19:55:19 +0200 Subject: [PATCH 3/5] prime-gaming: only print code to redeem game for Origin --- prime-gaming.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/prime-gaming.js b/prime-gaming.js index 13677ef..4e0dce0 100644 --- a/prime-gaming.js +++ b/prime-gaming.js @@ -78,9 +78,12 @@ for (const card of games) { await (await card.$('text=Claim')).click(); // await page.waitForNavigation(); await page.click('button:has-text("Claim now")'); + console.log(await (await page.$('[data-a-target="hero-header-subtitle"]')).innerText()); // TODO only Origin shows a key, check for 'Claimed' or code - const code = await page.inputValue('input[type="text"]'); - console.log('Code to redeem game:', code); + if (await page.locator('div:has-text("Origin")').count() > 0) { + const code = await page.inputValue('input[type="text"]'); + console.log('Code to redeem game:', code); + } // await page.pause(); await page.goto(URL_CLAIM, {waitUntil: 'domcontentloaded'}); n = await page.locator(game_sel).count(); From 930b7b525673faa598feb62b8e75a036050a202e Mon Sep 17 00:00:00 2001 From: Ralf Vogler Date: Thu, 31 Mar 2022 19:56:02 +0200 Subject: [PATCH 4/5] prime-gaming: removing Headless from userAgent not required, works headless --- prime-gaming.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prime-gaming.js b/prime-gaming.js index 4e0dce0..2f39690 100644 --- a/prime-gaming.js +++ b/prime-gaming.js @@ -15,7 +15,6 @@ const context = await chromium.launchPersistentContext(path.resolve(__dirname, ' // channel: 'chrome', // https://playwright.dev/docs/browsers#google-chrome--microsoft-edge, chrome will not work on arm64 linux, only chromium which is the default headless, viewport: { width: 1280, height: 1280 }, - userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36', // see replace of Headless in newStealthContext above. TODO update if browser is updated! locale: "en-US", // ignore OS locale to be sure to have english text for locators }); @@ -34,6 +33,7 @@ const clickIfExists = async selector => { }; await page.goto(URL_CLAIM, {waitUntil: 'domcontentloaded'}); // default 'load' takes forever +// need to wait for some elements to exist before checking if signed in or accepting cookies: await Promise.any(['button:has-text("Sign in")', '[data-a-target="user-dropdown-first-name-text"]'].map(s => page.waitForSelector(s))); await clickIfExists('[aria-label="Cookies usage disclaimer banner"] button:has-text("Accept Cookies")'); // to not waste screen space in --debug while (await page.locator('button:has-text("Sign in")').count() > 0) { From 0381b73d5eabc0cec36afa142b6a99f64a443d08 Mon Sep 17 00:00:00 2001 From: Ralf Vogler Date: Thu, 31 Mar 2022 20:10:29 +0200 Subject: [PATCH 5/5] readme: prime-gaming works headless, docker for epic-games, see #11 --- README.md | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index eff7f62..41e0b18 100644 --- a/README.md +++ b/README.md @@ -11,31 +11,36 @@ Claims free games on 2. Clone/download this repository and `cd` into it in a terminal 3. Run `npm install && npx playwright install chromium` -This downloads Chromium (337 MB) to a cache in home ([doc](https://playwright.dev/docs/browsers#managing-browser-binaries)). +This downloads Chromium (343 MB) to a cache in home ([doc](https://playwright.dev/docs/browsers#managing-browser-binaries)). ## Usage - -Both scripts start an automated Chromium instance. It will first check if you are logged in, and if not wait for you to do so. After login, you can also restart the script if it does not redirect back. +Both scripts start an automated Chromium instance, either with the browser GUI shown or hidden (*headless mode*). -If something goes wrong, use `PWDEBUG=1 node epic-games` to [inspect](https://playwright.dev/docs/inspector). +Login has to be done in the browser. It's hard to automate since you usually need to enter some OTP (but you can select 'remember this device'). +After login, the script will just continue, but you can also restart it. -Ideally, claiming would run in *headless mode* (without browser GUI - comment out `headless: false` to test), and on a Raspberry Pi: -- Epic Games Store detects running in headless mode (despite stealth plugin) and gets stuck with a captcha challenge ([issue](https://github.com/vogler/free-games-claimer/issues/2)). Did not test it yet for Prime Gaming. -- Playwright seems to not run on (headless) RPi? See [issue](https://github.com/vogler/free-games-claimer/issues/3). +If something goes wrong, use `PWDEBUG=1 node ...` to [inspect](https://playwright.dev/docs/inspector). ### Epic Games Store Run `node epic-games` -Login: Instead of redirecting back, the website seems to just reload the login URL. Go to https://www.epicgames.com/store/en-US/free-games manually, or restart the script. +Does not run headless, but can be run quasi-headless inside a Docker container (see below). + +They detect headless mode (despite stealth plugin) and it gets stuck with a captcha challenge ([issue](https://github.com/vogler/free-games-claimer/issues/2)). ### Amazon Prime Gaming -Run `node prime-gaming` +Run `node prime-gaming` + +Runs headless. Run `node prime-gaming show` to show the GUI (to login). Claiming the Amazon Games works, external Epic Games also work if the account is linked. -Keys for Origin and GOG should be printed to the console and need to be redeemed manually at the moment ([issue](https://github.com/vogler/free-games-claimer/issues/5)). +Keys for Origin (and GOG?) should be printed to the console and need to be redeemed manually at the moment ([issue](https://github.com/vogler/free-games-claimer/issues/5)). Other stores not tested. +### Docker +See https://github.com/vogler/free-games-claimer/pull/11 (TODO). + ### Run periodically Epic Games releases one (sometimes more) free game *every week*, but around christmas every day. Prime Gaming has new games *every month*.