Merge branch 'vogler:main' into main

This commit is contained in:
Trung Le 2022-04-01 01:59:40 +07:00 committed by GitHub
commit 3ecde7f3cb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 34 additions and 17 deletions

View file

@ -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
<!-- Use `npm run login` which opens a browser where you can login. When closing the browser, it writes a file `auth.json` containing cookies that should keep you logged in for some time (`expires` in a month?). -->
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`
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*.

View file

@ -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,10 +12,9 @@ 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,15 +33,23 @@ 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) {
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'});
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")';
@ -66,12 +75,15 @@ 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")');
console.log(await (await page.$('[data-a-target="hero-header-subtitle"]')).innerText());
// TODO only Origin shows a key, check for 'Claimed' or 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();