urls from json, go to purchaseURL instead of click, got captcha
This commit is contained in:
parent
bc8a89f365
commit
3ccec68c33
1 changed files with 31 additions and 26 deletions
|
|
@ -15,6 +15,19 @@ db.data ||= {};
|
|||
|
||||
handleSIGINT();
|
||||
|
||||
// get current promotionalOffers from json instead of checking the website
|
||||
// process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; // otherwise got UNABLE_TO_GET_ISSUER_CERT_LOCALLY
|
||||
const promoJson = await (await fetch('https://store-site-backend-static.ak.epicgames.com/freeGamesPromotions')).json(); // ?locale=en-US
|
||||
const currentGames = promoJson.data.Catalog.searchStore.elements.filter(e => e.promotions?.promotionalOffers?.length);
|
||||
const gameURL = e => `https://store.epicgames.com/p/${e.catalogNs.mappings[0].pageSlug}`; // gameURL(e.urlSlug) is wrong and leads to 404!
|
||||
console.log('Free games:', currentGames.map(e => `${e.title} - ${gameURL(e)}`));
|
||||
|
||||
// TODO check if there are new games to claim before launching browser? https://github.com/vogler/free-games-claimer/issues/29
|
||||
// Options:
|
||||
// 1. Check order history (https://www.epicgames.com/account/v2/payment/ajaxGetOrderHistory) - only contains the last 10 orders
|
||||
// 2. Check epic-games.json - would need to know the logged in user for `cfg.dir.browser`
|
||||
// However, this may not always speed up the process since a game may have already been claimed before.
|
||||
|
||||
// https://www.nopecha.com extension source from https://github.com/NopeCHA/NopeCHA/releases/tag/0.1.16
|
||||
// const ext = path.resolve('nopecha'); // used in Chromium, currently not needed in Firefox
|
||||
|
||||
|
|
@ -99,18 +112,10 @@ try {
|
|||
console.log(`Signed in as ${user}`);
|
||||
db.data[user] ||= {};
|
||||
|
||||
// Detect free games
|
||||
const game_loc = page.locator('a:has(span:text-is("Free Now"))');
|
||||
await game_loc.last().waitFor();
|
||||
// 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
|
||||
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);
|
||||
|
||||
for (const url of urls) {
|
||||
// This URL will order all free games, but it will fail if some games have already been claimed:
|
||||
// const purchaseURL = 'https://store.epicgames.com/purchase?' + currentGames.map(e => `offers=1-${e.namespace}-${e.id}`).join('&');
|
||||
for (const game of currentGames) {
|
||||
const url = gameURL(game);
|
||||
await page.goto(url); // , { waitUntil: 'domcontentloaded' });
|
||||
const btnText = await page.locator('//button[@data-testid="purchase-cta-button"][not(contains(.,"Loading"))]').first().innerText(); // barrier to block until page is loaded
|
||||
|
||||
|
|
@ -121,7 +126,8 @@ try {
|
|||
await page.waitForTimeout(2000);
|
||||
}
|
||||
|
||||
const title = await page.locator('h1').first().innerText();
|
||||
// const title = await page.locator('h1').first().innerText();
|
||||
const title = game.title;
|
||||
const game_id = page.url().split('/').pop();
|
||||
db.data[user][game_id] ||= { title, time: datetime(), url: page.url() }; // this will be set on the initial run only!
|
||||
console.log('Current free game:', title);
|
||||
|
|
@ -142,8 +148,10 @@ try {
|
|||
console.log(' Base game:', baseUrl);
|
||||
// await page.click('a:has-text("Overview")');
|
||||
} else { // GET
|
||||
console.log(' Not in library yet! Click GET.');
|
||||
await page.click('[data-testid="purchase-cta-button"]', { delay: 11 }); // got stuck here without delay (or mouse move), see #75, 1ms was also enough
|
||||
console.log(' Not in library yet! Claim!');
|
||||
// go to purchase of unclaimed game - https://github.com/vogler/free-games-claimer/issues/127
|
||||
const purchaseURL = `https://store.epicgames.com/purchase?offers=1-${game.namespace}-${game.id}`;
|
||||
await page.goto(purchaseURL);
|
||||
|
||||
// click Continue if 'Device not supported. This product is not compatible with your current device.' - avoided by Windows userAgent?
|
||||
page.click('button:has-text("Continue")').catch(_ => { }); // needed since change from Chromium to Firefox?
|
||||
|
|
@ -155,23 +163,20 @@ try {
|
|||
await page.locator('button:has-text("Accept")').click();
|
||||
}).catch(_ => { });
|
||||
|
||||
// it then creates an iframe for the purchase
|
||||
await page.waitForSelector('#webPurchaseContainer iframe'); // TODO needed?
|
||||
const iframe = page.frameLocator('#webPurchaseContainer iframe');
|
||||
// skip game if unavailable in region, https://github.com/vogler/free-games-claimer/issues/46 TODO check games for account's region
|
||||
if (await iframe.locator(':has-text("unavailable in your region")').count() > 0) {
|
||||
if (await page.locator(':has-text("unavailable in your region")').count() > 0) {
|
||||
console.error(' This product is unavailable in your region!');
|
||||
db.data[user][game_id].status = notify_game.status = 'unavailable-in-region';
|
||||
continue;
|
||||
}
|
||||
|
||||
iframe.locator('.payment-pin-code').waitFor().then(async () => {
|
||||
page.locator('.payment-pin-code').waitFor().then(async () => {
|
||||
if (!cfg.eg_parentalpin) {
|
||||
console.error(' EG_PARENTALPIN not set. Need to enter Parental Control PIN manually.');
|
||||
notify('epic-games: EG_PARENTALPIN not set. Need to enter Parental Control PIN manually.');
|
||||
}
|
||||
await iframe.locator('input.payment-pin-code__input').first().type(cfg.eg_parentalpin);
|
||||
await iframe.locator('button:has-text("Continue")').click({ delay: 11 });
|
||||
await page.locator('input.payment-pin-code__input').first().type(cfg.eg_parentalpin);
|
||||
await page.locator('button:has-text("Continue")').click({ delay: 11 });
|
||||
}).catch(_ => { });
|
||||
|
||||
if (cfg.debug) await page.pause();
|
||||
|
|
@ -181,14 +186,14 @@ try {
|
|||
}
|
||||
|
||||
// Playwright clicked before button was ready to handle event, https://github.com/vogler/free-games-claimer/issues/84#issuecomment-1474346591
|
||||
await iframe.locator('button:has-text("Place Order"):not(:has(.payment-loading--loading))').click({ delay: 11 });
|
||||
await page.locator('button:has-text("Place Order"):not(:has(.payment-loading--loading))').click({ delay: 11 });
|
||||
|
||||
// I Agree button is only shown for EU accounts! https://github.com/vogler/free-games-claimer/pull/7#issuecomment-1038964872
|
||||
const btnAgree = iframe.locator('button:has-text("I Agree")');
|
||||
const btnAgree = page.locator('button:has-text("I Agree")');
|
||||
btnAgree.waitFor().then(() => btnAgree.click()).catch(_ => { }); // EU: wait for and click 'I Agree'
|
||||
try {
|
||||
// context.setDefaultTimeout(100 * 1000); // give time to solve captcha, iframe goes blank after 60s?
|
||||
const captcha = iframe.locator('#h_captcha_challenge_checkout_free_prod iframe');
|
||||
const captcha = page.locator('#h_captcha_challenge_checkout_free_prod iframe');
|
||||
captcha.waitFor().then(async () => { // don't await, since element may not be shown
|
||||
// console.info(' Got hcaptcha challenge! NopeCHA extension will likely solve it.')
|
||||
console.error(' Got hcaptcha challenge! Lost trust due to too many login attempts? You can solve the captcha in the browser or get a new IP address.')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue