👷 ci(build): add SonarQube scan to build workflow
- introduce SonarQube scanning step for code quality analysis - update workflow dependencies and execution order 🐛 fix(auth): improve error handling and code formatting - remove unused imports and fix code indentation - enhance error handling with improved catch blocks 💄 style(general): standardize code formatting and style consistency - update various files to ensure consistent code style - adjust indentation and whitespace for readability
This commit is contained in:
parent
0e5303da62
commit
d40a577f47
7 changed files with 95 additions and 62 deletions
|
|
@ -41,7 +41,7 @@ const handleMFA = async p => {
|
|||
const otpField = p.locator('#auth-mfa-otpcode, input[name=otpCode]');
|
||||
if (!await otpField.count()) return false;
|
||||
console.log('Two-Step Verification - enter the One Time Password (OTP), e.g. generated by your Authenticator App');
|
||||
await p.locator('#auth-mfa-remember-device, [name=rememberDevice]').check().catch(_ => {});
|
||||
await p.locator('#auth-mfa-remember-device, [name=rememberDevice]').check().catch(() => {});
|
||||
const otp = cfg.pg_otpkey && authenticator.generate(cfg.pg_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 otpField.first().pressSequentially(otp.toString());
|
||||
await p.locator('input[type="submit"], button[type="submit"]').first().click();
|
||||
|
|
@ -61,7 +61,7 @@ try {
|
|||
await page.click('input[type="submit"]');
|
||||
await page.fill('[name=password]', password);
|
||||
await page.click('input[type="submit"]');
|
||||
await handleMFA(page).catch(_ => {});
|
||||
await handleMFA(page).catch(() => {});
|
||||
page.waitForURL('**/ap/signin**').then(async () => { // check for wrong credentials
|
||||
const error = await page.locator('.a-alert-content').first().innerText();
|
||||
if (!error.trim.length) return;
|
||||
|
|
@ -91,7 +91,7 @@ try {
|
|||
'[data-a-target="user-dropdown-first-name-text"]',
|
||||
'[data-testid="user-dropdown-first-name-text"]',
|
||||
].map(s => page.waitForSelector(s)));
|
||||
page.click('[aria-label="Cookies usage disclaimer banner"] button:has-text("Accept Cookies")').catch(_ => { }); // to not waste screen space when non-headless, TODO does not work reliably, need to wait for something else first?
|
||||
page.click('[aria-label="Cookies usage disclaimer banner"] button:has-text("Accept Cookies")').catch(() => { }); // to not waste screen space when non-headless, TODO does not work reliably, need to wait for something else first?
|
||||
while (await page.locator('button:has-text("Sign in"), button:has-text("Anmelden")').count() > 0) {
|
||||
console.error('Not signed in anymore.');
|
||||
await page.click('button:has-text("Sign in")');
|
||||
|
|
@ -115,7 +115,7 @@ try {
|
|||
await context.close(); // finishes potential recording
|
||||
process.exit(1);
|
||||
});
|
||||
handleMFA(page).catch(_ => {});
|
||||
handleMFA(page).catch(() => {});
|
||||
} else {
|
||||
console.log('Waiting for you to login in the browser.');
|
||||
await notify('prime-gaming: no longer signed in and not enough options set for automatic login.');
|
||||
|
|
@ -209,7 +209,7 @@ try {
|
|||
const games = await locateGamesList();
|
||||
// Load all cards (old and new layout) by scrolling the container or the page
|
||||
if (games) await scrollUntilStable(() => games.evaluate(el => el.scrollHeight).catch(() => 0));
|
||||
await scrollUntilStable(() => page.evaluate(() => document.scrollingElement?.scrollHeight ?? 0));
|
||||
await scrollUntilStable(() => page.evaluate(() => globalThis.document?.scrollingElement?.scrollHeight ?? 0));
|
||||
|
||||
const normalizeClaimUrl = url => {
|
||||
if (!url) return { url, key: null };
|
||||
|
|
@ -241,7 +241,7 @@ try {
|
|||
const hrefs = [...new Set(await anchorClaims.evaluateAll(anchors => anchors.map(a => a.getAttribute('href')).filter(Boolean)))];
|
||||
for (const href of hrefs) {
|
||||
const { url, key } = normalizeClaimUrl(href);
|
||||
const title = key || (await anchorClaims.first().innerText()) || 'Unknown title';
|
||||
const title = key || await anchorClaims.first().innerText() || 'Unknown title';
|
||||
cards.push({ kind: 'external', title, url, key });
|
||||
}
|
||||
}
|
||||
|
|
@ -286,15 +286,15 @@ try {
|
|||
// bottom to top: oldest to newest games
|
||||
internal.reverse();
|
||||
external.reverse();
|
||||
const sameOrNewPage = async url => new Promise(async (resolve, _reject) => {
|
||||
const sameOrNewPage = async url => {
|
||||
const isNew = page.url() != url;
|
||||
let p = page;
|
||||
if (isNew) {
|
||||
p = await context.newPage();
|
||||
await p.goto(url, { waitUntil: 'domcontentloaded' });
|
||||
}
|
||||
resolve([p, isNew]);
|
||||
});
|
||||
return [p, isNew];
|
||||
};
|
||||
const skipBasedOnTime = async url => {
|
||||
// console.log(' Checking time left for game:', url);
|
||||
const [p, isNew] = await sameOrNewPage(url);
|
||||
|
|
@ -305,12 +305,12 @@ try {
|
|||
}
|
||||
const dueDateOrg = await dueDateLoc.first().innerText();
|
||||
const dueDate = new Date(Date.parse(dueDateOrg + ' 17:00'));
|
||||
const daysLeft = (dueDate.getTime() - Date.now())/1000/60/60/24;
|
||||
const daysLeft = (dueDate.getTime() - Date.now()) / 1000 / 60 / 60 / 24;
|
||||
const availabilityText = await p.locator('.availability-date, [data-testid="availability-end-date"], [data-test-selector="availability-end-date"]').first().innerText().catch(() => dueDateOrg);
|
||||
console.log(' ', availabilityText, '->', daysLeft.toFixed(2));
|
||||
if (isNew) await p.close();
|
||||
return daysLeft > cfg.pg_timeLeft;
|
||||
}
|
||||
};
|
||||
console.log('\nNumber of free unclaimed games (Prime Gaming):', internal.length);
|
||||
// claim games in internal store
|
||||
for (const card of internal) {
|
||||
|
|
@ -353,12 +353,16 @@ try {
|
|||
try {
|
||||
await c.waitFor({ state: 'visible', timeout: 5000 });
|
||||
if (!await c.isEnabled()) {
|
||||
await c.evaluate(el => { el.disabled = false; el.removeAttribute('disabled'); el.click(); });
|
||||
await c.evaluate(el => {
|
||||
el.disabled = false;
|
||||
el.removeAttribute('disabled');
|
||||
el.click();
|
||||
});
|
||||
} else {
|
||||
await c.click();
|
||||
}
|
||||
return true;
|
||||
} catch (_) {
|
||||
} catch {
|
||||
// try next candidate
|
||||
}
|
||||
}
|
||||
|
|
@ -375,7 +379,7 @@ try {
|
|||
continue;
|
||||
}
|
||||
await page.goto(url, { waitUntil: 'domcontentloaded' });
|
||||
await page.waitForSelector('[data-a-target="buy-box"]', { timeout: 10000 }).catch(_ => {});
|
||||
await page.waitForSelector('[data-a-target="buy-box"]', { timeout: 10000 }).catch(() => {});
|
||||
if (cfg.debug) await page.pause();
|
||||
let store = 'unknown';
|
||||
const detailLoc = page.locator('[data-a-target="DescriptionItemDetails"], [data-testid="DescriptionItemDetails"]');
|
||||
|
|
@ -438,9 +442,9 @@ try {
|
|||
if (cfg.interactive && !await confirm()) continue;
|
||||
await clickCTA(page);
|
||||
await Promise.any([
|
||||
page.waitForSelector('.thank-you-title:has-text("Success")', { timeout: cfg.timeout }).catch(_ => {}),
|
||||
page.waitForSelector('div:has-text("Link game account")', { timeout: cfg.timeout }).catch(_ => {}),
|
||||
]).catch(_ => {});
|
||||
page.waitForSelector('.thank-you-title:has-text("Success")', { timeout: cfg.timeout }).catch(() => {}),
|
||||
page.waitForSelector('div:has-text("Link game account")', { timeout: cfg.timeout }).catch(() => {}),
|
||||
]).catch(() => {});
|
||||
db.data[user][title] ||= { title, time: datetime(), url, store };
|
||||
if (await page.locator('div:has-text("Link game account")').count() // TODO still needed? epic games store just has 'Link account' as the button text now.
|
||||
|| await page.locator('div:has-text("Link account")').count()) {
|
||||
|
|
@ -466,16 +470,16 @@ try {
|
|||
let code;
|
||||
try {
|
||||
// ensure CTA was clicked in case code is behind it
|
||||
await clickCTA(page).catch(_ => {});
|
||||
await clickCTA(page).catch(() => {});
|
||||
code = await Promise.any([
|
||||
page.inputValue('input[type="text"]'),
|
||||
page.textContent('[data-a-target="ClaimStateClaimCodeContent"]').then(s => s.replace('Your code: ', '')),
|
||||
]);
|
||||
} catch (_) {
|
||||
} catch {
|
||||
console.error(' Could not find claim code on page (timeout). Please check manually.');
|
||||
db.data[user][title].status = 'claimed (code not found)';
|
||||
notify_game.status = 'claimed (code not found)';
|
||||
await page.screenshot({ path: screenshot('external', `${filenamify(title)}_nocode.png`), fullPage: true }).catch(_ => {});
|
||||
await page.screenshot({ path: screenshot('external', `${filenamify(title)}_nocode.png`), fullPage: true }).catch(() => {});
|
||||
continue;
|
||||
}
|
||||
console.log(' Code to redeem game:', chalk.blue(code));
|
||||
|
|
@ -563,7 +567,7 @@ try {
|
|||
if (j?.events?.cart.length && j.events.cart[0]?.data?.reason == 'UserAlreadyOwnsContent') {
|
||||
redeem_action = 'already redeemed';
|
||||
console.error(' error: UserAlreadyOwnsContent');
|
||||
} else if (true) { // TODO what's returned on success?
|
||||
} else { // TODO what's returned on success?
|
||||
redeem_action = 'redeemed';
|
||||
db.data[user][title].status = 'claimed and redeemed?';
|
||||
console.log(' Redeemed successfully? Please report if not in https://github.com/vogler/free-games-claimer/issues/5');
|
||||
|
|
@ -610,7 +614,7 @@ try {
|
|||
// await page.pause();
|
||||
}
|
||||
await page.goto(URL_CLAIM, { waitUntil: 'domcontentloaded' });
|
||||
page.click('button[data-type="Game"]').catch(_ => {});
|
||||
page.click('button[data-type="Game"]').catch(() => {});
|
||||
|
||||
if (notify_games.length && games) { // make screenshot of all games if something was claimed and list exists
|
||||
const p = screenshot(`${filenamify(datetime())}.png`);
|
||||
|
|
@ -628,7 +632,7 @@ try {
|
|||
await loot.waitFor();
|
||||
|
||||
process.stdout.write('Loading all DLCs on page...');
|
||||
await scrollUntilStable(() => loot.locator('[data-a-target="item-card"]').count())
|
||||
await scrollUntilStable(() => loot.locator('[data-a-target="item-card"]').count());
|
||||
|
||||
console.log('\nNumber of already claimed DLC:', await loot.locator('p:has-text("Collected")').count());
|
||||
|
||||
|
|
@ -657,7 +661,7 @@ try {
|
|||
// most games have a button 'Get in-game content'
|
||||
// epic-games: Fall Guys: Claim -> Continue -> Go to Epic Games (despite account linked and logged into epic-games) -> not tied to account but via some cookie?
|
||||
await Promise.any([page.click('.tw-button:has-text("Get in-game content")'), page.click('.tw-button:has-text("Claim your gift")'), page.click('.tw-button:has-text("Claim")').then(() => page.click('button:has-text("Continue")'))]);
|
||||
page.click('button:has-text("Continue")').catch(_ => { });
|
||||
page.click('button:has-text("Continue")').catch(() => { });
|
||||
const linkAccountButton = page.locator('[data-a-target="LinkAccountButton"]');
|
||||
let unlinked_store;
|
||||
if (await linkAccountButton.count()) {
|
||||
|
|
@ -674,7 +678,7 @@ try {
|
|||
dlc_unlinked[unlinked_store] ??= [];
|
||||
dlc_unlinked[unlinked_store].push(title);
|
||||
} else {
|
||||
const code = await page.inputValue('input[type="text"]').catch(_ => undefined);
|
||||
const code = await page.inputValue('input[type="text"]').catch(() => undefined);
|
||||
console.log(' Code to redeem game:', chalk.blue(code));
|
||||
db.data[user][title].code = code;
|
||||
db.data[user][title].status = 'claimed';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue