From 6b13287b61ff684f1d8c6e157c079c8e1f6fc4fd Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 11 May 2023 18:38:44 +0200
Subject: [PATCH 001/253] eg: click 'Yes, buy now' if 'This edition contains
something you already have. Still interested?'
Happened for add-on https://store.epicgames.com/en-US/p/the-sims-4--the-daring-lifestyle-bundle
---
epic-games.js | 3 +++
1 file changed, 3 insertions(+)
diff --git a/epic-games.js b/epic-games.js
index 7e1bf62..37310ea 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -155,6 +155,9 @@ try {
// 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?
+ // click 'Yes, buy now' if 'This edition contains something you already have. Still interested?'
+ page.click('button:has-text("Yes, buy now")').catch(_ => { });
+
// Accept End User License Agreement (only needed once)
page.locator('input#agree').waitFor().then(async () => {
console.log('Accept End User License Agreement (only needed once)');
From af935d48898d4df8490483c4611a591c3d36f951 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 11 May 2023 18:50:54 +0200
Subject: [PATCH 002/253] gog: no quotes around user, as for {eg, pg}
---
gog.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/gog.js b/gog.js
index 4e27cc2..1785663 100644
--- a/gog.js
+++ b/gog.js
@@ -84,7 +84,7 @@ try {
if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
}
user = await page.locator('#menuUsername').first().textContent(); // innerText is uppercase due to styling!
- console.log(`Signed in as '${user}'`);
+ console.log(`Signed in as ${user}`);
db.data[user] ||= {};
const banner = page.locator('#giveaway');
From 491ee899a5d7d7e517eadceee4875e457e8b6b0c Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 11 May 2023 18:57:48 +0200
Subject: [PATCH 003/253] pg: #126 change selector to handle potential button
text 'Claim' (instead of 'Claim game') for internal games
---
prime-gaming.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index b7fa620..4330cd9 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -96,7 +96,7 @@ try {
const games_sel = 'div[data-a-target="offer-list-FGWP_FULL"]';
await page.waitForSelector(games_sel);
console.log('Number of already claimed games (total):', await page.locator(`${games_sel} p:has-text("Collected")`).count());
- const game_sel = `${games_sel} [data-a-target="item-card"]:has-text("Claim game")`;
+ const game_sel = `${games_sel} [data-a-target="claim-prime-offer-card"]:has-text("Claim")`;
console.log('Number of free unclaimed games (Prime Gaming):', await page.locator(game_sel).count());
const games = await page.$$(game_sel);
// for (let i=1; i<=n; i++) {
@@ -106,7 +106,7 @@ try {
const title = await (await card.$('.item-card-details__body__primary')).innerText();
console.log('Current free game:', title);
if (cfg.dryrun) continue;
- await (await card.$('button:has-text("Claim game")')).click();
+ await (await card.$('button:has-text("Claim")')).click();
db.data[user][title] ||= { title, time: datetime(), store: 'internal' };
notify_games.push({ title, status: 'claimed', url: URL_CLAIM });
// const img = await (await card.$('img.tw-image')).getAttribute('src');
@@ -116,7 +116,7 @@ try {
}
// claim games in external/linked stores. Linked: origin.com, epicgames.com; Redeem-key: gog.com, legacygames.com, microsoft
let n;
- const game_sel_ext = `${games_sel} [data-a-target="item-card"]:has(p:text-is("Claim"))`;
+ const game_sel_ext = `${games_sel} [data-a-target="learn-more-card"]:has(p:text-is("Claim"))`;
do {
n = await page.locator(game_sel_ext).count();
console.log('Number of free unclaimed games (external stores):', n);
From 290fe289d4dac79e595f5bddf9ed54a72dd75a2a Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 16 May 2023 13:31:53 +0200
Subject: [PATCH 004/253] pg: fix #142: `PG_CLAIMDLC` locator 'a' resolved to 2
elements
---
prime-gaming.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 4330cd9..8390fd5 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -259,7 +259,7 @@ try {
const dlcs = await Promise.all(cards.map(async card => ({
game: await card.locator('.item-card-details__body p').innerText(),
title: await card.locator('.item-card-details__body__primary').innerText(),
- url: 'https://gaming.amazon.com' + await card.locator('a').getAttribute('href'),
+ url: 'https://gaming.amazon.com' + await card.locator('a').first().getAttribute('href'),
})));
// console.log(dlcs);
From 590b01aba2d94b97a5bb4e35411e18b9ef4ed6f5 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 17 May 2023 00:11:35 +0200
Subject: [PATCH 005/253] pg: refactor: use more locators & all() instead of $
---
prime-gaming.js | 51 ++++++++++++++++++++++---------------------------
1 file changed, 23 insertions(+), 28 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 8390fd5..6f95c23 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -93,20 +93,18 @@ try {
}
await page.click('button[data-type="Game"]');
- const games_sel = 'div[data-a-target="offer-list-FGWP_FULL"]';
- await page.waitForSelector(games_sel);
- console.log('Number of already claimed games (total):', await page.locator(`${games_sel} p:has-text("Collected")`).count());
- const game_sel = `${games_sel} [data-a-target="claim-prime-offer-card"]:has-text("Claim")`;
- console.log('Number of free unclaimed games (Prime Gaming):', await page.locator(game_sel).count());
- const games = await page.$$(game_sel);
- // for (let i=1; i<=n; i++) {
- for (const card of games) {
- // const card = page.locator(`:nth-match(${game_sel}, ${i})`); // this will reevaluate after games are claimed and index will be wrong
- // const title = await card.locator('h3').first().innerText();
- const title = await (await card.$('.item-card-details__body__primary')).innerText();
+ const games = page.locator('div[data-a-target="offer-list-FGWP_FULL"]');
+ await games.waitFor();
+ console.log('Number of already claimed games (total):', await games.locator('p:has-text("Collected")').count());
+ const internal = await games.locator('[data-a-target="claim-prime-offer-card"]:has-text("Claim")').all();
+ const external = await games.locator('[data-a-target="learn-more-card"]:has(p:text-is("Claim"))').all();
+ console.log('Number of free unclaimed games (Prime Gaming):', internal.length);
+ // claim games in internal store
+ for (const card of internal) {
+ const title = await card.locator('.item-card-details__body__primary').innerText();
console.log('Current free game:', title);
if (cfg.dryrun) continue;
- await (await card.$('button:has-text("Claim")')).click();
+ await card.locator('button:has-text("Claim")').click();
db.data[user][title] ||= { title, time: datetime(), store: 'internal' };
notify_games.push({ title, status: 'claimed', url: URL_CLAIM });
// const img = await (await card.$('img.tw-image')).getAttribute('src');
@@ -114,18 +112,15 @@ try {
const p = path.resolve(cfg.dir.screenshots, 'prime-gaming', 'internal', `${filenamify(title)}.png`);
await card.screenshot({ path: p });
}
+ console.log('Number of free unclaimed games (external stores):', external.length);
// claim games in external/linked stores. Linked: origin.com, epicgames.com; Redeem-key: gog.com, legacygames.com, microsoft
- let n;
- const game_sel_ext = `${games_sel} [data-a-target="learn-more-card"]:has(p:text-is("Claim"))`;
- do {
- n = await page.locator(game_sel_ext).count();
- console.log('Number of free unclaimed games (external stores):', n);
- const card = await page.$(game_sel_ext);
+ for (const card of external) {
if (!card) break;
- const title = await (await card.$('.item-card-details__body__primary')).innerText();
+ const title = await card.locator('.item-card-details__body__primary').innerText();
console.log('Current free game:', title);
- if (cfg.dryrun) break; // TODO change back to continue, but need different iteration scheme
- await (await card.$('text=Claim')).click(); // goes to URL of game, no need to wait
+ if (cfg.debug) await page.pause();
+ if (cfg.dryrun) continue;
+ await card.locator('text=Claim').click(); // goes to URL of game, no need to wait
await Promise.any([page.click('button:has-text("Claim now")'), page.click('button:has-text("Complete Claim")'), page.waitForSelector('div:has-text("Link game account")')]); // waits for navigation
const store_text = await (await page.$('[data-a-target="hero-header-subtitle"]')).innerText();
// Full game for PC [and MAC] on: gog.com, Origin, Legacy Games, EPIC GAMES, Battle.net
@@ -238,23 +233,23 @@ try {
// await page.pause();
await page.goto(URL_CLAIM, { waitUntil: 'domcontentloaded' });
await page.click('button[data-type="Game"]');
- } while (n);
+ }
const p = path.resolve(cfg.dir.screenshots, 'prime-gaming', `${filenamify(datetime())}.png`);
// await page.screenshot({ path: p, fullPage: true }); // fullPage does not make a difference since scroll not on body but on some element
await page.keyboard.press('End'); // scroll to bottom to show all games
await page.waitForTimeout(1000); // wait for fade in animation
const viewportSize = page.viewportSize(); // current viewport size
await page.setViewportSize({...viewportSize, height: 3000}); // increase height, otherwise element screenshot is cut off at the top and bottom
- if (notify_games.length) await page.locator(games_sel).screenshot({ path: p }); // screenshot of all claimed games
+ if (notify_games.length) await games.screenshot({ path: p }); // screenshot of all claimed games
if (cfg.pg_claimdlc) {
console.log('Trying to claim in-game content...');
await page.click('button[data-type="InGameLoot"]');
- const loot_sel = 'div[data-a-target="offer-list-IN_GAME_LOOT"]';
- await page.waitForSelector(loot_sel);
- console.log('Number of already claimed DLC:', await page.locator(`${loot_sel} p:has-text("Collected")`).count());
+ const loot = page.locator('div[data-a-target="offer-list-IN_GAME_LOOT"]');
+ await loot.waitFor();
+ console.log('Number of already claimed DLC:', await loot.locator('p:has-text("Collected")').count());
- const cards = await page.locator(`${loot_sel} [data-a-target="item-card"]:has(p:text-is("Claim"))`).all();
+ const cards = await loot.locator('[data-a-target="item-card"]:has(p:text-is("Claim"))').all();
console.log('Number of unclaimed DLC:', cards.length);
const dlcs = await Promise.all(cards.map(async card => ({
game: await card.locator('.item-card-details__body p').innerText(),
@@ -267,7 +262,7 @@ try {
const title = `${dlc.game} - ${dlc.title}`;
const url = dlc.url;
console.log('Current DLC:', title);
- // if (cfg.dryrun) continue;
+ if (cfg.dryrun) continue;
db.data[user][title] ||= { title, time: datetime(), store: 'DLC', status: 'failed: need account linking' };
const notify_game = { title, url };
notify_games.push(notify_game); // status is updated below
From c4eb1e03ce4c8e8ab4c14c3b1b74fd0a52d601b2 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 17 May 2023 00:41:37 +0200
Subject: [PATCH 006/253] pg: need elementHandles() instead of all() for
internal since it changes
---
prime-gaming.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 6f95c23..012aab6 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -96,15 +96,15 @@ try {
const games = page.locator('div[data-a-target="offer-list-FGWP_FULL"]');
await games.waitFor();
console.log('Number of already claimed games (total):', await games.locator('p:has-text("Collected")').count());
- const internal = await games.locator('[data-a-target="claim-prime-offer-card"]:has-text("Claim")').all();
+ const internal = await games.locator('[data-a-target="claim-prime-offer-card"]:has-text("Claim")').elementHandles(); // can't use .all() here since the list of elements will change after click while we iterate over it
const external = await games.locator('[data-a-target="learn-more-card"]:has(p:text-is("Claim"))').all();
console.log('Number of free unclaimed games (Prime Gaming):', internal.length);
// claim games in internal store
for (const card of internal) {
- const title = await card.locator('.item-card-details__body__primary').innerText();
+ const title = await (await card.$('.item-card-details__body__primary')).innerText();
console.log('Current free game:', title);
if (cfg.dryrun) continue;
- await card.locator('button:has-text("Claim")').click();
+ await (await card.$('button:has-text("Claim")')).click();
db.data[user][title] ||= { title, time: datetime(), store: 'internal' };
notify_games.push({ title, status: 'claimed', url: URL_CLAIM });
// const img = await (await card.$('img.tw-image')).getAttribute('src');
From 60acf747d04f74a9412e93daad4890b9ea04456c Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 17 May 2023 00:55:58 +0200
Subject: [PATCH 007/253] pg: scroll to bottom to load all games
There may be so many unclaimed games that not all of them are loaded
initially.
Also relevant to show the correct number of collected games.
---
prime-gaming.js | 3 +++
1 file changed, 3 insertions(+)
diff --git a/prime-gaming.js b/prime-gaming.js
index 8390fd5..d5d9453 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -93,6 +93,8 @@ try {
}
await page.click('button[data-type="Game"]');
+ await page.keyboard.press('End'); // scroll to bottom to show all games
+ await page.waitForLoadState('networkidle'); // wait for all games to be loaded
const games_sel = 'div[data-a-target="offer-list-FGWP_FULL"]';
await page.waitForSelector(games_sel);
console.log('Number of already claimed games (total):', await page.locator(`${games_sel} p:has-text("Collected")`).count());
@@ -102,6 +104,7 @@ try {
// for (let i=1; i<=n; i++) {
for (const card of games) {
// const card = page.locator(`:nth-match(${game_sel}, ${i})`); // this will reevaluate after games are claimed and index will be wrong
+ await card.scrollIntoViewIfNeeded();
// const title = await card.locator('h3').first().innerText();
const title = await (await card.$('.item-card-details__body__primary')).innerText();
console.log('Current free game:', title);
From 240e64ae4cd03a908b4e54ede049c7fdd8e8f048 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 17 May 2023 10:20:17 +0200
Subject: [PATCH 008/253] pg: also use elementHandles() for external
---
prime-gaming.js | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 702d996..3513b50 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -98,8 +98,9 @@ try {
const games = page.locator('div[data-a-target="offer-list-FGWP_FULL"]');
await games.waitFor();
console.log('Number of already claimed games (total):', await games.locator('p:has-text("Collected")').count());
- const internal = await games.locator('[data-a-target="claim-prime-offer-card"]:has-text("Claim")').elementHandles(); // can't use .all() here since the list of elements will change after click while we iterate over it
- const external = await games.locator('[data-a-target="learn-more-card"]:has(p:text-is("Claim"))').all();
+ // can't use .all() since the list of elements via locator will change after click while we iterate over it
+ const internal = await games.locator('[data-a-target="claim-prime-offer-card"]:has-text("Claim")').elementHandles();
+ const external = await games.locator('[data-a-target="learn-more-card"]:has(p:text-is("Claim"))').elementHandles();
console.log('Number of free unclaimed games (Prime Gaming):', internal.length);
// claim games in internal store
for (const card of internal) {
@@ -118,12 +119,12 @@ try {
console.log('Number of free unclaimed games (external stores):', external.length);
// claim games in external/linked stores. Linked: origin.com, epicgames.com; Redeem-key: gog.com, legacygames.com, microsoft
for (const card of external) {
- if (!card) break;
- const title = await card.locator('.item-card-details__body__primary').innerText();
+ // if (!card) continue;
+ const title = await (await card.$('.item-card-details__body__primary')).innerText();
console.log('Current free game:', title);
if (cfg.debug) await page.pause();
if (cfg.dryrun) continue;
- await card.locator('text=Claim').click(); // goes to URL of game, no need to wait
+ await (await card.$('text=Claim')).click(); // goes to URL of game, no need to wait
await Promise.any([page.click('button:has-text("Claim now")'), page.click('button:has-text("Complete Claim")'), page.waitForSelector('div:has-text("Link game account")')]); // waits for navigation
const store_text = await (await page.$('[data-a-target="hero-header-subtitle"]')).innerText();
// Full game for PC [and MAC] on: gog.com, Origin, Legacy Games, EPIC GAMES, Battle.net
From e4ebae3744593e3268b61f520b47bef2febd1a11 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 17 May 2023 11:15:07 +0200
Subject: [PATCH 009/253] pg: `PG_REDEEM`, fixes for gog, #5
---
prime-gaming.js | 28 +++++++++++++++++++++-------
1 file changed, 21 insertions(+), 7 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 3513b50..bcea0f0 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -163,15 +163,16 @@ try {
if (store == 'gog.com') {
// await page.goto(`https://redeem.gog.com/v1/bonusCodes/${code}`); // {"reason":"Invalid or no captcha"}
await page2.fill('#codeInput', code);
- const r = page2.waitForResponse(r => r.url().startsWith('https://redeem.gog.com/'));
- await page2.click('[type="submit"]');
- // console.log(await page2.locator('.warning-message').innerText());
- const rt = await (await r).text();
- console.debug(` Response: ${rt}`);
+ // wait for responses before clicking on Continue and then Redeem
+ // first there are requests with OPTIONS and GET to https://redeem.gog.com/v1/bonusCodes/XYZ?language=de-DE
+ const r1 = page2.waitForResponse(r => r.request().method() == 'GET' && r.url().startsWith('https://redeem.gog.com/'));
+ await page2.click('[type="submit"]'); // click Continue
+ // console.log(await page2.locator('.warning-message').innerText()); // does not exist if there is no warning
+ const r1t = await (await r1).text();
+ const reason = JSON.parse(r1t).reason;
// {"reason":"Invalid or no captcha"}
// {"reason":"code_used"}
// {"reason":"code_not_found"}
- const reason = JSON.parse(rt).reason;
if (reason && reason.includes('captcha')) {
redeem_action = 'redeem (got captcha)';
console.error(' Got captcha; could not redeem!');
@@ -183,7 +184,20 @@ try {
console.error(' Code was not found!');
} else { // TODO not logged in? need valid unused code to test.
redeem_action = 'redeemed?';
- console.log(' Redeemed successfully? Please report your Response from above (if it is new) in https://github.com/vogler/free-games-claimer/issues/5');
+ console.log(' Redeemed successfully? Please report your Responses (if new) in https://github.com/vogler/free-games-claimer/issues/5');
+ console.debug(` Response 1: ${r1t}`);
+ // then after the click on Redeem there is a POST request which should return {} if claimed successfully
+ const r2 = page2.waitForResponse(r => r.request().method() == 'POST' && r.url().startsWith('https://redeem.gog.com/'));
+ await page2.click('[type="submit"]'); // click Redeem
+ const r2t = await (await r2).text();
+ console.debug(` Response 2: ${r2t}`);
+ if (r2t == '{}') {
+ redeem_action = 'redeemed';
+ console.log(' Redeemed successfully.');
+ } else {
+ redeem_action = 'redeemed?';
+ console.log(' Unknown Response 2 - please report in https://github.com/vogler/free-games-claimer/issues/5');
+ }
// db.data[user][title].status = 'claimed and redeemed';
}
} else if (store == 'microsoft games') {
From 819e4cc57e8645588c70ca786f4dc64e90ac606d Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 17 May 2023 11:28:31 +0200
Subject: [PATCH 010/253] pg: `PG_REDEEM` tested legacy games successfully, #5
---
prime-gaming.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index bcea0f0..926bbdd 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -224,7 +224,6 @@ try {
}
}
} else if (store == 'legacy games') {
- console.error(` Redeem on ${store} not yet tested!`);
await page2.fill('[name=coupon_code]', code);
await page2.fill('[name=email]', cfg.pg_email); // TODO option for sep. email?
await page2.fill('[name=email_validate]', cfg.pg_email);
From 8ee63c7a35bb3ed43ab7f59dfb121c89bb7c9368 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 17 May 2023 11:29:45 +0200
Subject: [PATCH 011/253] pg: support `RECORD=1`
---
prime-gaming.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/prime-gaming.js b/prime-gaming.js
index 926bbdd..2e75d0c 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -19,6 +19,8 @@ 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
+ 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/pg-${datetime()}.har` } : undefined, // will record a HAR file with network requests and responses; can be imported in Chrome devtools
});
// TODO test if needed
From 368229be096ed360c4afde153048c61669fc5dd0 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 17 May 2023 11:35:10 +0200
Subject: [PATCH 012/253] pg: screenshot prep only if needed
---
prime-gaming.js | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 2e75d0c..83c1592 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -253,13 +253,16 @@ try {
await page.goto(URL_CLAIM, { waitUntil: 'domcontentloaded' });
await page.click('button[data-type="Game"]');
}
- const p = path.resolve(cfg.dir.screenshots, 'prime-gaming', `${filenamify(datetime())}.png`);
- // await page.screenshot({ path: p, fullPage: true }); // fullPage does not make a difference since scroll not on body but on some element
- await page.keyboard.press('End'); // scroll to bottom to show all games
- await page.waitForTimeout(1000); // wait for fade in animation
- const viewportSize = page.viewportSize(); // current viewport size
- await page.setViewportSize({...viewportSize, height: 3000}); // increase height, otherwise element screenshot is cut off at the top and bottom
- if (notify_games.length) await games.screenshot({ path: p }); // screenshot of all claimed games
+
+ if (notify_games.length) {
+ const p = path.resolve(cfg.dir.screenshots, 'prime-gaming', `${filenamify(datetime())}.png`);
+ // await page.screenshot({ path: p, fullPage: true }); // fullPage does not make a difference since scroll not on body but on some element
+ await page.keyboard.press('End'); // scroll to bottom to show all games
+ await page.waitForTimeout(1000); // wait for fade in animation
+ const viewportSize = page.viewportSize(); // current viewport size
+ await page.setViewportSize({...viewportSize, height: 3000}); // increase height, otherwise element screenshot is cut off at the top and bottom
+ await games.screenshot({ path: p }); // screenshot of all claimed games
+ }
if (cfg.pg_claimdlc) {
console.log('Trying to claim in-game content...');
From 0d074d0397e3349e1f3bfaa5713f559df6976ac5 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 18 May 2023 16:03:24 +0200
Subject: [PATCH 013/253] pg: dlc: scroll to end of page until all are loaded,
#55
---
prime-gaming.js | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 83c1592..3160a96 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -269,7 +269,21 @@ try {
await page.click('button[data-type="InGameLoot"]');
const loot = page.locator('div[data-a-target="offer-list-IN_GAME_LOOT"]');
await loot.waitFor();
- console.log('Number of already claimed DLC:', await loot.locator('p:has-text("Collected")').count());
+
+ process.stdout.write('Loading all DLCs on page...');
+ let n1 = 0;
+ let n2 = 0;
+ do {
+ n1 = n2;
+ n2 = await loot.locator('[data-a-target="item-card"]').count();
+ // console.log(n2);
+ process.stdout.write(` ${n2}`);
+ await page.keyboard.press('End'); // scroll to bottom to show all dlcs
+ await page.waitForLoadState('networkidle'); // did not wait for dlcs to be loaded
+ await page.waitForTimeout(1000);
+ } while (n2 > n1);
+
+ console.log('\nNumber of already claimed DLC:', await loot.locator('p:has-text("Collected")').count());
const cards = await loot.locator('[data-a-target="item-card"]:has(p:text-is("Claim"))').all();
console.log('Number of unclaimed DLC:', cards.length);
@@ -284,6 +298,7 @@ try {
const title = `${dlc.game} - ${dlc.title}`;
const url = dlc.url;
console.log('Current DLC:', title);
+ if (cfg.debug) await page.pause();
if (cfg.dryrun) continue;
db.data[user][title] ||= { title, time: datetime(), store: 'DLC', status: 'failed: need account linking' };
const notify_game = { title, url };
From 7fb872608d6ba015653d1944e9c1086532675786 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 18 May 2023 17:32:02 +0200
Subject: [PATCH 014/253] pg: dlc: try/catch for each dlc, #55
---
prime-gaming.js | 46 ++++++++++++++++++++++++++--------------------
1 file changed, 26 insertions(+), 20 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 3160a96..372d072 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -264,6 +264,7 @@ try {
await games.screenshot({ path: p }); // screenshot of all claimed games
}
+ // https://github.com/vogler/free-games-claimer/issues/55
if (cfg.pg_claimdlc) {
console.log('Trying to claim in-game content...');
await page.click('button[data-type="InGameLoot"]');
@@ -303,27 +304,32 @@ try {
db.data[user][title] ||= { title, time: datetime(), store: 'DLC', status: 'failed: need account linking' };
const notify_game = { title, url };
notify_games.push(notify_game); // status is updated below
- await page.goto(url, { waitUntil: 'domcontentloaded' });
- // most games have a button 'Get in-game content'
- // epic-games: Fall Guys: Claim now -> 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('button:has-text("Get in-game content")'), page.click('button:has-text("Claim your gift")'), page.click('button:has-text("Claim now")').then(() => page.click('button:has-text("Continue")'))]);
- page.click('button:has-text("Continue")').catch(_ => { });
- const linkAccountModal = page.locator('[data-a-target="LinkAccountModal"]');
- const linkAccountButton = linkAccountModal.locator('[data-a-target="LinkAccountButton"]');
- if (await linkAccountButton.count()) {
- console.error(' Missing account linking:', await linkAccountButton.innerText());
- } else if(await page.locator('text=Link game account').count()) { // epic-games only?
- console.error(' Missing account linking:', await page.locator('button[data-a-target="gms-cta"]').innerText());
- } else {
- const code = await page.inputValue('input[type="text"]');
- console.log(' Code to redeem game:', code);
- db.data[user][title].code = code;
- db.data[user][title].status = 'claimed';
- // notify_game.status = `${redeem_action} ${code} on ${store}`;
+ try {
+ await page.goto(url, { waitUntil: 'domcontentloaded' });
+ // most games have a button 'Get in-game content'
+ // epic-games: Fall Guys: Claim now -> 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('button:has-text("Get in-game content")'), page.click('button:has-text("Claim your gift")'), page.click('button:has-text("Claim now")').then(() => page.click('button:has-text("Continue")'))]);
+ page.click('button:has-text("Continue")').catch(_ => { });
+ const linkAccountModal = page.locator('[data-a-target="LinkAccountModal"]');
+ const linkAccountButton = linkAccountModal.locator('[data-a-target="LinkAccountButton"]');
+ if (await linkAccountButton.count()) {
+ console.error(' Missing account linking:', await linkAccountButton.innerText());
+ } else if(await page.locator('text=Link game account').count()) { // epic-games only?
+ console.error(' Missing account linking:', await page.locator('button[data-a-target="gms-cta"]').innerText());
+ } else {
+ const code = await page.inputValue('input[type="text"]');
+ console.log(' Code to redeem game:', code);
+ db.data[user][title].code = code;
+ db.data[user][title].status = 'claimed';
+ // notify_game.status = `${redeem_action} ${code} on ${store}`;
+ }
+ // await page.pause();
+ } catch (error) {
+ console.error(error);
+ } finally {
+ await page.goto(URL_CLAIM, { waitUntil: 'domcontentloaded' });
+ await page.click('button[data-type="InGameLoot"]');
}
- // await page.pause();
- await page.goto(URL_CLAIM, { waitUntil: 'domcontentloaded' });
- await page.click('button[data-type="InGameLoot"]');
}
}
} catch (error) {
From feadfc5acf6dfe1fc05493c8c2eaa1d84e7696d0 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 18 May 2023 18:02:11 +0200
Subject: [PATCH 015/253] pg: redeem: gog: status = 'claimed and redeemed'
---
prime-gaming.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 372d072..a772e3e 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -196,11 +196,11 @@ try {
if (r2t == '{}') {
redeem_action = 'redeemed';
console.log(' Redeemed successfully.');
+ db.data[user][title].status = 'claimed and redeemed';
} else {
redeem_action = 'redeemed?';
console.log(' Unknown Response 2 - please report in https://github.com/vogler/free-games-claimer/issues/5');
}
- // db.data[user][title].status = 'claimed and redeemed';
}
} else if (store == 'microsoft games') {
console.error(` Redeem on ${store} not yet implemented!`);
From 00e213cf5050bc01f5d1989ce0c45feed7ea883d Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Fri, 2 Jun 2023 00:11:10 +0200
Subject: [PATCH 016/253] ue: support RECORD=1
---
unrealengine.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/unrealengine.js b/unrealengine.js
index 25be32e..87651d5 100644
--- a/unrealengine.js
+++ b/unrealengine.js
@@ -24,6 +24,8 @@ 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 for firefox: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:106.0) Gecko/20100101 Firefox/106.0
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/ue-${datetime()}.har` } : undefined, // will record a HAR file with network requests and responses; can be imported in Chrome devtools
});
await stealth(context);
From f906bef2a82ba87b9748e8659c01fd9759360d91 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Fri, 2 Jun 2023 00:14:21 +0200
Subject: [PATCH 017/253] ue: fix 'Sign In'/user detection, #44
---
unrealengine.js | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/unrealengine.js b/unrealengine.js
index 87651d5..af3fa51 100644
--- a/unrealengine.js
+++ b/unrealengine.js
@@ -3,7 +3,7 @@
import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdated playwright-extra
import { authenticator } from 'otplib';
import path from 'path';
-import { existsSync, writeFileSync } from 'fs';
+import { writeFileSync } from 'fs';
import { jsonDb, datetime, stealth, filenamify, prompt, notify, html_game_list, handleSIGINT } from './util.js';
import { cfg } from './config.js';
@@ -43,7 +43,9 @@ try {
await page.goto(URL_CLAIM, { waitUntil: 'domcontentloaded' }); // 'domcontentloaded' faster than default 'load' https://playwright.dev/docs/api/class-page#page-goto
- while (await page.locator('a[role="button"]:has-text("Sign In")').count() > 0) {
+ await page.waitForResponse(r => r.request().method() == 'POST' && r.url().startsWith('https://graphql.unrealengine.com/ue/graphql'));
+
+ while (await page.locator('.display-name').count() == 0) {
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
@@ -79,11 +81,11 @@ try {
process.exit(1);
}
}
- await page.waitForURL(URL_CLAIM);
+ await page.waitForURL('**unrealengine.com/marketplace/**');
if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
}
await page.waitForTimeout(1000);
- user = await page.locator('.user-label').first().innerHTML();
+ user = await page.locator('.display-name').first().innerHTML();
console.log(`Signed in as ${user}`);
db.data[user] ||= {};
@@ -110,10 +112,10 @@ try {
}
if (await p.locator('.btn .in-cart').count()) {
console.log(' ↳ Already in cart');
- continue;
+ } else {
+ await p.locator('.btn .add').click();
+ console.log(' ↳ Added to cart');
}
- await p.locator('.btn .add').click();
- console.log(' ↳ Added to cart');
ids.push(id);
}
if (!ids.length) {
From 2ceccdae6baa8fa990421fcbd5976d3b0b6640bc Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Fri, 2 Jun 2023 00:30:31 +0200
Subject: [PATCH 018/253] ue: add as experimental to README.md, #44
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index c10d726..047d0ce 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,7 @@ Claims free games periodically on
-
[Amazon Prime Gaming](https://gaming.amazon.com)
-
[GOG](https://www.gog.com)
-
[Xbox Live Games with Gold](https://www.xbox.com/en-US/live/gold#gameswithgold) - planned
+-
[Unreal Engine (Assets)](https://www.unrealengine.com/marketplace/en-US/assets?count=20&sortBy=effectiveDate&sortDir=DESC&start=0&tag=4910) ([experimental](https://github.com/vogler/free-games-claimer/issues/44), same login as Epic Games)
Pull requests welcome :)
From d3d22b1582a1ec9f6df32f0438d3828eab03bebb Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Fri, 2 Jun 2023 00:47:48 +0200
Subject: [PATCH 019/253] ue: wait 2s before checking cart, #44
---
unrealengine.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/unrealengine.js b/unrealengine.js
index af3fa51..871c82e 100644
--- a/unrealengine.js
+++ b/unrealengine.js
@@ -121,6 +121,7 @@ try {
if (!ids.length) {
console.log('Nothing to claim');
} else {
+ await page.waitForTimeout(2000);
const price = (await page.locator('.shopping-cart .total .price').innerText()).split(' ');
console.log('Price: ', price[1], 'instead of', price[0]);
if (price[1] != '0') {
From 7e4770b846135f7d0c36ac101914d35506f79474 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Fri, 2 Jun 2023 00:52:11 +0200
Subject: [PATCH 020/253] ue: notify on 'Price is not 0!' + ask to report, #44
---
unrealengine.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/unrealengine.js b/unrealengine.js
index 871c82e..2993fea 100644
--- a/unrealengine.js
+++ b/unrealengine.js
@@ -125,7 +125,9 @@ try {
const price = (await page.locator('.shopping-cart .total .price').innerText()).split(' ');
console.log('Price: ', price[1], 'instead of', price[0]);
if (price[1] != '0') {
- console.error('Price is not 0! Exit!');
+ const err = 'Price is not 0! Exit! Please report.'
+ console.error(err);
+ notify('unrealengine: ' + err);
process.exit(1);
}
// await page.pause();
From 0b9d5d0b6370552c4f065c7da0fd393f20690146 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 22 Jun 2023 17:16:35 +0200
Subject: [PATCH 021/253] pg: comments
---
prime-gaming.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index a772e3e..c731efa 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -153,7 +153,7 @@ try {
const code = await page.inputValue('input[type="text"]');
console.log(' Code to redeem game:', code);
if (store == 'legacy games') { // may be different URL like https://legacygames.com/primeday/puzzleoftheyear/
- redeem[store] = await (await page.$('li:has-text("Click here") a')).getAttribute('href');
+ redeem[store] = await (await page.$('li:has-text("Click here") a')).getAttribute('href'); // full text: Click here to enter your redemption code.
}
console.log(' URL to redeem game:', redeem[store]);
db.data[user][title].code = code;
@@ -254,7 +254,7 @@ try {
await page.click('button[data-type="Game"]');
}
- if (notify_games.length) {
+ if (notify_games.length) { // make screenshot of all games if something was claimed
const p = path.resolve(cfg.dir.screenshots, 'prime-gaming', `${filenamify(datetime())}.png`);
// await page.screenshot({ path: p, fullPage: true }); // fullPage does not make a difference since scroll not on body but on some element
await page.keyboard.press('End'); // scroll to bottom to show all games
From 85513031f2d03478a9ed833cc4e9fa9a8fd24ed4 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Fri, 23 Jun 2023 01:21:42 +0200
Subject: [PATCH 022/253] =?UTF-8?q?pg:=20external:=20fix=20lost=20elementH?=
=?UTF-8?q?andle=C2=A0error=20due=20to=20navigation?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
elementHandle.$: Protocol error (Page.adoptNode)
---
prime-gaming.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index c731efa..c99eec0 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -102,7 +102,7 @@ try {
console.log('Number of already claimed games (total):', await games.locator('p:has-text("Collected")').count());
// can't use .all() since the list of elements via locator will change after click while we iterate over it
const internal = await games.locator('[data-a-target="claim-prime-offer-card"]:has-text("Claim")').elementHandles();
- const external = await games.locator('[data-a-target="learn-more-card"]:has(p:text-is("Claim"))').elementHandles();
+ const external = games.locator('[data-a-target="learn-more-card"]:has(p:text-is("Claim"))'); // using .elementHandles() here would lead to error due to page navigation: elementHandle.$: Protocol error (Page.adoptNode)
console.log('Number of free unclaimed games (Prime Gaming):', internal.length);
// claim games in internal store
for (const card of internal) {
@@ -118,9 +118,9 @@ try {
const p = path.resolve(cfg.dir.screenshots, 'prime-gaming', 'internal', `${filenamify(title)}.png`);
await card.screenshot({ path: p });
}
- console.log('Number of free unclaimed games (external stores):', external.length);
+ console.log('Number of free unclaimed games (external stores):', await external.count());
// claim games in external/linked stores. Linked: origin.com, epicgames.com; Redeem-key: gog.com, legacygames.com, microsoft
- for (const card of external) {
+ for (const card of await external.elementHandles()) { // TODO refactor (result of external locator changes with each iteration)
// if (!card) continue;
const title = await (await card.$('.item-card-details__body__primary')).innerText();
console.log('Current free game:', title);
From f0e15b5c7cc24343192b14e9e344b26166cc2304 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Fri, 23 Jun 2023 01:24:06 +0200
Subject: [PATCH 023/253] pg: fix #158 - page for gog (and ?) changed (button,
store, code)
The claim page for Legacy Games is still unchanged and works with the
old code.
---
prime-gaming.js | 28 ++++++++++++++++++++++------
1 file changed, 22 insertions(+), 6 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index c99eec0..7108e58 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -127,12 +127,28 @@ try {
if (cfg.debug) await page.pause();
if (cfg.dryrun) continue;
await (await card.$('text=Claim')).click(); // goes to URL of game, no need to wait
- await Promise.any([page.click('button:has-text("Claim now")'), page.click('button:has-text("Complete Claim")'), page.waitForSelector('div:has-text("Link game account")')]); // waits for navigation
- const store_text = await (await page.$('[data-a-target="hero-header-subtitle"]')).innerText();
- // Full game for PC [and MAC] on: gog.com, Origin, Legacy Games, EPIC GAMES, Battle.net
- // 3 Full PC Games on Legacy Games
- const store = store_text.toLowerCase().replace(/.* on /, '');
+ await Promise.any([page.click('button:has-text("Get game")'), page.click('button:has-text("Claim now")'), page.click('button:has-text("Complete Claim")'), page.waitForSelector('div:has-text("Link game account")')]); // waits for navigation
+
+ // TODO would be simpler than the below, but will block for linked stores without code
+ // const redeem_text = await page.textContent('text=/ code on /'); // FAQ: How do I redeem my code?
+ // console.log(' ', redeem_text);
+ // // Before July 29, 2023, redeem your offer code on GOG.com.
+ // // Before July 1, 2023, redeem your product code on Legacy Games.
+ // let store = redeem_text.toLowerCase().replace(/.* on /, '').slice(0, -1);
+
+ let store = '';
+ const store_text = await page.$('[data-a-target="hero-header-subtitle"]'); // worked fine for every store, but now no longer works for gog.com
+ if (store_text) { // legacy games, ?
+ const store_texts = await store_text.innerText();
+ // Full game for PC [and MAC] on: Legacy Games, Origin, EPIC GAMES, Battle.net; alt: 3 Full PC Games on Legacy Games
+ store = store_texts.toLowerCase().replace(/.* on /, '');
+ } else { // gog.com, ?
+ // $('[data-a-target="DescriptionItemDetails"]').innerText is e.g. 'Prey for PC on GOG.com.' but does not work for Legacy Games
+ const item_text = await page.innerText('[data-a-target="DescriptionItemDetails"]');
+ store = item_text.toLowerCase().replace(/.* on /, '').slice(0, -1);
+ }
console.log(' External store:', store);
+
const url = page.url().split('?')[0];
db.data[user][title] ||= { title, time: datetime(), url, store };
const notify_game = { title, url };
@@ -150,7 +166,7 @@ try {
'legacy games': 'https://www.legacygames.com/primedeal',
};
if (store in redeem) { // did not work for linked origin: && !await page.locator('div:has-text("Successfully Claimed")').count()
- const code = await page.inputValue('input[type="text"]');
+ const code = await Promise.any([page.inputValue('input[type="text"]'), page.textContent('[data-a-target="ClaimStateClaimCodeContent"]').then(s => s.replace('Your code: ', ''))]); // input: Legacy Games; text: gog.com
console.log(' Code to redeem game:', code);
if (store == 'legacy games') { // may be different URL like https://legacygames.com/primeday/puzzleoftheyear/
redeem[store] = await (await page.$('li:has-text("Click here") a')).getAttribute('href'); // full text: Click here to enter your redemption code.
From acc337a4f99606dd3a786dc2cb33eb27623bef95 Mon Sep 17 00:00:00 2001
From: Omair
Date: Wed, 24 May 2023 20:12:00 +0100
Subject: [PATCH 024/253] xbox: add config values
---
config.js | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/config.js b/config.js
index 32974e1..b420a5a 100644
--- a/config.js
+++ b/config.js
@@ -37,6 +37,10 @@ export const cfg = {
gog_password: process.env.GOG_PASSWORD || process.env.PASSWORD,
gog_newsletter: process.env.GOG_NEWSLETTER == '1', // do not unsubscribe from newsletter after claiming a game
// OTP only via GOG_EMAIL, can't add app...
+ // auth xbox
+ xbox_email: process.env.XBOX_EMAIL || process.env.EMAIL,
+ xbox_password: process.env.XBOX_PASSWORD || process.env.PASSWORD,
+ xbox_otpkey: process.env.XBOX_OTPKEY, // TODO unimplemented
// experimmental - likely to change
pg_redeem: process.env.PG_REDEEM == '1', // prime-gaming: redeem keys on external stores
From a7dcfe72ccea6434b4f1b6b04ddbb61a96c011fb Mon Sep 17 00:00:00 2001
From: Omair
Date: Wed, 24 May 2023 20:12:33 +0100
Subject: [PATCH 025/253] xbox: add implementation for xbox games with gold
---
config.js | 3 +-
xbox.js | 256 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 257 insertions(+), 2 deletions(-)
create mode 100644 xbox.js
diff --git a/config.js b/config.js
index b420a5a..b1e31e6 100644
--- a/config.js
+++ b/config.js
@@ -40,8 +40,7 @@ export const cfg = {
// auth xbox
xbox_email: process.env.XBOX_EMAIL || process.env.EMAIL,
xbox_password: process.env.XBOX_PASSWORD || process.env.PASSWORD,
- xbox_otpkey: process.env.XBOX_OTPKEY, // TODO unimplemented
-
+ xbox_otpkey: process.env.XBOX_OTPKEY,
// experimmental - likely to change
pg_redeem: process.env.PG_REDEEM == '1', // prime-gaming: redeem keys on external stores
pg_claimdlc: process.env.PG_CLAIMDLC == '1', // prime-gaming: claim in-game content
diff --git a/xbox.js b/xbox.js
new file mode 100644
index 0000000..501f4f1
--- /dev/null
+++ b/xbox.js
@@ -0,0 +1,256 @@
+import { firefox } from "playwright-firefox"; // stealth plugin needs no outdated playwright-extra
+import { authenticator } from "otplib";
+import {
+ datetime,
+ handleSIGINT,
+ html_game_list,
+ jsonDb,
+ notify,
+ prompt,
+} from "./util.js";
+import path from "path";
+import { existsSync, writeFileSync } from "fs";
+import { cfg } from "./config.js";
+
+// ### SETUP
+const URL_CLAIM = "https://www.xbox.com/en-US/live/gold"; // #gameswithgold";
+
+console.log(datetime(), "started checking xbox");
+
+const db = await jsonDb("xbox.json");
+db.data ||= {};
+
+handleSIGINT();
+
+// https://playwright.dev/docs/auth#multi-factor-authentication
+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
+});
+
+if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
+
+const page = context.pages().length
+ ? context.pages()[0]
+ : await context.newPage(); // should always exist
+
+const notify_games = [];
+let user;
+
+main();
+
+async function main() {
+ try {
+ await performLogin();
+ await getAndSaveUser();
+ await redeemFreeGames();
+ } catch (error) {
+ console.error(error);
+ process.exitCode ||= 1;
+ if (error.message && process.exitCode != 130)
+ notify(`xbox 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(`xbox (${user}):
${html_game_list(notify_games)}`);
+ }
+ await context.close();
+ }
+}
+
+async function performLogin() {
+ await page.goto(URL_CLAIM, { waitUntil: "domcontentloaded" }); // default 'load' takes forever
+
+ const signInLocator = page
+ .getByRole("link", {
+ name: "Sign in to your account",
+ })
+ .first();
+ const usernameLocator = page
+ .getByRole("button", {
+ name: "Account manager for",
+ })
+ .first();
+
+ await Promise.any([signInLocator.waitFor(), usernameLocator.waitFor()]);
+
+ if (await usernameLocator.isVisible()) {
+ return; // logged in using saved cookie
+ } else if (await signInLocator.isVisible()) {
+ console.error("Not signed in anymore.");
+ await signInLocator.click();
+ await signInToXbox();
+ } else {
+ console.error("lost! where am i?");
+ }
+}
+
+async function signInToXbox() {
+ page.waitForLoadState("domcontentloaded");
+ 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!`);
+
+ // ### FETCH EMAIL/PASS
+ if (cfg.xbox_email && cfg.xbox_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.xbox_email || (await prompt({ message: "Enter email" }));
+ const password =
+ email &&
+ (cfg.xbox_password ||
+ (await prompt({
+ type: "password",
+ message: "Enter password",
+ })));
+ // ### FILL IN EMAIL/PASS
+ if (email && password) {
+ const usernameLocator = page
+ .getByPlaceholder("Email, phone, or Skype")
+ .first();
+ const passwordLocator = page.getByPlaceholder("Password").first();
+
+ await Promise.any([
+ usernameLocator.waitFor(),
+ passwordLocator.waitFor(),
+ ]);
+
+ // username may already be saved from before, if so, skip to filling in password
+ if (await page.getByPlaceholder("Email, phone, or Skype").isVisible()) {
+ await usernameLocator.fill(email);
+ await page.getByRole("button", { name: "Next" }).click();
+ }
+
+ await passwordLocator.fill(password);
+ await page.getByRole("button", { name: "Sign in" }).click();
+
+ // handle MFA, but don't await it
+ page.locator('input[name="otc"]')
+ .waitFor()
+ .then(async () => {
+ console.log("Two-Step Verification - Enter security code");
+ console.log(
+ await page
+ .locator('div[data-bind="text: description"]')
+ .innerText()
+ );
+ const otp =
+ (cfg.xbox_otpkey &&
+ authenticator.generate(cfg.xbox_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.type('input[name="otc"]', otp.toString());
+ await page
+ .getByLabel("Don't ask me again on this device")
+ .check(); // Trust this Browser
+ await page.getByRole("button", { name: "Verify" }).click();
+ })
+ .catch((_) => {});
+
+ // Trust this browser, but don't await it
+ page.getByLabel("Don't show this again")
+ .waitFor()
+ .then(async () => {
+ await page.getByLabel("Don't show this again").check();
+ await page.getByRole("button", { name: "Yes" }).click();
+ })
+ .catch((_) => {});
+ } else {
+ console.log("Waiting for you to login in the browser.");
+ await notify(
+ "xbox: no longer signed in and not enough options set for automatic login."
+ );
+ if (cfg.headless) {
+ console.log(
+ "Run `SHOW=1 node xbox` to login in the opened browser."
+ );
+ await context.close();
+ process.exit(1);
+ }
+ }
+
+ // ### VERIFY SIGNED IN
+ await page.waitForURL(`${URL_CLAIM}**`);
+
+ if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
+}
+
+async function getAndSaveUser() {
+ user = await page.locator("#mectrl_currentAccount_primary").innerHTML();
+ console.log(`Signed in as '${user}'`);
+ db.data[user] ||= {};
+}
+
+async function redeemFreeGames() {
+ const monthlyGamesLocator = await page.locator(".f-size-large").all();
+
+ const monthlyGamesPageLinks = await Promise.all(
+ monthlyGamesLocator.map(
+ async (el) => await el.locator("a").getAttribute("href")
+ )
+ );
+ console.log("Free games:", monthlyGamesPageLinks);
+
+ for (const url of monthlyGamesPageLinks) {
+ await page.goto(url);
+
+ const title = await page.locator("h1").first().innerText();
+ 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);
+ const notify_game = { title, url, status: "failed" };
+ notify_games.push(notify_game); // status is updated below
+
+ // SELECTORS
+ const getBtnLocator = page.getByText("GET", { exact: true }).first();
+ const installToLocator = page
+ .getByText("INSTALL TO", { exact: true })
+ .first();
+
+ await Promise.any([
+ getBtnLocator.waitFor(),
+ installToLocator.waitFor(),
+ ]);
+
+ if (await installToLocator.isVisible()) {
+ console.log(" Already in library! Nothing to claim.");
+ notify_game.status = "existed";
+ db.data[user][game_id].status ||= "existed"; // does not overwrite claimed or failed
+ } else if (await getBtnLocator.isVisible()) {
+ console.log(" Not in library yet! Click GET.");
+ await getBtnLocator.click();
+
+ // wait for popup
+ await page
+ .locator('iframe[name="purchase-sdk-hosted-iframe"]')
+ .waitFor();
+ const popupLocator = page.frameLocator(
+ "[name=purchase-sdk-hosted-iframe]"
+ );
+
+ const finalGetBtnLocator = popupLocator.getByText("GET");
+ await finalGetBtnLocator.waitFor();
+ await finalGetBtnLocator.click();
+
+ await page.getByText("Thank you for your purchase.").waitFor();
+ notify_game.status = "claimed";
+ db.data[user][game_id].status = "claimed";
+ db.data[user][game_id].time = datetime(); // claimed time overwrites failed/dryrun time
+ console.log(" Claimed successfully!");
+ }
+
+ // notify_game.status = db.data[user][game_id].status; // claimed or failed
+
+ // const p = path.resolve(cfg.dir.screenshots, playstation-plus', `${game_id}.png`);
+ // if (!existsSync(p)) await page.screenshot({ path: p, fullPage: false }); // fullPage is quite long...
+ }
+}
From 8c6ff57054894e99a8f671c271a847bd8ac3394b Mon Sep 17 00:00:00 2001
From: Omair
Date: Mon, 26 Jun 2023 15:46:41 -0400
Subject: [PATCH 026/253] xbox: update readme and dockerfile with xbox
info/scripts
---
Dockerfile | 2 +-
README.md | 15 +++++++++++----
2 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index ebad9eb..5d8ee90 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -77,4 +77,4 @@ ENV SHOW 1
# Script to setup display server & VNC is always executed.
ENTRYPOINT ["docker-entrypoint.sh"]
# Default command to run. This is replaced by appending own command, e.g. `docker run ... node prime-gaming` to only run this script.
-CMD node epic-games; node prime-gaming; node gog
+CMD node epic-games; node prime-gaming; node gog; node xbox;
diff --git a/README.md b/README.md
index 047d0ce..3938546 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ Claims free games periodically on
-
[Epic Games Store](https://www.epicgames.com/store/free-games)
-
[Amazon Prime Gaming](https://gaming.amazon.com)
-
[GOG](https://www.gog.com)
--
[Xbox Live Games with Gold](https://www.xbox.com/en-US/live/gold#gameswithgold) - planned
+-
[Xbox Live Games with Gold](https://www.xbox.com/en-US/live/gold#gameswithgold) ([experimental](https://github.com/vogler/free-games-claimer/issues/19))
-
[Unreal Engine (Assets)](https://www.unrealengine.com/marketplace/en-US/assets?count=20&sortBy=effectiveDate&sortDir=DESC&start=0&tag=4910) ([experimental](https://github.com/vogler/free-games-claimer/issues/44), same login as Epic Games)
Pull requests welcome :)
@@ -24,7 +24,7 @@ Easy option: [install Docker](https://docs.docker.com/get-docker/) (or [podman](
```
docker run --rm -it -p 6080:6080 -v fgc:/fgc/data --pull=always ghcr.io/vogler/free-games-claimer
```
-This will run `node epic-games; node prime-gaming; node gog` - if you only want to claim games for one of the stores, you can override the default command by appending e.g. `node epic-games` at the end of the `docker run` command, or if you want several `bash -c "node epic-games.js; node gog.js"`.
+This will run `node epic-games; node prime-gaming; node gog; node xbox;` - if you only want to claim games for one of the stores, you can override the default command by appending e.g. `node epic-games` at the end of the `docker run` command, or if you want several `bash -c "node epic-games.js; node gog.js"`.
Data (including json files with claimed games, codes to redeem, screenshots) is stored in the Docker volume `fgc`.
@@ -86,6 +86,9 @@ Available options/variables and their default values:
| GOG_EMAIL | | GOG email for login. Overrides EMAIL. |
| GOG_PASSWORD | | GOG password for login. Overrides PASSWORD. |
| GOG_NEWSLETTER | 0 | Do not unsubscribe from newsletter after claiming a game if 1. |
+| XBOX_EMAIL | | Xbox email for login. Overrides EMAIL. |
+| XBOX_PASSWORD | | Xbox password for login. Overrides PASSWORD. |
+| XBOX_OTPKEY | | Xbox MFA OTP key. |
See `config.js` for all options.
@@ -113,6 +116,7 @@ To get the OTP key, it is easiest to follow the store's guide for adding an auth
- **Epic Games**: visit [password & security](https://www.epicgames.com/account/password), enable 'third-party authenticator app', copy the 'Manual Entry Key' and use it to set `EG_OTPKEY`.
- **Prime Gaming**: visit Amazon 'Your Account › Login & security', 2-step verification › Manage › Add new app › Can't scan the barcode, copy the bold key and use it to set `PG_OTPKEY`
- **GOG**: only offers OTP via email
+- **Xbox**: visit [additional security](https://account.live.com/proofs/manage/additional) > Add a new way to sign in or verify > Use an app > Set up a different Authenticator app > I can't scan the bar code > copy the bold key and use it to set `XBOX_OTPKEY`
Beware that storing passwords and OTP keys as clear text may be a security risk. Use a unique/generated password! TODO: maybe at least offer to base64 encode for storage.
@@ -130,13 +134,16 @@ Claiming the Amazon Games works out-of-the-box, however, for games on external s
Keys and URLs are printed to the console, included in notifications and saved in `data/prime-gaming.json`. A screenshot of the page with the key is also saved to `data/screenshots`.
[TODO](https://github.com/vogler/free-games-claimer/issues/5): ~~redeem keys on external stores.~~
+### Xbox Games With Gold
+Run `node xbox` (locally or in docker).
+
### Run periodically
#### How often?
Epic Games usually has two free games *every week*, before Christmas every day.
Prime Gaming has new games *every month* or more often during Prime days.
-GOG usually has one new game every couples of weeks.
+GOG usually has one new game every couples of weeks. Xbox usually has two games *every month*.
-It is save to run the scripts every day.
+It is safe to run the scripts every day.
#### How to schedule?
The container/scripts will claim currently available games and then exit.
From 04bd56cd77e1d87b99e5dfb79ea684cfc24f201c Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Fri, 23 Jun 2023 01:32:39 +0200
Subject: [PATCH 027/253] pg: legacy games: status = claimed and redeemed
---
prime-gaming.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/prime-gaming.js b/prime-gaming.js
index 7108e58..878ba50 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -249,6 +249,7 @@ try {
await page2.click('[type="submit"]');
redeem_action = 'redeemed?';
console.log(' Redeemed successfully? Please report problems in https://github.com/vogler/free-games-claimer/issues/5');
+ db.data[user][title].status = 'claimed and redeemed';
} else {
console.error(` Redeem on ${store} not yet implemented!`);
}
From 2e8a731573f1f6c2b222cc1b21dcd3f1c3954346 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 13 Jul 2023 10:56:12 +0200
Subject: [PATCH 028/253] pg: update selectors for internal/external games,
fixes #164
---
prime-gaming.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 878ba50..695951e 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -97,12 +97,13 @@ try {
await page.click('button[data-type="Game"]');
await page.keyboard.press('End'); // scroll to bottom to show all games
await page.waitForLoadState('networkidle'); // wait for all games to be loaded
+ await page.waitForTimeout(2000); // TODO networkidle wasn't enough to load all already collected games
const games = page.locator('div[data-a-target="offer-list-FGWP_FULL"]');
await games.waitFor();
console.log('Number of already claimed games (total):', await games.locator('p:has-text("Collected")').count());
// can't use .all() since the list of elements via locator will change after click while we iterate over it
- const internal = await games.locator('[data-a-target="claim-prime-offer-card"]:has-text("Claim")').elementHandles();
- const external = games.locator('[data-a-target="learn-more-card"]:has(p:text-is("Claim"))'); // using .elementHandles() here would lead to error due to page navigation: elementHandle.$: Protocol error (Page.adoptNode)
+ const internal = await games.locator('.item-card__action:has([data-a-target="FGWPOffer"])').elementHandles();
+ const external = games.locator('.item-card__action:has([data-a-target="ExternalOfferClaim"])'); // using .elementHandles() here would lead to error due to page navigation: elementHandle.$: Protocol error (Page.adoptNode)
console.log('Number of free unclaimed games (Prime Gaming):', internal.length);
// claim games in internal store
for (const card of internal) {
From a90f90afc098b3043ee1ef90e57164b748d3b6dc Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 13 Jul 2023 11:28:18 +0200
Subject: [PATCH 029/253] pg: PG_REDEEM: only pause if DEBUG=1
Fixes https://github.com/vogler/free-games-claimer/issues/5#issuecomment-1527063819
---
prime-gaming.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 695951e..462fe49 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -254,7 +254,7 @@ try {
} else {
console.error(` Redeem on ${store} not yet implemented!`);
}
- await page2.pause();
+ if (cfg.debug) await page.pause();
await page2.close();
}
notify_game.status = `${redeem_action} ${code} on ${store}`;
From 22f673282b12d3f047c3c25e4d8650e299d9ae74 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 13 Jul 2023 11:30:48 +0200
Subject: [PATCH 030/253] pg: fix a90f90afc098b3043ee1ef90e57164b748d3b6dc
---
prime-gaming.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 462fe49..d9fcd7e 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -254,7 +254,7 @@ try {
} else {
console.error(` Redeem on ${store} not yet implemented!`);
}
- if (cfg.debug) await page.pause();
+ if (cfg.debug) await page2.pause();
await page2.close();
}
notify_game.status = `${redeem_action} ${code} on ${store}`;
From b5ead8ea21129830596f7058775c97e9832813e9 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 20 Jul 2023 16:10:50 +0200
Subject: [PATCH 031/253] SCREENSHOTS_DIR=0 to disable screenshots, fixes #172
---
config.js | 2 +-
epic-games.js | 8 +++++---
gog.js | 8 ++++----
prime-gaming.js | 13 ++++++-------
unrealengine.js | 10 +++++-----
util.js | 3 +++
6 files changed, 24 insertions(+), 20 deletions(-)
diff --git a/config.js b/config.js
index 32974e1..d863d8d 100644
--- a/config.js
+++ b/config.js
@@ -20,7 +20,7 @@ export const cfg = {
get dir() { // avoids ReferenceError: Cannot access 'dataDir' before initialization
return {
browser: process.env.BROWSER_DIR || dataDir('browser'), // for multiple accounts or testing
- screenshots: process.env.SCREENSHOTS_DIR || dataDir('screenshots'), // if not wanted: /dev/null
+ screenshots: process.env.SCREENSHOTS_DIR || dataDir('screenshots'), // set to 0 to disable screenshots
}
},
// auth epic-games
diff --git a/epic-games.js b/epic-games.js
index 37310ea..314b349 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -2,9 +2,11 @@ import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdate
import { authenticator } from 'otplib';
import path from 'path';
import { existsSync, writeFileSync } from 'fs';
-import { jsonDb, datetime, stealth, filenamify, prompt, notify, html_game_list, handleSIGINT } from './util.js';
+import { resolve, jsonDb, datetime, stealth, filenamify, prompt, notify, html_game_list, handleSIGINT } from './util.js';
import { cfg } from './config.js';
+const screenshot = (...a) => resolve(cfg.dir.screenshots, 'epic-games', ...a);
+
const URL_CLAIM = 'https://store.epicgames.com/en-US/free-games';
const URL_LOGIN = 'https://www.epicgames.com/id/login?lang=en-US&noHostRedirect=true&redirectUrl=' + URL_CLAIM;
@@ -218,13 +220,13 @@ try {
console.log(e);
// console.error(' Failed to claim! Try again if NopeCHA timed out. Click the extension to see if you ran out of credits (refill after 24h). To avoid captchas try to get a new IP or set a cookie from https://www.hcaptcha.com/accessibility');
console.error(' Failed to claim! To avoid captchas try to get a new IP address.');
- const p = path.resolve(cfg.dir.screenshots, 'epic-games', 'failed', `${game_id}_${filenamify(datetime())}.png`);
+ const p = screenshot('failed', `${game_id}_${filenamify(datetime())}.png`);
await page.screenshot({ path: p, fullPage: true });
db.data[user][game_id].status = 'failed';
}
notify_game.status = db.data[user][game_id].status; // claimed or failed
- const p = path.resolve(cfg.dir.screenshots, 'epic-games', `${game_id}.png`);
+ const p = screenshot(`${game_id}.png`);
if (!existsSync(p)) await page.screenshot({ path: p, fullPage: false }); // fullPage is quite long...
}
}
diff --git a/gog.js b/gog.js
index 1785663..cde7acb 100644
--- a/gog.js
+++ b/gog.js
@@ -1,8 +1,9 @@
import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdated playwright-extra
-import path from 'path';
-import { jsonDb, datetime, filenamify, prompt, notify, html_game_list, handleSIGINT } from './util.js';
+import { resolve, jsonDb, datetime, filenamify, prompt, notify, html_game_list, handleSIGINT } from './util.js';
import { cfg } from './config.js';
+const screenshot = (...a) => resolve(cfg.dir.screenshots, 'gog', ...a);
+
const URL_CLAIM = 'https://www.gog.com/en';
console.log(datetime(), 'started checking gog');
@@ -99,8 +100,7 @@ try {
db.data[user][title] ||= { title, time: datetime(), url };
if (cfg.dryrun) process.exit(1);
await page.locator('#giveaway:not(.is-loading)').waitFor(); // otherwise screenshot is sometimes with loading indicator instead of game title
- const p = path.resolve(cfg.dir.screenshots, 'gog', `${filenamify(title)}.png`);
- await banner.screenshot({ path: p }); // overwrites every time - only keep first?
+ await banner.screenshot({ path: screenshot(`${filenamify(title)}.png`) }); // overwrites every time - only keep first?
// await banner.getByRole('button', { name: 'Add to library' }).click();
// instead of clicking the button, we visit the auto-claim URL which gives as a JSON response which is easier than checking the state of a button
diff --git a/prime-gaming.js b/prime-gaming.js
index d9fcd7e..1af1eb8 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -1,9 +1,10 @@
import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdated playwright-extra
import { authenticator } from 'otplib';
-import path from 'path';
-import { jsonDb, datetime, stealth, filenamify, prompt, notify, html_game_list, handleSIGINT } from './util.js';
+import { resolve, jsonDb, datetime, stealth, filenamify, prompt, notify, html_game_list, handleSIGINT } from './util.js';
import { cfg } from './config.js';
+const screenshot = (...a) => resolve(cfg.dir.screenshots, 'prime-gaming', ...a);
+
// 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';
@@ -116,8 +117,7 @@ try {
notify_games.push({ title, status: 'claimed', url: URL_CLAIM });
// const img = await (await card.$('img.tw-image')).getAttribute('src');
// console.log('Image:', img);
- const p = path.resolve(cfg.dir.screenshots, 'prime-gaming', 'internal', `${filenamify(title)}.png`);
- await card.screenshot({ path: p });
+ await card.screenshot({ path: screenshot('internal', `${filenamify(title)}.png`) });
}
console.log('Number of free unclaimed games (external stores):', await external.count());
// claim games in external/linked stores. Linked: origin.com, epicgames.com; Redeem-key: gog.com, legacygames.com, microsoft
@@ -263,8 +263,7 @@ try {
db.data[user][title].status = 'claimed';
}
// save screenshot of potential code just in case
- const p = path.resolve(cfg.dir.screenshots, 'prime-gaming', 'external', `${filenamify(title)}.png`);
- await page.screenshot({ path: p, fullPage: true });
+ await page.screenshot({ path: screenshot('external', `${filenamify(title)}.png`), fullPage: true });
// console.info(' Saved a screenshot of page to', p);
}
// await page.pause();
@@ -273,7 +272,7 @@ try {
}
if (notify_games.length) { // make screenshot of all games if something was claimed
- const p = path.resolve(cfg.dir.screenshots, 'prime-gaming', `${filenamify(datetime())}.png`);
+ const p = screenshot(`${filenamify(datetime())}.png`);
// await page.screenshot({ path: p, fullPage: true }); // fullPage does not make a difference since scroll not on body but on some element
await page.keyboard.press('End'); // scroll to bottom to show all games
await page.waitForTimeout(1000); // wait for fade in animation
diff --git a/unrealengine.js b/unrealengine.js
index 2993fea..830496c 100644
--- a/unrealengine.js
+++ b/unrealengine.js
@@ -4,9 +4,11 @@ import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdate
import { authenticator } from 'otplib';
import path from 'path';
import { writeFileSync } from 'fs';
-import { jsonDb, datetime, stealth, filenamify, prompt, notify, html_game_list, handleSIGINT } from './util.js';
+import { resolve, jsonDb, datetime, stealth, filenamify, prompt, notify, html_game_list, handleSIGINT } from './util.js';
import { cfg } from './config.js';
+const screenshot = (...a) => resolve(cfg.dir.screenshots, 'unrealengine', ...a);
+
const URL_CLAIM = 'https://www.unrealengine.com/marketplace/en-US/assets?count=20&sortBy=effectiveDate&sortDir=DESC&start=0&tag=4910';
const URL_LOGIN = 'https://www.epicgames.com/id/login?lang=en-US&noHostRedirect=true&redirectUrl=' + URL_CLAIM;
@@ -176,15 +178,13 @@ try {
console.log(e);
// console.error(' Failed to claim! Try again if NopeCHA timed out. Click the extension to see if you ran out of credits (refill after 24h). To avoid captchas try to get a new IP or set a cookie from https://www.hcaptcha.com/accessibility');
console.error(' Failed to claim! To avoid captchas try to get a new IP address.');
- const p = path.resolve(cfg.dir.screenshots, 'unrealengine', 'failed', `${filenamify(datetime())}.png`);
- await page.screenshot({ path: p, fullPage: true });
+ await page.screenshot({ path: screenshot('failed', `${filenamify(datetime())}.png`), fullPage: true });
// db.data[user][id].status = 'failed';
notify_games.forEach(g => g.status = 'failed');
}
// notify_game.status = db.data[user][game_id].status; // claimed or failed
- const p = path.resolve(cfg.dir.screenshots, 'unrealengine', `${filenamify(datetime())}.png`);
- if (notify_games.length) await page.screenshot({ path: p, fullPage: false }); // fullPage is quite long...
+ if (notify_games.length) await page.screenshot({ path: screenshot(`${filenamify(datetime())}.png`), fullPage: false }); // fullPage is quite long...
console.log('Done');
}
} catch (error) {
diff --git a/util.js b/util.js
index 2725745..e581684 100644
--- a/util.js
+++ b/util.js
@@ -7,6 +7,9 @@ const __dirname = path.dirname(__filename);
// explicit object instead of Object.fromEntries since the built-in type would loose the keys, better type: https://dev.to/svehla/typescript-object-fromentries-389c
export const dataDir = s => path.resolve(__dirname, 'data', s);
+// modified path.resolve to return null if first argument is '0', used to disable screenshots
+export const resolve = (...a) => a.length && a[0] == '0' ? null : path.resolve(...a);
+
// json database
import { Low } from 'lowdb';
import { JSONFile } from 'lowdb/node';
From f89b4a6a3659bfabdfb1987585ae51ef7b7d05af Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 10 Aug 2023 14:37:00 +0200
Subject: [PATCH 032/253] ue: mention new assets to claim every first Tuesday
of a month
---
README.md | 1 +
unrealengine.js | 1 +
2 files changed, 2 insertions(+)
diff --git a/README.md b/README.md
index 047d0ce..538ea4f 100644
--- a/README.md
+++ b/README.md
@@ -135,6 +135,7 @@ Claiming the Amazon Games works out-of-the-box, however, for games on external s
Epic Games usually has two free games *every week*, before Christmas every day.
Prime Gaming has new games *every month* or more often during Prime days.
GOG usually has one new game every couples of weeks.
+Unreal Engine has new assets to claim *every first Tuesday of a month*.
It is save to run the scripts every day.
diff --git a/unrealengine.js b/unrealengine.js
index 830496c..e44e684 100644
--- a/unrealengine.js
+++ b/unrealengine.js
@@ -1,4 +1,5 @@
// TODO This is mostly a copy of epic-games.js
+// New assets to claim every first Tuesday of a month.
import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdated playwright-extra
import { authenticator } from 'otplib';
From 08fc59520c6a937d04a0a761d3608ceb889bfd1e Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 17 Aug 2023 16:29:08 +0200
Subject: [PATCH 033/253] update docker/build-push-action v3 -> v4
---
.github/workflows/docker.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index c09e9c7..6c3555d 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -38,7 +38,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
-
name: Build and push
- uses: docker/build-push-action@v3
+ uses: docker/build-push-action@v4
with:
context: .
platforms: linux/amd64,linux/arm64 # ,linux/arm/v7
From 13def8ba18066258f84dbb25afbf00a21c14f03a Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 17 Aug 2023 16:30:12 +0200
Subject: [PATCH 034/253] build on every push or PR to main
---
.github/workflows/docker.yml | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 6c3555d..6da8fd6 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -1,14 +1,16 @@
name: Build and push Docker image (amd64, arm64 to hub.docker.com and ghcr.io)
on:
- workflow_dispatch:
- push:
- branches:
- - "main"
+ workflow_dispatch: # allow manual trigger
+ # https://github.com/orgs/community/discussions/26276
+ push: # on every branch, but not for PRs from forks?
paths-ignore:
- ".github/**"
- ".gitignore"
- "README.md"
+ pull_request: # includes PRs from forks but only triggers on creation, not pushes?
+ branches:
+ - "main" # only PRs against main
jobs:
docker:
From 490c4c0788ea2db4fa2b6096df6e106df08e993e Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 17 Aug 2023 16:31:43 +0200
Subject: [PATCH 035/253] don't ignore .github, only issue templates
---
.github/workflows/docker.yml | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 6da8fd6..1914326 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -5,9 +5,8 @@ on:
# https://github.com/orgs/community/discussions/26276
push: # on every branch, but not for PRs from forks?
paths-ignore:
- - ".github/**"
- - ".gitignore"
- "README.md"
+ - ".github/ISSUE_TEMPLATE/**"
pull_request: # includes PRs from forks but only triggers on creation, not pushes?
branches:
- "main" # only PRs against main
From d0761dea2627ea3fc1360a64bce9ad298245ad08 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 17 Aug 2023 16:37:05 +0200
Subject: [PATCH 036/253] pg: dlc: account linking: mention store, close #180
---
prime-gaming.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 1af1eb8..d22c8e7 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -330,7 +330,7 @@ try {
const linkAccountModal = page.locator('[data-a-target="LinkAccountModal"]');
const linkAccountButton = linkAccountModal.locator('[data-a-target="LinkAccountButton"]');
if (await linkAccountButton.count()) {
- console.error(' Missing account linking:', await linkAccountButton.innerText());
+ console.error(' Missing account linking:', await linkAccountButton.getAttribute('aria-label'));
} else if(await page.locator('text=Link game account').count()) { // epic-games only?
console.error(' Missing account linking:', await page.locator('button[data-a-target="gms-cta"]').innerText());
} else {
From 86832e7fe986cbc52e7531040e63b35fe95a0b05 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 17 Aug 2023 16:43:47 +0200
Subject: [PATCH 037/253] pg: dlc: log url of dlc if account not linked, #180
---
prime-gaming.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index d22c8e7..90b1a15 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -330,7 +330,7 @@ try {
const linkAccountModal = page.locator('[data-a-target="LinkAccountModal"]');
const linkAccountButton = linkAccountModal.locator('[data-a-target="LinkAccountButton"]');
if (await linkAccountButton.count()) {
- console.error(' Missing account linking:', await linkAccountButton.getAttribute('aria-label'));
+ console.error(' Missing account linking:', await linkAccountButton.getAttribute('aria-label'), url);
} else if(await page.locator('text=Link game account').count()) { // epic-games only?
console.error(' Missing account linking:', await page.locator('button[data-a-target="gms-cta"]').innerText());
} else {
From 5c8f8fc3fd3bb533c97b6f704344c5c29876568c Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 17 Aug 2023 16:44:59 +0200
Subject: [PATCH 038/253] pg: dlc: log url of dlc if account not linked, #180
---
prime-gaming.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 90b1a15..d1b69eb 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -332,7 +332,7 @@ try {
if (await linkAccountButton.count()) {
console.error(' Missing account linking:', await linkAccountButton.getAttribute('aria-label'), url);
} else if(await page.locator('text=Link game account').count()) { // epic-games only?
- console.error(' Missing account linking:', await page.locator('button[data-a-target="gms-cta"]').innerText());
+ console.error(' Missing account linking:', await page.locator('button[data-a-target="gms-cta"]').innerText(), url);
} else {
const code = await page.inputValue('input[type="text"]');
console.log(' Code to redeem game:', code);
From e794a3306fdee58dc1d2837d323eb23ff0023162 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 17 Aug 2023 17:02:47 +0200
Subject: [PATCH 039/253] pg: dlc: grouped list of dlcs per unlinked store,
#180
---
prime-gaming.js | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index d1b69eb..380812a 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -312,6 +312,7 @@ try {
})));
// console.log(dlcs);
+ const dlc_unlinked = {};
for (const dlc of dlcs) {
const title = `${dlc.game} - ${dlc.title}`;
const url = dlc.url;
@@ -329,10 +330,18 @@ try {
page.click('button:has-text("Continue")').catch(_ => { });
const linkAccountModal = page.locator('[data-a-target="LinkAccountModal"]');
const linkAccountButton = linkAccountModal.locator('[data-a-target="LinkAccountButton"]');
+ let unlinked_store;
if (await linkAccountButton.count()) {
- console.error(' Missing account linking:', await linkAccountButton.getAttribute('aria-label'), url);
+ unlinked_store = await linkAccountButton.getAttribute('aria-label');
+ unlinked_store = unlinked_store.match(/Link (.*) account/)[1];
} else if(await page.locator('text=Link game account').count()) { // epic-games only?
- console.error(' Missing account linking:', await page.locator('button[data-a-target="gms-cta"]').innerText(), url);
+ console.error(' Missing account linking (epic-games specific button?):', await page.locator('button[data-a-target="gms-cta"]').innerText()); // TODO needed?
+ unlinked_store = 'epic-games';
+ }
+ if (unlinked_store) {
+ console.error(' Missing account linking:', unlinked_store, url);
+ dlc_unlinked[unlinked_store] ??= [];
+ dlc_unlinked[unlinked_store].push(title);
} else {
const code = await page.inputValue('input[type="text"]');
console.log(' Code to redeem game:', code);
@@ -348,6 +357,7 @@ try {
await page.click('button[data-type="InGameLoot"]');
}
}
+ console.log('DLC: Unlinked accounts:', dlc_unlinked);
}
} catch (error) {
console.error(error); // .toString()?
From e2e28301e6ab7acb16b525f5580bbd4cb01a4b0a Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sat, 19 Aug 2023 16:35:58 +0200
Subject: [PATCH 040/253] pg: fix #185?
---
prime-gaming.js | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 380812a..bdd662d 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -328,12 +328,13 @@ try {
// epic-games: Fall Guys: Claim now -> 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('button:has-text("Get in-game content")'), page.click('button:has-text("Claim your gift")'), page.click('button:has-text("Claim now")').then(() => page.click('button:has-text("Continue")'))]);
page.click('button:has-text("Continue")').catch(_ => { });
- const linkAccountModal = page.locator('[data-a-target="LinkAccountModal"]');
- const linkAccountButton = linkAccountModal.locator('[data-a-target="LinkAccountButton"]');
+ const linkAccountButton = page.locator('[data-a-target="LinkAccountButton"]');
let unlinked_store;
if (await linkAccountButton.count()) {
unlinked_store = await linkAccountButton.getAttribute('aria-label');
- unlinked_store = unlinked_store.match(/Link (.*) account/)[1];
+ console.debug(' LinkAccountButton label:', unlinked_store);
+ const match = unlinked_store.match(/Link (.*) account/);
+ if (match.length == 2) unlinked_store = match[1];
} else if(await page.locator('text=Link game account').count()) { // epic-games only?
console.error(' Missing account linking (epic-games specific button?):', await page.locator('button[data-a-target="gms-cta"]').innerText()); // TODO needed?
unlinked_store = 'epic-games';
From 7551a6ca965e53048a4991bf482d7bf9873f6d98 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sat, 19 Aug 2023 17:04:33 +0200
Subject: [PATCH 041/253] pg: fixup e2e28301e6ab7acb16b525f5580bbd4cb01a4b0a?
---
prime-gaming.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index bdd662d..1f165b3 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -334,7 +334,7 @@ try {
unlinked_store = await linkAccountButton.getAttribute('aria-label');
console.debug(' LinkAccountButton label:', unlinked_store);
const match = unlinked_store.match(/Link (.*) account/);
- if (match.length == 2) unlinked_store = match[1];
+ if (match && match.length == 2) unlinked_store = match[1];
} else if(await page.locator('text=Link game account').count()) { // epic-games only?
console.error(' Missing account linking (epic-games specific button?):', await page.locator('button[data-a-target="gms-cta"]').innerText()); // TODO needed?
unlinked_store = 'epic-games';
From 40bcf1c8a2995b93c33f5206746a6c81e2ad9c18 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 23 Aug 2023 00:09:27 +0200
Subject: [PATCH 042/253] pg: status 'claimed' (not just 'claimed and
redeemed') for external stores
---
prime-gaming.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/prime-gaming.js b/prime-gaming.js
index 1f165b3..da1c1c9 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -159,6 +159,7 @@ try {
notify_game.status = `failed: need account linking for ${store}`;
db.data[user][title].status = 'failed: need account linking';
} else {
+ db.data[user][title].status = 'claimed';
// print code if there is one
const redeem = {
// 'origin': 'https://www.origin.com/redeem', // TODO still needed or now only via account linking?
From 3d1168f653625b9d51f9b40ce953caff0551791a Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 23 Aug 2023 00:51:19 +0200
Subject: [PATCH 043/253] pg: external: split loop for URLs to avoid issue with
changed elementHandles (Page.adoptNode)
---
prime-gaming.js | 25 +++++++++++++++----------
1 file changed, 15 insertions(+), 10 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index da1c1c9..63c1d96 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -104,7 +104,7 @@ try {
console.log('Number of already claimed games (total):', await games.locator('p:has-text("Collected")').count());
// can't use .all() since the list of elements via locator will change after click while we iterate over it
const internal = await games.locator('.item-card__action:has([data-a-target="FGWPOffer"])').elementHandles();
- const external = games.locator('.item-card__action:has([data-a-target="ExternalOfferClaim"])'); // using .elementHandles() here would lead to error due to page navigation: elementHandle.$: Protocol error (Page.adoptNode)
+ const external = await games.locator('.item-card__action:has([data-a-target="ExternalOfferClaim"])').all();
console.log('Number of free unclaimed games (Prime Gaming):', internal.length);
// claim games in internal store
for (const card of internal) {
@@ -119,15 +119,21 @@ try {
// console.log('Image:', img);
await card.screenshot({ path: screenshot('internal', `${filenamify(title)}.png`) });
}
- console.log('Number of free unclaimed games (external stores):', await external.count());
+ console.log('Number of free unclaimed games (external stores):', external.length);
// claim games in external/linked stores. Linked: origin.com, epicgames.com; Redeem-key: gog.com, legacygames.com, microsoft
- for (const card of await external.elementHandles()) { // TODO refactor (result of external locator changes with each iteration)
- // if (!card) continue;
- const title = await (await card.$('.item-card-details__body__primary')).innerText();
- console.log('Current free game:', title);
+ const external_info = [];
+ for (const card of external) { // need to get data incl. URLs in this loop and then navigate in another, otherwise .all() would update after coming back and .elementHandles() like above would lead to error due to page navigation: elementHandle.$: Protocol error (Page.adoptNode)
+ const title = await card.locator('.item-card-details__body__primary').innerText();
+ const slug = await card.locator('a:has-text("Claim")').first().getAttribute('href');
+ const url = 'https://gaming.amazon.com' + slug.split('?')[0];
+ console.log('Current free game:', title); //, url);
+ // await (await card.$('text=Claim')).click(); // goes to URL of game, no need to wait
+ external_info.push({title, url});
+ }
+ for (const {title, url} of external_info) {
+ await page.goto(url, { waitUntil: 'domcontentloaded' });
if (cfg.debug) await page.pause();
if (cfg.dryrun) continue;
- await (await card.$('text=Claim')).click(); // goes to URL of game, no need to wait
await Promise.any([page.click('button:has-text("Get game")'), page.click('button:has-text("Claim now")'), page.click('button:has-text("Complete Claim")'), page.waitForSelector('div:has-text("Link game account")')]); // waits for navigation
// TODO would be simpler than the below, but will block for linked stores without code
@@ -150,7 +156,6 @@ try {
}
console.log(' External store:', store);
- const url = page.url().split('?')[0];
db.data[user][title] ||= { title, time: datetime(), url, store };
const notify_game = { title, url };
notify_games.push(notify_game); // status is updated below
@@ -268,9 +273,9 @@ try {
// console.info(' Saved a screenshot of page to', p);
}
// await page.pause();
- await page.goto(URL_CLAIM, { waitUntil: 'domcontentloaded' });
- await page.click('button[data-type="Game"]');
}
+ await page.goto(URL_CLAIM, { waitUntil: 'domcontentloaded' });
+ await page.click('button[data-type="Game"]');
if (notify_games.length) { // make screenshot of all games if something was claimed
const p = screenshot(`${filenamify(datetime())}.png`);
From 6a7cca31a4b87ecbe08387c1c5da966cc163056d Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 24 Aug 2023 13:13:54 +0200
Subject: [PATCH 044/253] update dependencies via `ncu -u`, lowdb: pass
defaultData
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
dotenv ^16.0.3 → ^16.3.1
enquirer ^2.3.6 → ^2.4.1
lowdb ^5.1.0 → ^6.0.1
playwright-firefox ^1.31.0 → ^1.37.1
puppeteer-extra-plugin-stealth ^2.11.1 → ^2.11.2
https://github.com/typicode/lowdb/releases/tag/v6.0.0
> Require defaultData parameter for Low and LowSync constructors to improve TypeScript experience
---
epic-games.js | 3 +-
gog.js | 3 +-
package-lock.json | 227 +++++++++++++++++++++++++++-------------------
package.json | 10 +-
prime-gaming.js | 3 +-
unrealengine.js | 3 +-
util.js | 4 +-
7 files changed, 143 insertions(+), 110 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index 314b349..f828031 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -12,8 +12,7 @@ const URL_LOGIN = 'https://www.epicgames.com/id/login?lang=en-US&noHostRedirect=
console.log(datetime(), 'started checking epic-games');
-const db = await jsonDb('epic-games.json');
-db.data ||= {};
+const db = await jsonDb('epic-games.json', {});
handleSIGINT();
diff --git a/gog.js b/gog.js
index cde7acb..4943ff8 100644
--- a/gog.js
+++ b/gog.js
@@ -8,8 +8,7 @@ const URL_CLAIM = 'https://www.gog.com/en';
console.log(datetime(), 'started checking gog');
-const db = await jsonDb('gog.json');
-db.data ||= {};
+const db = await jsonDb('gog.json', {});
handleSIGINT();
diff --git a/package-lock.json b/package-lock.json
index 8dee708..b40dc0e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,12 +10,12 @@
"license": "MIT",
"dependencies": {
"cross-env": "^7.0.3",
- "dotenv": "^16.0.3",
- "enquirer": "^2.3.6",
- "lowdb": "^5.1.0",
+ "dotenv": "^16.3.1",
+ "enquirer": "^2.4.1",
+ "lowdb": "^6.0.1",
"otplib": "^12.0.1",
- "playwright-firefox": "^1.31.0",
- "puppeteer-extra-plugin-stealth": "^2.11.1"
+ "playwright-firefox": "^1.37.1",
+ "puppeteer-extra-plugin-stealth": "^2.11.2"
}
},
"node_modules/@otplib/core": {
@@ -61,9 +61,9 @@
}
},
"node_modules/@types/debug": {
- "version": "4.1.7",
- "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz",
- "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==",
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz",
+ "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==",
"dependencies": {
"@types/ms": "*"
}
@@ -81,6 +81,14 @@
"node": ">=6"
}
},
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/arr-union": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
@@ -170,27 +178,31 @@
}
},
"node_modules/deepmerge": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
- "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/dotenv": {
- "version": "16.0.3",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
- "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==",
+ "version": "16.3.1",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
+ "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
"engines": {
"node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/motdotla/dotenv?sponsor=1"
}
},
"node_modules/enquirer": {
- "version": "2.3.6",
- "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
- "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz",
+ "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==",
"dependencies": {
- "ansi-colors": "^4.1.1"
+ "ansi-colors": "^4.1.1",
+ "strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8.6"
@@ -253,9 +265,9 @@
}
},
"node_modules/graceful-fs": {
- "version": "4.2.10",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
- "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
},
"node_modules/inflight": {
"version": "1.0.6",
@@ -339,14 +351,14 @@
}
},
"node_modules/lowdb": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-5.1.0.tgz",
- "integrity": "sha512-OEysJ2S3j05RqehEypEv3h6EgdV4Y7LTq7LngRNqe1IxsInOm66/sa3fzoI6mmqs2CC+zIJW3vfncGNv2IGi3A==",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-6.0.1.tgz",
+ "integrity": "sha512-1ktuKYLlQzAWwl4/PQkIr8JzNXgcTM6rAhpXaQ6BR+VwI98Q8ZwMFhBOn9u0ldcW3K/WWzhYpS3xyGTshgVGzA==",
"dependencies": {
"steno": "^3.0.0"
},
"engines": {
- "node": ">=14.16"
+ "node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/typicode"
@@ -436,35 +448,35 @@
}
},
"node_modules/playwright-core": {
- "version": "1.31.0",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.31.0.tgz",
- "integrity": "sha512-/KquBjS5DcASCh8cGeNVHuC0kyb7c9plKTwaKxgOGtxT7+DZO2fjmFvPDBSXslEIK5CeOO/2kk5rOCktFXKEdA==",
+ "version": "1.37.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.37.1.tgz",
+ "integrity": "sha512-17EuQxlSIYCmEMwzMqusJ2ztDgJePjrbttaefgdsiqeLWidjYz9BxXaTaZWxH1J95SHGk6tjE+dwgWILJoUZfA==",
"bin": {
- "playwright": "cli.js"
+ "playwright-core": "cli.js"
},
"engines": {
- "node": ">=14"
+ "node": ">=16"
}
},
"node_modules/playwright-firefox": {
- "version": "1.31.0",
- "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.31.0.tgz",
- "integrity": "sha512-E+v16LzBt6SaSRCLH0ZV8NuikTmbmbh9Ky1JgD5sCoF8OHJ3jEjtuoHAJCkO57PJhMX9q/oZ+x133seUMIsKzA==",
+ "version": "1.37.1",
+ "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.37.1.tgz",
+ "integrity": "sha512-I8QScyW+hjGltywqLNh3Y1W96/3x70el9wNneuI34l3uVhiCRt9Co27+kiL+UlA1V8MTzaMere3ONQ8lGeut5w==",
"hasInstallScript": true,
"dependencies": {
- "playwright-core": "1.31.0"
+ "playwright-core": "1.37.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
- "node": ">=14"
+ "node": ">=16"
}
},
"node_modules/puppeteer-extra-plugin": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin/-/puppeteer-extra-plugin-3.2.2.tgz",
- "integrity": "sha512-0uatQxzuVn8yegbrEwSk03wvwpMB5jNs7uTTnermylLZzoT+1rmAQaJXwlS3+vADUbw6ELNgNEHC7Skm0RqHbQ==",
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin/-/puppeteer-extra-plugin-3.2.3.tgz",
+ "integrity": "sha512-6RNy0e6pH8vaS3akPIKGg28xcryKscczt4wIl0ePciZENGE2yoaQJNd17UiEbdmh5/6WW6dPcfRWT9lxBwCi2Q==",
"dependencies": {
"@types/debug": "^4.1.0",
"debug": "^4.1.1",
@@ -487,13 +499,13 @@
}
},
"node_modules/puppeteer-extra-plugin-stealth": {
- "version": "2.11.1",
- "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-stealth/-/puppeteer-extra-plugin-stealth-2.11.1.tgz",
- "integrity": "sha512-n0wdC0Ilc9tk5L6FWLyd0P2gT8b2fp+2NuB+KB0oTSw3wXaZ0D6WNakjJsayJ4waGzIJFCUHkmK9zgx5NKMoFw==",
+ "version": "2.11.2",
+ "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-stealth/-/puppeteer-extra-plugin-stealth-2.11.2.tgz",
+ "integrity": "sha512-bUemM5XmTj9i2ZerBzsk2AN5is0wHMNE6K0hXBzBXOzP5m5G3Wl0RHhiqKeHToe/uIH8AoZiGhc1tCkLZQPKTQ==",
"dependencies": {
"debug": "^4.1.1",
- "puppeteer-extra-plugin": "^3.2.2",
- "puppeteer-extra-plugin-user-preferences": "^2.4.0"
+ "puppeteer-extra-plugin": "^3.2.3",
+ "puppeteer-extra-plugin-user-preferences": "^2.4.1"
},
"engines": {
"node": ">=8"
@@ -512,13 +524,13 @@
}
},
"node_modules/puppeteer-extra-plugin-user-data-dir": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-user-data-dir/-/puppeteer-extra-plugin-user-data-dir-2.4.0.tgz",
- "integrity": "sha512-qrhYPTGIqzL2hpeJ5DXjf8xMy5rt1UvcqSgpGTTOUOjIMz1ROWnKHjBoE9fNBJ4+ToRZbP8MzIDXWlEk/e1zJA==",
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-user-data-dir/-/puppeteer-extra-plugin-user-data-dir-2.4.1.tgz",
+ "integrity": "sha512-kH1GnCcqEDoBXO7epAse4TBPJh9tEpVEK/vkedKfjOVOhZAvLkHGc9swMs5ChrJbRnf8Hdpug6TJlEuimXNQ+g==",
"dependencies": {
"debug": "^4.1.1",
"fs-extra": "^10.0.0",
- "puppeteer-extra-plugin": "^3.2.2",
+ "puppeteer-extra-plugin": "^3.2.3",
"rimraf": "^3.0.2"
},
"engines": {
@@ -538,14 +550,14 @@
}
},
"node_modules/puppeteer-extra-plugin-user-preferences": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-user-preferences/-/puppeteer-extra-plugin-user-preferences-2.4.0.tgz",
- "integrity": "sha512-4XxMhMkJ+qqLsPY9ULF90qS9Bj1Qrwwgp1TY9zTdp1dJuy7QSgYE7xlyamq3cKrRuzg3QUOqygJo52sVeXSg5A==",
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-user-preferences/-/puppeteer-extra-plugin-user-preferences-2.4.1.tgz",
+ "integrity": "sha512-i1oAZxRbc1bk8MZufKCruCEC3CCafO9RKMkkodZltI4OqibLFXF3tj6HZ4LZ9C5vCXZjYcDWazgtY69mnmrQ9A==",
"dependencies": {
"debug": "^4.1.1",
"deepmerge": "^4.2.2",
- "puppeteer-extra-plugin": "^3.2.2",
- "puppeteer-extra-plugin-user-data-dir": "^2.4.0"
+ "puppeteer-extra-plugin": "^3.2.3",
+ "puppeteer-extra-plugin-user-data-dir": "^2.4.1"
},
"engines": {
"node": ">=8"
@@ -640,6 +652,17 @@
"url": "https://github.com/sponsors/typicode"
}
},
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/thirty-two": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz",
@@ -720,9 +743,9 @@
}
},
"@types/debug": {
- "version": "4.1.7",
- "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz",
- "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==",
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz",
+ "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==",
"requires": {
"@types/ms": "*"
}
@@ -737,6 +760,11 @@
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
"integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="
},
+ "ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+ },
"arr-union": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
@@ -800,21 +828,22 @@
}
},
"deepmerge": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
- "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg=="
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
},
"dotenv": {
- "version": "16.0.3",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
- "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ=="
+ "version": "16.3.1",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
+ "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ=="
},
"enquirer": {
- "version": "2.3.6",
- "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
- "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz",
+ "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==",
"requires": {
- "ansi-colors": "^4.1.1"
+ "ansi-colors": "^4.1.1",
+ "strip-ansi": "^6.0.1"
}
},
"for-in": {
@@ -859,9 +888,9 @@
}
},
"graceful-fs": {
- "version": "4.2.10",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
- "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
},
"inflight": {
"version": "1.0.6",
@@ -928,9 +957,9 @@
"integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ=="
},
"lowdb": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-5.1.0.tgz",
- "integrity": "sha512-OEysJ2S3j05RqehEypEv3h6EgdV4Y7LTq7LngRNqe1IxsInOm66/sa3fzoI6mmqs2CC+zIJW3vfncGNv2IGi3A==",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-6.0.1.tgz",
+ "integrity": "sha512-1ktuKYLlQzAWwl4/PQkIr8JzNXgcTM6rAhpXaQ6BR+VwI98Q8ZwMFhBOn9u0ldcW3K/WWzhYpS3xyGTshgVGzA==",
"requires": {
"steno": "^3.0.0"
}
@@ -1003,22 +1032,22 @@
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
"playwright-core": {
- "version": "1.31.0",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.31.0.tgz",
- "integrity": "sha512-/KquBjS5DcASCh8cGeNVHuC0kyb7c9plKTwaKxgOGtxT7+DZO2fjmFvPDBSXslEIK5CeOO/2kk5rOCktFXKEdA=="
+ "version": "1.37.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.37.1.tgz",
+ "integrity": "sha512-17EuQxlSIYCmEMwzMqusJ2ztDgJePjrbttaefgdsiqeLWidjYz9BxXaTaZWxH1J95SHGk6tjE+dwgWILJoUZfA=="
},
"playwright-firefox": {
- "version": "1.31.0",
- "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.31.0.tgz",
- "integrity": "sha512-E+v16LzBt6SaSRCLH0ZV8NuikTmbmbh9Ky1JgD5sCoF8OHJ3jEjtuoHAJCkO57PJhMX9q/oZ+x133seUMIsKzA==",
+ "version": "1.37.1",
+ "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.37.1.tgz",
+ "integrity": "sha512-I8QScyW+hjGltywqLNh3Y1W96/3x70el9wNneuI34l3uVhiCRt9Co27+kiL+UlA1V8MTzaMere3ONQ8lGeut5w==",
"requires": {
- "playwright-core": "1.31.0"
+ "playwright-core": "1.37.1"
}
},
"puppeteer-extra-plugin": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin/-/puppeteer-extra-plugin-3.2.2.tgz",
- "integrity": "sha512-0uatQxzuVn8yegbrEwSk03wvwpMB5jNs7uTTnermylLZzoT+1rmAQaJXwlS3+vADUbw6ELNgNEHC7Skm0RqHbQ==",
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin/-/puppeteer-extra-plugin-3.2.3.tgz",
+ "integrity": "sha512-6RNy0e6pH8vaS3akPIKGg28xcryKscczt4wIl0ePciZENGE2yoaQJNd17UiEbdmh5/6WW6dPcfRWT9lxBwCi2Q==",
"requires": {
"@types/debug": "^4.1.0",
"debug": "^4.1.1",
@@ -1026,35 +1055,35 @@
}
},
"puppeteer-extra-plugin-stealth": {
- "version": "2.11.1",
- "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-stealth/-/puppeteer-extra-plugin-stealth-2.11.1.tgz",
- "integrity": "sha512-n0wdC0Ilc9tk5L6FWLyd0P2gT8b2fp+2NuB+KB0oTSw3wXaZ0D6WNakjJsayJ4waGzIJFCUHkmK9zgx5NKMoFw==",
+ "version": "2.11.2",
+ "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-stealth/-/puppeteer-extra-plugin-stealth-2.11.2.tgz",
+ "integrity": "sha512-bUemM5XmTj9i2ZerBzsk2AN5is0wHMNE6K0hXBzBXOzP5m5G3Wl0RHhiqKeHToe/uIH8AoZiGhc1tCkLZQPKTQ==",
"requires": {
"debug": "^4.1.1",
- "puppeteer-extra-plugin": "^3.2.2",
- "puppeteer-extra-plugin-user-preferences": "^2.4.0"
+ "puppeteer-extra-plugin": "^3.2.3",
+ "puppeteer-extra-plugin-user-preferences": "^2.4.1"
}
},
"puppeteer-extra-plugin-user-data-dir": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-user-data-dir/-/puppeteer-extra-plugin-user-data-dir-2.4.0.tgz",
- "integrity": "sha512-qrhYPTGIqzL2hpeJ5DXjf8xMy5rt1UvcqSgpGTTOUOjIMz1ROWnKHjBoE9fNBJ4+ToRZbP8MzIDXWlEk/e1zJA==",
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-user-data-dir/-/puppeteer-extra-plugin-user-data-dir-2.4.1.tgz",
+ "integrity": "sha512-kH1GnCcqEDoBXO7epAse4TBPJh9tEpVEK/vkedKfjOVOhZAvLkHGc9swMs5ChrJbRnf8Hdpug6TJlEuimXNQ+g==",
"requires": {
"debug": "^4.1.1",
"fs-extra": "^10.0.0",
- "puppeteer-extra-plugin": "^3.2.2",
+ "puppeteer-extra-plugin": "^3.2.3",
"rimraf": "^3.0.2"
}
},
"puppeteer-extra-plugin-user-preferences": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-user-preferences/-/puppeteer-extra-plugin-user-preferences-2.4.0.tgz",
- "integrity": "sha512-4XxMhMkJ+qqLsPY9ULF90qS9Bj1Qrwwgp1TY9zTdp1dJuy7QSgYE7xlyamq3cKrRuzg3QUOqygJo52sVeXSg5A==",
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-user-preferences/-/puppeteer-extra-plugin-user-preferences-2.4.1.tgz",
+ "integrity": "sha512-i1oAZxRbc1bk8MZufKCruCEC3CCafO9RKMkkodZltI4OqibLFXF3tj6HZ4LZ9C5vCXZjYcDWazgtY69mnmrQ9A==",
"requires": {
"debug": "^4.1.1",
"deepmerge": "^4.2.2",
- "puppeteer-extra-plugin": "^3.2.2",
- "puppeteer-extra-plugin-user-data-dir": "^2.4.0"
+ "puppeteer-extra-plugin": "^3.2.3",
+ "puppeteer-extra-plugin-user-data-dir": "^2.4.1"
}
},
"rimraf": {
@@ -1109,6 +1138,14 @@
"resolved": "https://registry.npmjs.org/steno/-/steno-3.0.0.tgz",
"integrity": "sha512-uZtn7Ht9yXLiYgOsmo8btj4+f7VxyYheMt8g6F1ANjyqByQXEE2Gygjgenp3otHH1TlHsS4JAaRGv5wJ1wvMNw=="
},
+ "strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "requires": {
+ "ansi-regex": "^5.0.1"
+ }
+ },
"thirty-two": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz",
diff --git a/package.json b/package.json
index 04cf02b..6179433 100644
--- a/package.json
+++ b/package.json
@@ -11,12 +11,12 @@
"type": "module",
"dependencies": {
"cross-env": "^7.0.3",
- "dotenv": "^16.0.3",
- "enquirer": "^2.3.6",
- "lowdb": "^5.1.0",
+ "dotenv": "^16.3.1",
+ "enquirer": "^2.4.1",
+ "lowdb": "^6.0.1",
"otplib": "^12.0.1",
- "playwright-firefox": "^1.31.0",
- "puppeteer-extra-plugin-stealth": "^2.11.1"
+ "playwright-firefox": "^1.37.1",
+ "puppeteer-extra-plugin-stealth": "^2.11.2"
},
"repository": {
"type": "git",
diff --git a/prime-gaming.js b/prime-gaming.js
index 63c1d96..659bd26 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -10,8 +10,7 @@ const URL_CLAIM = 'https://gaming.amazon.com/home';
console.log(datetime(), 'started checking prime-gaming');
-const db = await jsonDb('prime-gaming.json');
-db.data ||= {};
+const db = await jsonDb('prime-gaming.json', {});
handleSIGINT();
diff --git a/unrealengine.js b/unrealengine.js
index e44e684..4370a85 100644
--- a/unrealengine.js
+++ b/unrealengine.js
@@ -15,8 +15,7 @@ const URL_LOGIN = 'https://www.epicgames.com/id/login?lang=en-US&noHostRedirect=
console.log(datetime(), 'started checking unrealengine');
-const db = await jsonDb('unrealengine.json');
-db.data ||= {};
+const db = await jsonDb('unrealengine.json', {});
handleSIGINT();
diff --git a/util.js b/util.js
index e581684..8cc91e9 100644
--- a/util.js
+++ b/util.js
@@ -13,8 +13,8 @@ export const resolve = (...a) => a.length && a[0] == '0' ? null : path.resolve(.
// json database
import { Low } from 'lowdb';
import { JSONFile } from 'lowdb/node';
-export const jsonDb = async file => {
- const db = new Low(new JSONFile(dataDir(file)));
+export const jsonDb = async (file, defaultData) => {
+ const db = new Low(new JSONFile(dataDir(file)), defaultData);
await db.read();
return db;
};
From da23bc2f92f513ffda801e5156ec870a6f0673bc Mon Sep 17 00:00:00 2001
From: 4n4n4s
Date: Fri, 25 Aug 2023 17:42:52 +0200
Subject: [PATCH 045/253] Update README.md
Update documentation
---
README.md | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 538ea4f..ac35150 100644
--- a/README.md
+++ b/README.md
@@ -67,7 +67,7 @@ Available options/variables and their default values:
| WIDTH | 1280 | Width of the opened browser (and of screen for VNC in Docker). |
| HEIGHT | 1280 | Height of the opened browser (and of screen for VNC in Docker). |
| VNC_PASSWORD | | VNC password for Docker. No password used by default! |
-| NOTIFY | | Notification services to use (Pushover, Slack, Telegram...), see below. |
+| NOTIFY | | Notification services to use (Pushover, Slack, Telegram...), see below. [Apprise]([Apprise](https://github.com/caronc/apprise)) |
| NOTIFY_TITLE | | Optional title for notifications, e.g. for Pushover. |
| BROWSER_DIR | data/browser | Directory for browser profile, e.g. for multiple accounts. |
| TIMEOUT | 60 | Timeout for any page action. Should be fine even on slow machines. |
@@ -93,7 +93,7 @@ See `config.js` for all options.
You can add options directly in the command or put them in a file to load.
##### Docker
-You can pass variables using `-e VAR=VAL`, for example `docker run -e EMAIL=foo@bar.baz -e NOTIFY='tgram://...' ...` or using `--env-file fgc.env` where `fgc.env` is a file on your host system (see [docs](https://docs.docker.com/engine/reference/commandline/run/#env)). You can also `docker cp` your configuration file to `/fgc/data/config.env` in the `fgc` volume to store it with the rest of the data instead of on the host ([example](https://github.com/moby/moby/issues/25245#issuecomment-365980572)).
+You can pass variables using `-e VAR=VAL`, for example `docker run -e EMAIL=foo@bar.baz -e NOTIFY='tgram://bottoken/ChatID' ...` or using `--env-file fgc.env` where `fgc.env` is a file on your host system (see [docs](https://docs.docker.com/engine/reference/commandline/run/#env)). You can also `docker cp` your configuration file to `/fgc/data/config.env` in the `fgc` volume to store it with the rest of the data instead of on the host ([example](https://github.com/moby/moby/issues/25245#issuecomment-365980572)).
If you are using [docker compose](https://docs.docker.com/compose/environment-variables/) (or Portainer etc.), you can put options in the `environment:` section.
##### Without Docker
@@ -147,6 +147,7 @@ If you want it to run regularly, you have to schedule the runs yourself:
- macOS: [launchd](https://stackoverflow.com/questions/132955/how-do-i-set-a-task-to-run-every-so-often)
- Windows: [task scheduler](https://active-directory-wp.com/docs/Usage/How_to_add_a_cron_job_on_Windows/Scheduled_tasks_and_cron_jobs_on_Windows/index.html), [other options](https://stackoverflow.com/questions/132971/what-is-the-windows-version-of-cron)
- any OS: use a process manager like [pm2](https://pm2.keymetrics.io/docs/usage/restart-strategies/)
+- Docker Compose `command: bash -c "node epic-games; node prime-gaming; node gog; echo sleeping; sleep 1d"`
TODO: ~~add some server-mode where the script just keeps running and claims games e.g. every day.~~
From 66c12b8ea2003a7ffa1541be3aad6086d77e65e9 Mon Sep 17 00:00:00 2001
From: 4n4n4s
Date: Fri, 25 Aug 2023 18:06:31 +0200
Subject: [PATCH 046/253] Create dependabot.yml
---
.github/dependabot.yml | 15 +++++++++++++++
1 file changed, 15 insertions(+)
create mode 100644 .github/dependabot.yml
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..8c4fdf8
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,15 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
+
+version: 2
+updates:
+ - package-ecosystem: "npm"
+ directory: "/"
+ schedule:
+ interval: "daily"
+ - package-ecosystem: "docker"
+ directory: "/"
+ schedule:
+ interval: "daily"
From a3adaae99fbae283091554efeee9eaa8b1a96cd0 Mon Sep 17 00:00:00 2001
From: 4n4n4s
Date: Fri, 25 Aug 2023 16:30:04 +0000
Subject: [PATCH 047/253] Fixed README.md
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index ac35150..a5f7113 100644
--- a/README.md
+++ b/README.md
@@ -67,7 +67,7 @@ Available options/variables and their default values:
| WIDTH | 1280 | Width of the opened browser (and of screen for VNC in Docker). |
| HEIGHT | 1280 | Height of the opened browser (and of screen for VNC in Docker). |
| VNC_PASSWORD | | VNC password for Docker. No password used by default! |
-| NOTIFY | | Notification services to use (Pushover, Slack, Telegram...), see below. [Apprise]([Apprise](https://github.com/caronc/apprise)) |
+| NOTIFY | | Notification services to use (Pushover, Slack, Telegram...), see below. [Apprise](https://github.com/caronc/apprise) |
| NOTIFY_TITLE | | Optional title for notifications, e.g. for Pushover. |
| BROWSER_DIR | data/browser | Directory for browser profile, e.g. for multiple accounts. |
| TIMEOUT | 60 | Timeout for any page action. Should be fine even on slow machines. |
@@ -147,7 +147,7 @@ If you want it to run regularly, you have to schedule the runs yourself:
- macOS: [launchd](https://stackoverflow.com/questions/132955/how-do-i-set-a-task-to-run-every-so-often)
- Windows: [task scheduler](https://active-directory-wp.com/docs/Usage/How_to_add_a_cron_job_on_Windows/Scheduled_tasks_and_cron_jobs_on_Windows/index.html), [other options](https://stackoverflow.com/questions/132971/what-is-the-windows-version-of-cron)
- any OS: use a process manager like [pm2](https://pm2.keymetrics.io/docs/usage/restart-strategies/)
-- Docker Compose `command: bash -c "node epic-games; node prime-gaming; node gog; echo sleeping; sleep 1d"`
+- Docker Compose `command: bash -c "node epic-games; node prime-gaming; node gog; echo sleeping; sleep 1d"` additionally add `restart: unless-stopped` to it.
TODO: ~~add some server-mode where the script just keeps running and claims games e.g. every day.~~
From a7d045f0e2439b86dcccb09bbdeed36875deeda8 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Fri, 25 Aug 2023 21:40:28 +0200
Subject: [PATCH 048/253] ignore dependabot.yml in workflows/docker.yml
---
.github/workflows/docker.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 1914326..f32d514 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -6,6 +6,7 @@ on:
push: # on every branch, but not for PRs from forks?
paths-ignore:
- "README.md"
+ - ".github/dependabot.yml"
- ".github/ISSUE_TEMPLATE/**"
pull_request: # includes PRs from forks but only triggers on creation, not pushes?
branches:
From 70b5182bedb41c671f739ce633e70486a0ab6b13 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Fri, 25 Aug 2023 21:43:57 +0200
Subject: [PATCH 049/253] same as before, just ignore .github/** and then
include docker.yml
---
.github/workflows/docker.yml | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index f32d514..01781f8 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -4,10 +4,11 @@ on:
workflow_dispatch: # allow manual trigger
# https://github.com/orgs/community/discussions/26276
push: # on every branch, but not for PRs from forks?
- paths-ignore:
- - "README.md"
- - ".github/dependabot.yml"
- - ".github/ISSUE_TEMPLATE/**"
+ paths:
+ - '**'
+ - '!README.md'
+ - '!.github/**'
+ - '.github/workflows/docker.yml'
pull_request: # includes PRs from forks but only triggers on creation, not pushes?
branches:
- "main" # only PRs against main
From c09d20766c17d01c5c0b8d326a2aafbe34134048 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Fri, 25 Aug 2023 21:50:17 +0200
Subject: [PATCH 050/253] dependabot daily -> weekly, may want to customize
`commit-message`
---
.github/dependabot.yml | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 8c4fdf8..1b47972 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -8,8 +8,21 @@ updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
- interval: "daily"
+ interval: "weekly"
+ # commit-message:
+ # prefix: "npm"
+ # include: "scope"
- package-ecosystem: "docker"
directory: "/"
schedule:
- interval: "daily"
+ interval: "weekly"
+ # commit-message:
+ # prefix: "docker"
+ # include: "scope"
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ # commit-message:
+ # prefix: "github-actions"
+ # include: "scope"
From 682c6512245e47f5968818922ff8ede6cbafb3fa Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sat, 26 Aug 2023 10:42:24 +0200
Subject: [PATCH 051/253] docker: use OCI labels
https://github.com/opencontainers/image-spec/blob/main/annotations.md
https://docs.docker.com/engine/reference/builder/#label
Before:
```console
$ docker image inspect --format='{{json .Config.Labels}}' ghcr.io/vogler/free-games-claimer | jq
{
"org.opencontainers.image.ref.name": "ubuntu",
"org.opencontainers.image.version": "22.04"
}
```
After:
```console
$ docker image inspect --format='{{json .Config.Labels}}' ghcr.io/vogler/free-games-claimer | jq
{
"org.opencontainers.image.base.name": "ubuntu:jammy",
"org.opencontainers.image.description": "Automatically claims free games on the Epic Games Store, Amazon Prime Gaming and GOG",
"org.opencontainers.image.name": "free-games-claimer",
"org.opencontainers.image.ref.name": "",
"org.opencontainers.image.revision": "",
"org.opencontainers.image.source": "https://github.com/vogler/free-games-claimer",
"org.opencontainers.image.title": "free-games-claimer",
"org.opencontainers.image.url": "https://github.com/vogler/free-games-claimer",
"org.opencontainers.image.version": "latest"
}
```
---
Dockerfile | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/Dockerfile b/Dockerfile
index ebad9eb..df20609 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -60,6 +60,16 @@ COPY . .
RUN dos2unix *.sh && chmod +x *.sh
COPY docker-entrypoint.sh /usr/local/bin/
+LABEL org.opencontainers.image.title="free-games-claimer" \
+ org.opencontainers.image.name="free-games-claimer" \
+ org.opencontainers.image.description="Automatically claims free games on the Epic Games Store, Amazon Prime Gaming and GOG" \
+ org.opencontainers.image.url="https://github.com/vogler/free-games-claimer" \
+ org.opencontainers.image.source="https://github.com/vogler/free-games-claimer" \
+ org.opencontainers.image.revision=${COMMIT_SHA} \
+ org.opencontainers.image.ref.name=${BRANCH} \
+ org.opencontainers.image.base.name="ubuntu:jammy" \
+ org.opencontainers.image.version="latest"
+
# Configure VNC via environment variables:
ENV VNC_PORT 5900
ENV NOVNC_PORT 6080
From 2eb17a1419ca5dd9c682b430e0091a352fd2c414 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 27 Aug 2023 18:33:27 +0200
Subject: [PATCH 052/253] docker: show version via $COMMIT_SHA (and $BRANCH if
not "main")
---
Dockerfile | 3 +++
docker-entrypoint.sh | 3 +++
2 files changed, 6 insertions(+)
diff --git a/Dockerfile b/Dockerfile
index df20609..82bb263 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -70,6 +70,9 @@ LABEL org.opencontainers.image.title="free-games-claimer" \
org.opencontainers.image.base.name="ubuntu:jammy" \
org.opencontainers.image.version="latest"
+ENV COMMIT_SHA=${COMMIT_SHA}
+ENV BRANCH=${BRANCH}
+
# Configure VNC via environment variables:
ENV VNC_PORT 5900
ENV NOVNC_PORT 6080
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
index 7796364..cfdac7e 100755
--- a/docker-entrypoint.sh
+++ b/docker-entrypoint.sh
@@ -2,6 +2,9 @@
set -eo pipefail # exit on error, error on any fail in pipe (not just last cmd); add -x to print each cmd; see gist bash_strict_mode.md
+echo "Version: https://github.com/vogler/free-games-claimer/tree/${COMMIT_SHA}"
+[ ! -z $BRANCH ] && [ $BRANCH != "main" ] && echo "Branch: ${BRANCH}"
+
# Remove chromium profile lock.
# When running in docker and then killing it, on the next run chromium displayed a dialog to unlock the profile which made the script time out.
# Maybe due to changed hostname of container or due to how the docker container kills playwright - didn't check.
From 8a010dbcc7521837b784a6f18f1360bce355954b Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 27 Aug 2023 23:32:56 +0200
Subject: [PATCH 053/253] docker: pass in build-args, add $NOW
---
.github/workflows/docker.yml | 12 +++++++++++-
Dockerfile | 12 ++++++++----
docker-entrypoint.sh | 3 ++-
3 files changed, 21 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 01781f8..8903c55 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -20,6 +20,11 @@ jobs:
-
name: Checkout
uses: actions/checkout@v3
+ -
+ name: Set environment variables
+ run: |
+ echo "BRANCH=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV
+ echo "NOW=$(date +'%Y-%m-%dT%H:%M:%S')" >> $GITHUB_ENV
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
@@ -29,6 +34,7 @@ jobs:
-
name: Login to Docker Hub
uses: docker/login-action@v2
+ if: ${{ secrets.DOCKERHUB_USERNAME && secrets.DOCKERHUB_TOKEN }}
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
@@ -44,8 +50,12 @@ jobs:
uses: docker/build-push-action@v4
with:
context: .
- platforms: linux/amd64,linux/arm64 # ,linux/arm/v7
push: true
+ build-args: |
+ COMMIT=${{ github.sha }}
+ BRANCH=${{ env.BRANCH }}
+ NOW=${{ env.NOW }}
+ platforms: linux/amd64,linux/arm64 # ,linux/arm/v7
tags: |
voglerr/free-games-claimer:latest
ghcr.io/vogler/free-games-claimer:latest
diff --git a/Dockerfile b/Dockerfile
index 82bb263..67a5e89 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -60,19 +60,23 @@ COPY . .
RUN dos2unix *.sh && chmod +x *.sh
COPY docker-entrypoint.sh /usr/local/bin/
+ARG COMMIT=""
+ARG BRANCH=""
+ARG NOW=""
+ENV COMMIT=${COMMIT}
+ENV BRANCH=${BRANCH}
+ENV NOW=${NOW}
+
LABEL org.opencontainers.image.title="free-games-claimer" \
org.opencontainers.image.name="free-games-claimer" \
org.opencontainers.image.description="Automatically claims free games on the Epic Games Store, Amazon Prime Gaming and GOG" \
org.opencontainers.image.url="https://github.com/vogler/free-games-claimer" \
org.opencontainers.image.source="https://github.com/vogler/free-games-claimer" \
- org.opencontainers.image.revision=${COMMIT_SHA} \
+ org.opencontainers.image.revision=${COMMIT} \
org.opencontainers.image.ref.name=${BRANCH} \
org.opencontainers.image.base.name="ubuntu:jammy" \
org.opencontainers.image.version="latest"
-ENV COMMIT_SHA=${COMMIT_SHA}
-ENV BRANCH=${BRANCH}
-
# Configure VNC via environment variables:
ENV VNC_PORT 5900
ENV NOVNC_PORT 6080
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
index cfdac7e..2b5ddee 100755
--- a/docker-entrypoint.sh
+++ b/docker-entrypoint.sh
@@ -2,8 +2,9 @@
set -eo pipefail # exit on error, error on any fail in pipe (not just last cmd); add -x to print each cmd; see gist bash_strict_mode.md
-echo "Version: https://github.com/vogler/free-games-claimer/tree/${COMMIT_SHA}"
+echo "Version: https://github.com/vogler/free-games-claimer/tree/${COMMIT}"
[ ! -z $BRANCH ] && [ $BRANCH != "main" ] && echo "Branch: ${BRANCH}"
+echo "Build: $NOW"
# Remove chromium profile lock.
# When running in docker and then killing it, on the next run chromium displayed a dialog to unlock the profile which made the script time out.
From d7949fb9dd4c696116e056c6930adceb059d7839 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 27 Aug 2023 23:39:52 +0200
Subject: [PATCH 054/253] can't use secrets in if of workflow step?
---
.github/workflows/docker.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 8903c55..4b4c78e 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -34,7 +34,7 @@ jobs:
-
name: Login to Docker Hub
uses: docker/login-action@v2
- if: ${{ secrets.DOCKERHUB_USERNAME && secrets.DOCKERHUB_TOKEN }}
+ # if: ${{ secrets.DOCKERHUB_USERNAME && secrets.DOCKERHUB_TOKEN }}
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
From f528fbfd305218ae59bb3b15b4f45ac9e705ff3e Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 27 Aug 2023 23:41:19 +0200
Subject: [PATCH 055/253] docker: more human-readable format for date $NOW
---
.github/workflows/docker.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 4b4c78e..2459dcd 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -24,7 +24,7 @@ jobs:
name: Set environment variables
run: |
echo "BRANCH=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV
- echo "NOW=$(date +'%Y-%m-%dT%H:%M:%S')" >> $GITHUB_ENV
+ echo "NOW=$(date -R)" >> $GITHUB_ENV # date -Iseconds; date +'%Y-%m-%dT%H:%M:%S'
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
From c211472d0c59337545f1c5a91b5aeb5d2fd022df Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 27 Aug 2023 23:59:30 +0200
Subject: [PATCH 056/253] docker: try GitHub Actions cache
https://docs.docker.com/build/ci/github-actions/cache/#github-cache
---
.github/workflows/docker.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 2459dcd..bf7fc06 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -59,3 +59,5 @@ jobs:
tags: |
voglerr/free-games-claimer:latest
ghcr.io/vogler/free-games-claimer:latest
+ cache-from: type=gha
+ cache-to: type=gha,mode=max
From d4d7ee32819013949e5b1a95f5179cd8a7f9fb77 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Mon, 28 Aug 2023 00:47:52 +0200
Subject: [PATCH 057/253] fix indent
---
util.js | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/util.js b/util.js
index 8cc91e9..0b2dcbe 100644
--- a/util.js
+++ b/util.js
@@ -108,11 +108,11 @@ export const notify = (html) => new Promise((resolve, reject) => {
const title = cfg.notify_title ? `-t ${cfg.notify_title}` : '';
exec(`apprise ${cfg.notify} -i html '${title}' -b '${html}'`, (error, stdout, stderr) => {
if (error) {
- console.log(`error: ${error.message}`);
- if (error.message.includes('command not found')) {
- console.info('Run `pip install apprise`. See https://github.com/vogler/free-games-claimer#notifications');
- }
- return resolve();
+ console.log(`error: ${error.message}`);
+ if (error.message.includes('command not found')) {
+ console.info('Run `pip install apprise`. See https://github.com/vogler/free-games-claimer#notifications');
+ }
+ return resolve();
}
if (stderr) console.error(`stderr: ${stderr}`);
if (stdout) console.log(`stdout: ${stdout}`);
From 6560afa2b5c48e659836b4040a4b9e5f7914b072 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Mon, 28 Aug 2023 01:17:57 +0200
Subject: [PATCH 058/253] version.js to check if running the latest version
---
version.js | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
create mode 100644 version.js
diff --git a/version.js b/version.js
new file mode 100644
index 0000000..170ed42
--- /dev/null
+++ b/version.js
@@ -0,0 +1,49 @@
+// check if running the latest version
+
+import {log} from 'console';
+import { existsSync, readFileSync } from 'fs';
+import { exec } from 'child_process';
+
+const execp = (cmd) => new Promise((resolve, reject) => {
+ exec(cmd, (error, stdout, stderr) => {
+ if (stderr) console.error(`stderr: ${stderr}`);
+ // if (stdout) console.log(`stdout: ${stdout}`);
+ if (error) {
+ console.log(`error: ${error.message}`);
+ if (error.message.includes('command not found')) {
+ console.info('Install git to check for updates!');
+ }
+ return reject();
+ }
+ resolve(stdout.trim());
+ });
+});
+
+const git_main = () => readFileSync('.git/refs/heads/main').toString().trim();
+
+let sha, date;
+if (existsSync('/.dockerenv')) {
+ log('Running inside Docker.');
+ ['COMMIT', 'BRANCH', 'NOW'].forEach(v => log(` ${v}:`, process.env[v]));
+ sha = process.env.COMMIT;
+ date = process.env.NOW;
+} else {
+ log('Not running inside Docker.');
+ sha = await execp('git rev-parse HEAD');
+ date = await execp('git show -s --format=%cD'); // same as format as `date -R` (RFC2822)
+ // date = await execp('git show -s --format=%ch'); // %ch is same as --date=human (short/relative)
+}
+
+const gh = await (await fetch('https://api.github.com/repos/vogler/free-games-claimer/commits/main', {
+ // headers: { accept: 'application/vnd.github.VERSION.sha' }
+ })).json();
+// log(gh);
+
+log('Local commit:', sha, new Date(date));
+log('Online commit:', gh.sha, new Date(gh.commit.committer.date));
+
+if (sha == gh.sha) {
+ log('Running the latest version!')
+} else {
+ log('Not running the latest version!')
+}
From 9261be690ce0aaa744a3106628b0579a1363fdbf Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Mon, 28 Aug 2023 01:25:09 +0200
Subject: [PATCH 059/253] '/.dockerenv' did not exist in container...
---
version.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/version.js b/version.js
index 170ed42..7b9ebef 100644
--- a/version.js
+++ b/version.js
@@ -1,7 +1,7 @@
// check if running the latest version
import {log} from 'console';
-import { existsSync, readFileSync } from 'fs';
+import { readFileSync } from 'fs';
import { exec } from 'child_process';
const execp = (cmd) => new Promise((resolve, reject) => {
@@ -22,7 +22,8 @@ const execp = (cmd) => new Promise((resolve, reject) => {
const git_main = () => readFileSync('.git/refs/heads/main').toString().trim();
let sha, date;
-if (existsSync('/.dockerenv')) {
+// if (existsSync('/.dockerenv')) { // did not work
+if (process.env.NOVNC_PORT) {
log('Running inside Docker.');
['COMMIT', 'BRANCH', 'NOW'].forEach(v => log(` ${v}:`, process.env[v]));
sha = process.env.COMMIT;
From 6bb1dca93412c82219bd8f68fd1dec8a42aab01b Mon Sep 17 00:00:00 2001
From: KevinMatt <36391318+kevinmatthe@users.noreply.github.com>
Date: Mon, 28 Aug 2023 15:16:30 +0800
Subject: [PATCH 060/253] epic-games: captcha on login: await notify (#195)
Adding async handle in captcha notify
---
epic-games.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index f828031..9211042 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -79,9 +79,9 @@ try {
await page.fill('#email', email);
await page.fill('#password', password);
await page.click('button[type="submit"]');
- page.waitForSelector('#h_captcha_challenge_login_prod iframe').then(() => {
+ page.waitForSelector('#h_captcha_challenge_login_prod iframe').then(async () => {
console.error('Got a captcha during login (likely due to too many attempts)! You may solve it in the browser, get a new IP or try again in a few hours.');
- notify('epic-games: got captcha during login. Please check.');
+ await notify('epic-games: got captcha during login. Please check.');
}).catch(_ => { });
// handle MFA, but don't await it
page.waitForURL('**/id/login/mfa**').then(async () => {
From 621032e459df85beb2996fa69ff93e8aeb526b9a Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 30 Aug 2023 19:20:28 +0200
Subject: [PATCH 061/253] eg: notify about captcha before claim, closes #191
---
epic-games.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/epic-games.js b/epic-games.js
index 9211042..6e0b62c 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -204,6 +204,7 @@ try {
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.')
+ await notify('epic-games: got captcha challenge right before claim. Use VNC to solve it manually.')
// await page.waitForTimeout(2000);
// const p = path.resolve(cfg.dir.screenshots, 'epic-games', 'captcha', `${filenamify(datetime())}.png`);
// await captcha.screenshot({ path: p });
From 93d082e7002d63adcae08a2c92370e2953e067d8 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 31 Aug 2023 17:05:12 +0200
Subject: [PATCH 062/253] eg: comment userAgent firefox (docker)
---
epic-games.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/epic-games.js b/epic-games.js
index 6e0b62c..82911ba 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -26,7 +26,8 @@ const context = await firefox.launchPersistentContext(cfg.dir.browser, {
headless: cfg.headless,
viewport: { width: cfg.width, height: cfg.height },
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 for firefox: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:106.0) Gecko/20100101 Firefox/106.0
+ // 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
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
From 5ae4ce5666b8a528e629468ea8e08a55762d4fe4 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 31 Aug 2023 18:59:10 +0200
Subject: [PATCH 063/253] eg: option TIME=1 to log duration of steps, #183
---
config.js | 1 +
epic-games.js | 15 +++++++++++++++
2 files changed, 16 insertions(+)
diff --git a/config.js b/config.js
index d863d8d..e6a9438 100644
--- a/config.js
+++ b/config.js
@@ -7,6 +7,7 @@ dotenv.config({ path: 'data/config.env' }); // loads env vars from file - will n
export const cfg = {
debug: process.env.PWDEBUG == '1', // runs non-headless and opens https://playwright.dev/docs/inspector
record: process.env.RECORD == '1', // `recordHar` (network) + `recordVideo`
+ time: process.env.TIME == '1', // log duration of each step
dryrun: process.env.DRYRUN == '1', // don't claim anything
show: process.env.SHOW == '1', // run non-headless
get headless() { return !this.debug && !this.show },
diff --git a/epic-games.js b/epic-games.js
index 82911ba..f23f229 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -16,6 +16,8 @@ const db = await jsonDb('epic-games.json', {});
handleSIGINT();
+if (cfg.time) console.time('startup');
+
// 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
@@ -63,6 +65,9 @@ try {
await page.goto(URL_CLAIM, { waitUntil: 'domcontentloaded' }); // 'domcontentloaded' faster than default 'load' https://playwright.dev/docs/api/class-page#page-goto
+ if (cfg.time) console.timeEnd('startup');
+ if (cfg.time) console.time('login');
+
// page.click('button:has-text("Accept All Cookies")').catch(_ => { }); // Not needed anymore since we set the cookie above. Clicking this did not always work since the message was animated in too slowly.
while (await page.locator('a[role="button"]:has-text("Sign In")').count() > 0) {
@@ -84,6 +89,9 @@ try {
console.error('Got a captcha during login (likely due to too many attempts)! You may solve it in the browser, get a new IP or try again in a few hours.');
await notify('epic-games: got captcha during login. Please check.');
}).catch(_ => { });
+ page.waitForSelector('h6:has-text("Incorrect response.")').then(async () => {
+ console.error('CAPTCHA!')
+ }).catch(_ => { });
// handle MFA, but don't await it
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 ...');
@@ -107,6 +115,8 @@ try {
user = await page.locator('#user span').first().innerHTML();
console.log(`Signed in as ${user}`);
db.data[user] ||= {};
+ if (cfg.time) console.timeEnd('login');
+ if (cfg.time) console.time('claim all games');
// Detect free games
const game_loc = page.locator('a:has(span:text-is("Free Now"))');
@@ -120,6 +130,7 @@ try {
console.log('Free games:', urls);
for (const url of urls) {
+ if (cfg.time) console.time('claim 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
@@ -174,6 +185,7 @@ try {
if (await iframe.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';
+ if (cfg.time) console.timeEnd('claim game');
continue;
}
@@ -190,6 +202,7 @@ try {
if (cfg.dryrun) {
console.log(' DRYRUN=1 -> Skip order!');
notify_game.status = 'skipped';
+ if (cfg.time) console.timeEnd('claim game');
continue;
}
@@ -230,7 +243,9 @@ try {
const p = screenshot(`${game_id}.png`);
if (!existsSync(p)) await page.screenshot({ path: p, fullPage: false }); // fullPage is quite long...
}
+ if (cfg.time) console.timeEnd('claim game');
}
+ if (cfg.time) console.timeEnd('claim all games');
} catch (error) {
console.error(error); // .toString()?
process.exitCode ||= 1;
From 59004cd4bb33fc1f7620a77cb4ee904272979118 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 31 Aug 2023 23:04:42 +0200
Subject: [PATCH 064/253] README: no docker updates: `git pull; npm install`
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index a5f7113..7cb4393 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,7 @@ Data (including json files with claimed games, codes to redeem, screenshots) is
2. Clone/download this repository and `cd` into it in a terminal
3. Run `npm install`
4. Run `pip install apprise` to install [apprise](https://github.com/caronc/apprise) if you want notifications
+5. To get updates: `git pull; npm install`
During `npm install` Playwright will download its Firefox to a cache in home ([doc](https://playwright.dev/docs/browsers#managing-browser-binaries)).
If you are missing some dependencies for the browser on your system, you can use `sudo npx playwright install firefox --with-deps`.
From 38975e811bac5133a0895ae450de7a7a5a3f2b9d Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 31 Aug 2023 23:08:29 +0200
Subject: [PATCH 065/253] eg: error: Incorrect repsonse for captcha!
---
epic-games.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/epic-games.js b/epic-games.js
index f23f229..4c0e7bd 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -90,7 +90,7 @@ try {
await notify('epic-games: got captcha during login. Please check.');
}).catch(_ => { });
page.waitForSelector('h6:has-text("Incorrect response.")').then(async () => {
- console.error('CAPTCHA!')
+ console.error('Incorrect repsonse for captcha!')
}).catch(_ => { });
// handle MFA, but don't await it
page.waitForURL('**/id/login/mfa**').then(async () => {
From d8e2093a0dc3f9c00b1b1cdd812395bbf179e231 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 5 Sep 2023 15:56:24 +0200
Subject: [PATCH 066/253] pg: INTERACTIVE=1 to confirm each claim or skip it
---
config.js | 1 +
prime-gaming.js | 5 ++++-
util.js | 1 +
3 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/config.js b/config.js
index e6a9438..2dd064d 100644
--- a/config.js
+++ b/config.js
@@ -9,6 +9,7 @@ export const cfg = {
record: process.env.RECORD == '1', // `recordHar` (network) + `recordVideo`
time: process.env.TIME == '1', // log duration of each step
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 },
width: Number(process.env.WIDTH) || 1280, // width of the opened browser
diff --git a/prime-gaming.js b/prime-gaming.js
index 659bd26..21b18fc 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -1,6 +1,6 @@
import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdated playwright-extra
import { authenticator } from 'otplib';
-import { resolve, jsonDb, datetime, stealth, filenamify, prompt, notify, html_game_list, handleSIGINT } from './util.js';
+import { resolve, jsonDb, datetime, stealth, filenamify, prompt, confirm, notify, html_game_list, handleSIGINT } from './util.js';
import { cfg } from './config.js';
const screenshot = (...a) => resolve(cfg.dir.screenshots, 'prime-gaming', ...a);
@@ -111,6 +111,7 @@ try {
const title = await (await card.$('.item-card-details__body__primary')).innerText();
console.log('Current free game:', title);
if (cfg.dryrun) continue;
+ if (cfg.interactive && !await confirm()) continue;
await (await card.$('button:has-text("Claim")')).click();
db.data[user][title] ||= { title, time: datetime(), store: 'internal' };
notify_games.push({ title, status: 'claimed', url: URL_CLAIM });
@@ -133,6 +134,7 @@ try {
await page.goto(url, { waitUntil: 'domcontentloaded' });
if (cfg.debug) await page.pause();
if (cfg.dryrun) continue;
+ if (cfg.interactive && !await confirm()) continue;
await Promise.any([page.click('button:has-text("Get game")'), page.click('button:has-text("Claim now")'), page.click('button:has-text("Complete Claim")'), page.waitForSelector('div:has-text("Link game account")')]); // waits for navigation
// TODO would be simpler than the below, but will block for linked stores without code
@@ -324,6 +326,7 @@ try {
console.log('Current DLC:', title);
if (cfg.debug) await page.pause();
if (cfg.dryrun) continue;
+ if (cfg.interactive && !await confirm()) continue;
db.data[user][title] ||= { title, time: datetime(), store: 'DLC', status: 'failed: need account linking' };
const notify_game = { title, url };
notify_games.push(notify_game); // status is updated below
diff --git a/util.js b/util.js
index 0b2dcbe..60ff4a5 100644
--- a/util.js
+++ b/util.js
@@ -98,6 +98,7 @@ const timeoutPlugin = timeout => enquirer => { // cancel prompt after timeout ms
enquirer.use(timeoutPlugin(cfg.login_timeout)); // TODO may not want to have this timeout for all prompts; better extend Prompt and add a timeout prompt option
// single prompt that just returns the non-empty value instead of an object
export const prompt = o => enquirer.prompt({name: 'name', type: 'input', message: 'Enter value', ...o}).then(r => r.name).catch(_ => {});
+export const confirm = o => prompt({type: 'confirm', message: 'Continue?', ...o})
// notifications via apprise CLI
import { exec } from 'child_process';
From d4ebdd091a478fb55cbde658f775f71baa6ab451 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 5 Sep 2023 15:59:17 +0200
Subject: [PATCH 067/253] pg: log current game in claim loop, not data loop
---
prime-gaming.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 21b18fc..3a02bbe 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -126,11 +126,11 @@ try {
const title = await card.locator('.item-card-details__body__primary').innerText();
const slug = await card.locator('a:has-text("Claim")').first().getAttribute('href');
const url = 'https://gaming.amazon.com' + slug.split('?')[0];
- console.log('Current free game:', title); //, url);
// await (await card.$('text=Claim')).click(); // goes to URL of game, no need to wait
external_info.push({title, url});
}
for (const {title, url} of external_info) {
+ console.log('Current free game:', title); //, url);
await page.goto(url, { waitUntil: 'domcontentloaded' });
if (cfg.debug) await page.pause();
if (cfg.dryrun) continue;
From b449ee4d7fd01fcc4d9054341235696fa9901a9b Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 5 Sep 2023 16:12:20 +0200
Subject: [PATCH 068/253] pg: redeem: legacygames success/error
---
prime-gaming.js | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 3a02bbe..1162496 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -246,7 +246,7 @@ try {
await page2.click('#nextButton');
redeem_action = 'redeemed?';
console.log(' Redeemed successfully? Please report your Response from above (if it is new) in https://github.com/vogler/free-games-claimer/issues/5');
- // db.data[user][title].status = 'claimed and redeemed';
+ db.data[user][title].status = 'claimed and redeemed?';
}
}
} else if (store == 'legacy games') {
@@ -255,9 +255,17 @@ try {
await page2.fill('[name=email_validate]', cfg.pg_email);
await page2.uncheck('[name=newsletter_sub]');
await page2.click('[type="submit"]');
- redeem_action = 'redeemed?';
- console.log(' Redeemed successfully? Please report problems in https://github.com/vogler/free-games-claimer/issues/5');
- db.data[user][title].status = 'claimed and redeemed';
+ try {
+ await page2.waitForResponse(r => r.url().startsWith('https://promo.legacygames.com/promotion-processing/order-management.php'));
+ await page2.waitForSelector('h2:has-text("Thanks for redeeming")');
+ redeem_action = 'redeemed';
+ db.data[user][title].status = 'claimed and redeemed';
+ } catch (error) {
+ console.error(' Got error', error);
+ redeem_action = 'redeemed?';
+ db.data[user][title].status = 'claimed and redeemed?';
+ console.log(' Redeemed successfully? Please report problems in https://github.com/vogler/free-games-claimer/issues/5');
+ }
} else {
console.error(` Redeem on ${store} not yet implemented!`);
}
From 97882c76c3f089c7c187866c42db1ad7864c4329 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 7 Sep 2023 13:25:14 +0200
Subject: [PATCH 069/253] eg: debug: window.screen
---
epic-games.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/epic-games.js b/epic-games.js
index 4c0e7bd..1ab81c6 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -50,6 +50,7 @@ if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
const page = context.pages().length ? context.pages()[0] : await context.newPage(); // should always exist
// console.debug('userAgent:', await page.evaluate(() => navigator.userAgent));
+if (cfg.debug) console.debug(await page.evaluate(() => window.screen));
if (cfg.record && cfg.debug) {
// const filter = _ => true;
const filter = r => r.url().includes('store.epicgames.com');
From c79f4cc40ff73670949c3b784ce8f7872e23ff24 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 7 Sep 2023 13:27:18 +0200
Subject: [PATCH 070/253] docker: migrate to new nodesource repo; node 19 -> 20
old node setup script had a warning to migrate with 60s pause during docker build
https://github.com/nodesource/distributions/wiki/How-to-migrate-to-the-new-repository
---
Dockerfile | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index 67a5e89..fd18159 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -10,8 +10,11 @@ ARG DEBIAN_FRONTEND=noninteractive
# Install up-to-date node & npm, deps for virtual screen & noVNC, firefox, pip for apprise.
RUN apt-get update \
- && apt-get install --no-install-recommends -y curl ca-certificates \
- && curl -fsSL https://deb.nodesource.com/setup_19.x | bash - \
+ && apt-get install --no-install-recommends -y curl ca-certificates gnupg \
+ && mkdir -p /etc/apt/keyrings \
+ && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
+ && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \
+ && apt-get update \
&& apt-get install --no-install-recommends -y \
nodejs \
xvfb \
@@ -42,6 +45,9 @@ RUN apt-get update \
/var/lib/apt/lists/* \
/var/tmp/*
+# RUN node --version
+# RUN npm --version
+
RUN ln -s /usr/share/novnc/vnc_auto.html /usr/share/novnc/index.html
RUN pip install apprise
From e30216c9b86a1d057442641b71547cad35267041 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 7 Sep 2023 13:30:49 +0200
Subject: [PATCH 071/253] eg: default resolution 1280x1280 -> 1920x1080, #183
---
Dockerfile | 4 ++--
config.js | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index fd18159..f686c55 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -90,8 +90,8 @@ EXPOSE 5900
EXPOSE 6080
# Configure Xvfb via environment variables:
-ENV WIDTH 1280
-ENV HEIGHT 1280
+ENV WIDTH 1920
+ENV HEIGHT 1080
ENV DEPTH 24
# Show browser instead of running headless
diff --git a/config.js b/config.js
index 2dd064d..e604d0f 100644
--- a/config.js
+++ b/config.js
@@ -12,8 +12,8 @@ export const cfg = {
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 },
- width: Number(process.env.WIDTH) || 1280, // width of the opened browser
- height: Number(process.env.HEIGHT) || 1280, // height of the opened browser
+ 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
login_timeout: (Number(process.env.LOGIN_TIMEOUT) || 180) * 1000, // higher timeout for login, will wait twice: prompt + wait for manual login
novnc_port: process.env.NOVNC_PORT, // running in docker if set
From d51f7310d97cfe57206eb82910a0aad5a181924d Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 7 Sep 2023 13:56:37 +0200
Subject: [PATCH 072/253] gog: fix #200, uncheck 'Marketing communications
through Trusted Partners'
---
gog.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/gog.js b/gog.js
index 4943ff8..fa82c8a 100644
--- a/gog.js
+++ b/gog.js
@@ -130,6 +130,7 @@ try {
if (status == 'claimed' && !cfg.gog_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();
}
}
From 44d712e333c6952d4aadc6a9a7b4865a4511227b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 11 Sep 2023 09:41:42 +0200
Subject: [PATCH 073/253] build(deps): bump actions/checkout from 3 to 4 (#207)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)
---
updated-dependencies:
- dependency-name: actions/checkout
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/docker.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index bf7fc06..f80650c 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -19,7 +19,7 @@ jobs:
steps:
-
name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
-
name: Set environment variables
run: |
From 30db49d9b92f997b75644b43ff3c28f7f2ccd27a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 18 Sep 2023 09:49:00 +0200
Subject: [PATCH 074/253] build(deps): bump docker/setup-qemu-action from 2 to
3 (#215)
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 2 to 3.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v2...v3)
---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/docker.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index f80650c..2ddc409 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -27,7 +27,7 @@ jobs:
echo "NOW=$(date -R)" >> $GITHUB_ENV # date -Iseconds; date +'%Y-%m-%dT%H:%M:%S'
-
name: Set up QEMU
- uses: docker/setup-qemu-action@v2
+ uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
From 6cdef56c094b860c633c40abb5e37b8f1ab21b60 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 18 Sep 2023 09:50:04 +0200
Subject: [PATCH 075/253] build(deps): bump docker/setup-buildx-action from 2
to 3 (#213)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2 to 3.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2...v3)
---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/docker.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 2ddc409..a493dc2 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -30,7 +30,7 @@ jobs:
uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
+ uses: docker/setup-buildx-action@v3
-
name: Login to Docker Hub
uses: docker/login-action@v2
From 0ba693eeb6dbe210ad829189d2142c2e125e27da Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 18 Sep 2023 09:51:23 +0200
Subject: [PATCH 076/253] build(deps): bump docker/login-action from 2 to 3
(#214)
Bumps [docker/login-action](https://github.com/docker/login-action) from 2 to 3.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v2...v3)
---
updated-dependencies:
- dependency-name: docker/login-action
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/docker.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index a493dc2..1cfe5b2 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -33,14 +33,14 @@ jobs:
uses: docker/setup-buildx-action@v3
-
name: Login to Docker Hub
- uses: docker/login-action@v2
+ uses: docker/login-action@v3
# if: ${{ secrets.DOCKERHUB_USERNAME && secrets.DOCKERHUB_TOKEN }}
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Login to GitHub Container Registry
- uses: docker/login-action@v2
+ uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
From 2a4da3f49cfec3e72ad543727bca50e4f136f3a5 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 18 Sep 2023 09:51:53 +0200
Subject: [PATCH 077/253] build(deps): bump docker/build-push-action from 4 to
5 (#212)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4 to 5.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v4...v5)
---
updated-dependencies:
- dependency-name: docker/build-push-action
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/docker.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 1cfe5b2..25470d5 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -47,7 +47,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
-
name: Build and push
- uses: docker/build-push-action@v4
+ uses: docker/build-push-action@v5
with:
context: .
push: true
From 1fbabbc0f2ad5f44e8265a98b7b6d949e2256e6a Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Mon, 18 Sep 2023 12:49:23 +0200
Subject: [PATCH 078/253] log path of recorded video
---
epic-games.js | 1 +
gog.js | 1 +
prime-gaming.js | 1 +
unrealengine.js | 1 +
4 files changed, 4 insertions(+)
diff --git a/epic-games.js b/epic-games.js
index 1ab81c6..56c1035 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -259,4 +259,5 @@ try {
}
}
if (cfg.debug) writeFileSync(path.resolve(cfg.dir.browser, 'cookies.json'), JSON.stringify(await context.cookies()));
+if (page.video()) console.log('Recorded video:', await page.video().path())
await context.close();
diff --git a/gog.js b/gog.js
index fa82c8a..6b02ee5 100644
--- a/gog.js
+++ b/gog.js
@@ -145,4 +145,5 @@ try {
notify(`gog (${user}):
${html_game_list(notify_games)}`);
}
}
+if (page.video()) console.log('Recorded video:', await page.video().path())
await context.close();
diff --git a/prime-gaming.js b/prime-gaming.js
index 1162496..1523e5f 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -387,4 +387,5 @@ try {
notify(`prime-gaming (${user}):
${html_game_list(notify_games)}`);
}
}
+if (page.video()) console.log('Recorded video:', await page.video().path())
await context.close();
diff --git a/unrealengine.js b/unrealengine.js
index 4370a85..a5d3282 100644
--- a/unrealengine.js
+++ b/unrealengine.js
@@ -199,4 +199,5 @@ try {
}
}
if (cfg.debug) writeFileSync(path.resolve(cfg.dir.browser, 'cookies.json'), JSON.stringify(await context.cookies()));
+if (page.video()) console.log('Recorded video:', await page.video().path())
await context.close();
From b748460b731bd565592e1c68c01a2c4eca9ae1a2 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Mon, 18 Sep 2023 12:58:20 +0200
Subject: [PATCH 079/253] disable Playwright's handleSIGINT and close context
ourselves to save recordings also on SIGINT
---
epic-games.js | 8 +++++---
gog.js | 12 +++++++-----
prime-gaming.js | 8 +++++---
unrealengine.js | 8 +++++---
util.js | 3 ++-
5 files changed, 24 insertions(+), 15 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index 56c1035..e58725b 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -14,8 +14,6 @@ console.log(datetime(), 'started checking epic-games');
const db = await jsonDb('epic-games.json', {});
-handleSIGINT();
-
if (cfg.time) console.time('startup');
// https://www.nopecha.com extension source from https://github.com/NopeCHA/NopeCHA/releases/tag/0.1.16
@@ -33,6 +31,7 @@ const context = await firefox.launchPersistentContext(cfg.dir.browser, {
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
args: [ // https://peter.sh/experiments/chromium-command-line-switches
// don't want to see bubble 'Restore pages? Chrome didn't shut down correctly.'
// '--restore-last-session', // does not apply for crash/killed
@@ -43,6 +42,8 @@ const context = await firefox.launchPersistentContext(cfg.dir.browser, {
// ignoreDefaultArgs: ['--enable-automation'], // remove default arg that shows the info bar with 'Chrome is being controlled by automated test software.'. Since Chromeium 106 this leads to show another info bar with 'You are using an unsupported command-line flag: --no-sandbox. Stability and security will suffer.'.
});
+handleSIGINT(context);
+
// Without stealth plugin, the website shows an hcaptcha on login with username/password and in the last step of claiming a game. It may have other heuristics like unsuccessful logins as well. After <6h (TBD) it resets to no captcha again. Getting a new IP also resets.
await stealth(context);
@@ -248,8 +249,9 @@ try {
}
if (cfg.time) console.timeEnd('claim all games');
} catch (error) {
- console.error(error); // .toString()?
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]}`);
} finally {
diff --git a/gog.js b/gog.js
index 6b02ee5..341ee6c 100644
--- a/gog.js
+++ b/gog.js
@@ -10,17 +10,18 @@ console.log(datetime(), 'started checking gog');
const db = await jsonDb('gog.json', {});
-handleSIGINT();
-
// https://playwright.dev/docs/auth#multi-factor-authentication
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
- // recordHar: { path: './data/gog.har' }, // https://toolbox.googleapps.com/apps/har_analyzer/
- // recordVideo: { dir: './data/videos' }, // console.log(await page.video().path());
+ 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
});
+handleSIGINT(context);
+
if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
const page = context.pages().length ? context.pages()[0] : await context.newPage(); // should always exist
@@ -135,8 +136,9 @@ try {
}
}
} catch (error) {
- console.error(error); // .toString()?
process.exitCode ||= 1;
+ console.error('--- Exception:');
+ console.error(error); // .toString()?
if (error.message && process.exitCode != 130)
notify(`gog failed: ${error.message.split('\n')[0]}`);
} finally {
diff --git a/prime-gaming.js b/prime-gaming.js
index 1523e5f..a5df36d 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -12,8 +12,6 @@ console.log(datetime(), 'started checking prime-gaming');
const db = await jsonDb('prime-gaming.json', {});
-handleSIGINT();
-
// https://playwright.dev/docs/auth#multi-factor-authentication
const context = await firefox.launchPersistentContext(cfg.dir.browser, {
headless: cfg.headless,
@@ -21,8 +19,11 @@ const context = await firefox.launchPersistentContext(cfg.dir.browser, {
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/pg-${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
});
+handleSIGINT(context);
+
// TODO test if needed
await stealth(context);
@@ -377,8 +378,9 @@ try {
console.log('DLC: Unlinked accounts:', dlc_unlinked);
}
} catch (error) {
- console.error(error); // .toString()?
process.exitCode ||= 1;
+ console.error('--- Exception:');
+ console.error(error); // .toString()?
if (error.message && process.exitCode != 130)
notify(`prime-gaming failed: ${error.message.split('\n')[0]}`);
} finally {
diff --git a/unrealengine.js b/unrealengine.js
index a5d3282..6523503 100644
--- a/unrealengine.js
+++ b/unrealengine.js
@@ -17,8 +17,6 @@ console.log(datetime(), 'started checking unrealengine');
const db = await jsonDb('unrealengine.json', {});
-handleSIGINT();
-
// https://playwright.dev/docs/auth#multi-factor-authentication
const context = await firefox.launchPersistentContext(cfg.dir.browser, {
headless: cfg.headless,
@@ -28,8 +26,11 @@ const context = await firefox.launchPersistentContext(cfg.dir.browser, {
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/ue-${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
});
+handleSIGINT(context);
+
await stealth(context);
if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
@@ -188,8 +189,9 @@ try {
console.log('Done');
}
} catch (error) {
- console.error(error); // .toString()?
process.exitCode ||= 1;
+ console.error('--- Exception:');
+ console.error(error); // .toString()?
if (error.message && process.exitCode != 130)
notify(`unrealengine failed: ${error.message.split('\n')[0]}`);
} finally {
diff --git a/util.js b/util.js
index 60ff4a5..94cccfd 100644
--- a/util.js
+++ b/util.js
@@ -27,9 +27,10 @@ export const datetimeUTC = (d = new Date()) => d.toISOString().replace('T', ' ')
export const datetime = (d = new Date()) => datetimeUTC(new Date(d.getTime() - d.getTimezoneOffset() * 60000));
export const filenamify = s => s.replaceAll(':', '.').replace(/[^a-z0-9 _\-.]/gi, '_'); // alternative: https://www.npmjs.com/package/filenamify - On Unix-like systems, / is reserved. On Windows, <>:"/\|?* along with trailing periods are reserved.
-export const handleSIGINT = () => process.on('SIGINT', () => { // e.g. when killed by Ctrl-C
+export const handleSIGINT = (context = null) => process.on('SIGINT', async () => { // e.g. when killed by Ctrl-C
console.error('\nInterrupted by SIGINT. Exit!'); // Exception shows where the script was:\n'); // killed before catch in docker...
process.exitCode = 130; // 128+SIGINT to indicate to parent that process was killed
+ if (context) await context.close(); // in order to save recordings also on SIGINT, we need to disable Playwright's handleSIGINT and close the context ourselves
});
// stealth with playwright: https://github.com/berstend/puppeteer-extra/issues/454#issuecomment-917437212
From 13b2917dd03c675d6ab9853fe188f15c52782688 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Mon, 18 Sep 2023 13:03:04 +0200
Subject: [PATCH 080/253] workaround for recordVideo broken in Playwright 1.36
Recording videos with relative path as in docs was broken for Firefox.
Issue: https://github.com/microsoft/playwright/issues/27086
Can be reverted after PR is available in release: https://github.com/microsoft/playwright/pull/27099
---
epic-games.js | 2 +-
gog.js | 3 ++-
prime-gaming.js | 3 ++-
unrealengine.js | 2 +-
4 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index e58725b..976ce07 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -29,7 +29,7 @@ const context = await firefox.launchPersistentContext(cfg.dir.browser, {
// 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
- 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
+ recordVideo: cfg.record ? { dir: path.resolve('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
args: [ // https://peter.sh/experiments/chromium-command-line-switches
diff --git a/gog.js b/gog.js
index 341ee6c..e41eed7 100644
--- a/gog.js
+++ b/gog.js
@@ -1,4 +1,5 @@
import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdated playwright-extra
+import path from 'path';
import { resolve, jsonDb, datetime, filenamify, prompt, notify, html_game_list, handleSIGINT } from './util.js';
import { cfg } from './config.js';
@@ -15,7 +16,7 @@ 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
- 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
+ recordVideo: cfg.record ? { dir: path.resolve('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
});
diff --git a/prime-gaming.js b/prime-gaming.js
index a5df36d..3816777 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -1,5 +1,6 @@
import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdated playwright-extra
import { authenticator } from 'otplib';
+import path from 'path';
import { resolve, jsonDb, datetime, stealth, filenamify, prompt, confirm, notify, html_game_list, handleSIGINT } from './util.js';
import { cfg } from './config.js';
@@ -17,7 +18,7 @@ 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
- 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
+ recordVideo: cfg.record ? { dir: path.resolve('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/pg-${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
});
diff --git a/unrealengine.js b/unrealengine.js
index 6523503..55d4e58 100644
--- a/unrealengine.js
+++ b/unrealengine.js
@@ -24,7 +24,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 for firefox: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:106.0) Gecko/20100101 Firefox/106.0
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
+ recordVideo: cfg.record ? { dir: path.resolve('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/ue-${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
});
From 840d35c2d22bccbc37720c9096d3d773091b30ef Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Mon, 18 Sep 2023 13:54:40 +0200
Subject: [PATCH 081/253] =?UTF-8?q?ncu=20-u:=20playwright-firefox=20^1.37.?=
=?UTF-8?q?1=20=20=E2=86=92=20=20^1.38.0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Dockerfile | 3 ++-
package-lock.json | 30 +++++++++++++++---------------
package.json | 2 +-
3 files changed, 18 insertions(+), 17 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index f686c55..7a291b3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -55,9 +55,10 @@ WORKDIR /fgc
COPY package*.json ./
# Playwright installs patched firefox to ~/.cache/ms-playwright/firefox-*
-# Requires some system deps to run (see install-deps above).
+# Requires some system deps to run (see inlined install-deps above).
RUN npm install
# Old: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD + install firefox (had to be done after `npm install` to get the correct version). Now: playwright-firefox as npm dep and `npm install` will only install that.
+# From 1.38 Playwright will no longer install browser automatically for playwright, but apparently still for playwright-firefox: https://github.com/microsoft/playwright/releases/tag/v1.38.0
# RUN npx playwright install firefox
COPY . .
diff --git a/package-lock.json b/package-lock.json
index b40dc0e..8078e6d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,7 +14,7 @@
"enquirer": "^2.4.1",
"lowdb": "^6.0.1",
"otplib": "^12.0.1",
- "playwright-firefox": "^1.37.1",
+ "playwright-firefox": "^1.38.0",
"puppeteer-extra-plugin-stealth": "^2.11.2"
}
},
@@ -448,9 +448,9 @@
}
},
"node_modules/playwright-core": {
- "version": "1.37.1",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.37.1.tgz",
- "integrity": "sha512-17EuQxlSIYCmEMwzMqusJ2ztDgJePjrbttaefgdsiqeLWidjYz9BxXaTaZWxH1J95SHGk6tjE+dwgWILJoUZfA==",
+ "version": "1.38.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.0.tgz",
+ "integrity": "sha512-f8z1y8J9zvmHoEhKgspmCvOExF2XdcxMW8jNRuX4vkQFrzV4MlZ55iwb5QeyiFQgOFCUolXiRHgpjSEnqvO48g==",
"bin": {
"playwright-core": "cli.js"
},
@@ -459,12 +459,12 @@
}
},
"node_modules/playwright-firefox": {
- "version": "1.37.1",
- "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.37.1.tgz",
- "integrity": "sha512-I8QScyW+hjGltywqLNh3Y1W96/3x70el9wNneuI34l3uVhiCRt9Co27+kiL+UlA1V8MTzaMere3ONQ8lGeut5w==",
+ "version": "1.38.0",
+ "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.38.0.tgz",
+ "integrity": "sha512-uNXdvj17JHbKir/EmdLtYEHkzI0ttFMX/3+HO/TW5z1hRmN5CydDNINm9xL/0AwvFSa5unZPM7S7+mUa3EiniA==",
"hasInstallScript": true,
"dependencies": {
- "playwright-core": "1.37.1"
+ "playwright-core": "1.38.0"
},
"bin": {
"playwright": "cli.js"
@@ -1032,16 +1032,16 @@
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
"playwright-core": {
- "version": "1.37.1",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.37.1.tgz",
- "integrity": "sha512-17EuQxlSIYCmEMwzMqusJ2ztDgJePjrbttaefgdsiqeLWidjYz9BxXaTaZWxH1J95SHGk6tjE+dwgWILJoUZfA=="
+ "version": "1.38.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.0.tgz",
+ "integrity": "sha512-f8z1y8J9zvmHoEhKgspmCvOExF2XdcxMW8jNRuX4vkQFrzV4MlZ55iwb5QeyiFQgOFCUolXiRHgpjSEnqvO48g=="
},
"playwright-firefox": {
- "version": "1.37.1",
- "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.37.1.tgz",
- "integrity": "sha512-I8QScyW+hjGltywqLNh3Y1W96/3x70el9wNneuI34l3uVhiCRt9Co27+kiL+UlA1V8MTzaMere3ONQ8lGeut5w==",
+ "version": "1.38.0",
+ "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.38.0.tgz",
+ "integrity": "sha512-uNXdvj17JHbKir/EmdLtYEHkzI0ttFMX/3+HO/TW5z1hRmN5CydDNINm9xL/0AwvFSa5unZPM7S7+mUa3EiniA==",
"requires": {
- "playwright-core": "1.37.1"
+ "playwright-core": "1.38.0"
}
},
"puppeteer-extra-plugin": {
diff --git a/package.json b/package.json
index 6179433..9a91f7f 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
"enquirer": "^2.4.1",
"lowdb": "^6.0.1",
"otplib": "^12.0.1",
- "playwright-firefox": "^1.37.1",
+ "playwright-firefox": "^1.38.0",
"puppeteer-extra-plugin-stealth": "^2.11.2"
},
"repository": {
From 41f1f9550964563df64e6535b0c2752c944c0c46 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Mon, 18 Sep 2023 14:02:15 +0200
Subject: [PATCH 082/253] PW: replace deprecated type() with
pressSequentially() for OTPs
---
epic-games.js | 4 ++--
gog.js | 2 +-
prime-gaming.js | 2 +-
unrealengine.js | 2 +-
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index 976ce07..16984a3 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -99,7 +99,7 @@ try {
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
- await page.type('input[name="code-input-0"]', otp.toString());
+ await page.locator('input[name="code-input-0"]').pressSequentially(otp.toString());
await page.click('button[type="submit"]');
}).catch(_ => { });
} else {
@@ -196,7 +196,7 @@ try {
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('input.payment-pin-code__input').first().pressSequentially(cfg.eg_parentalpin);
await iframe.locator('button:has-text("Continue")').click({ delay: 11 });
}).catch(_ => { });
diff --git a/gog.js b/gog.js
index e41eed7..be4514d 100644
--- a/gog.js
+++ b/gog.js
@@ -61,7 +61,7 @@ try {
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').type(otp.toString(), {delay: 10});
+ 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(_ => { });
diff --git a/prime-gaming.js b/prime-gaming.js
index 3816777..8fcfa2b 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -68,7 +68,7 @@ try {
console.log('Two-Step Verification - enter the One Time Password (OTP), e.g. generated by your Authenticator App');
await page.check('[name=rememberDevice]');
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 page.type('input[name=otpCode]', otp.toString());
+ await page.locator('input[name=otpCode]').pressSequentially(otp.toString());
await page.click('input[type="submit"]');
}).catch(_ => { });
} else {
diff --git a/unrealengine.js b/unrealengine.js
index 55d4e58..f7ee6fa 100644
--- a/unrealengine.js
+++ b/unrealengine.js
@@ -72,7 +72,7 @@ try {
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
- await page.type('input[name="code-input-0"]', otp.toString());
+ await page.locator('input[name="code-input-0"]').pressSequentially(otp.toString());
await page.click('button[type="submit"]');
}).catch(_ => { });
} else {
From 9ebd15f20437d93da0dc8c41f2dc8c7c08171268 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sat, 23 Sep 2023 11:01:31 +0200
Subject: [PATCH 083/253] fix log text if $VNC_PASSWORD is set, fixes #223
---
docker-entrypoint.sh | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
index 2b5ddee..0eea025 100755
--- a/docker-entrypoint.sh
+++ b/docker-entrypoint.sh
@@ -27,10 +27,15 @@ rm -f /tmp/.X1-lock
export DISPLAY=:1 # need to export this, otherwise playwright complains with 'Looks like you launched a headed browser without having a XServer running.'
Xvfb $DISPLAY -ac -screen 0 "${WIDTH}x${HEIGHT}x${DEPTH}" &
echo "Xvfb display server created screen with resolution ${WIDTH}x${HEIGHT}"
-pw="-nopw"
-[ -z "$VNC_PASSWORD" ] || pw="-passwd $VNC_PASSWORD"
+if [ -z "$VNC_PASSWORD" ]; then
+ pw="-nopw"
+ pwt="no password!"
+else
+ pw="-passwd $VNC_PASSWORD"
+ pwt="with password"
+fi
x11vnc -display $DISPLAY -forever -shared -rfbport $VNC_PORT -bg $pw 2>/dev/null 1>&2
-echo "VNC is running on port $VNC_PORT (no password!)"
+echo "VNC is running on port $VNC_PORT ($pwt)"
websockify -D --web "/usr/share/novnc/" $NOVNC_PORT "localhost:$VNC_PORT" 2>/dev/null 1>&2 &
echo "noVNC (VNC via browser) is running on http://localhost:$NOVNC_PORT"
echo
From d318a57be15f0e9608c81541710f7ff042d2242a Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Mon, 25 Sep 2023 09:35:30 +0200
Subject: [PATCH 084/253] =?UTF-8?q?ncu=20-u:=20playwright-firefox=20^1.38.?=
=?UTF-8?q?0=20=20=E2=86=92=20=20^1.38.1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
package-lock.json | 30 +++++++++++++++---------------
package.json | 2 +-
2 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 8078e6d..4cb3c34 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,7 +14,7 @@
"enquirer": "^2.4.1",
"lowdb": "^6.0.1",
"otplib": "^12.0.1",
- "playwright-firefox": "^1.38.0",
+ "playwright-firefox": "^1.38.1",
"puppeteer-extra-plugin-stealth": "^2.11.2"
}
},
@@ -448,9 +448,9 @@
}
},
"node_modules/playwright-core": {
- "version": "1.38.0",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.0.tgz",
- "integrity": "sha512-f8z1y8J9zvmHoEhKgspmCvOExF2XdcxMW8jNRuX4vkQFrzV4MlZ55iwb5QeyiFQgOFCUolXiRHgpjSEnqvO48g==",
+ "version": "1.38.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz",
+ "integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==",
"bin": {
"playwright-core": "cli.js"
},
@@ -459,12 +459,12 @@
}
},
"node_modules/playwright-firefox": {
- "version": "1.38.0",
- "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.38.0.tgz",
- "integrity": "sha512-uNXdvj17JHbKir/EmdLtYEHkzI0ttFMX/3+HO/TW5z1hRmN5CydDNINm9xL/0AwvFSa5unZPM7S7+mUa3EiniA==",
+ "version": "1.38.1",
+ "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.38.1.tgz",
+ "integrity": "sha512-IhOwSlhz8wpJnuzTCxZSQWgLGb+VX/8FjoUE7HuARbLXNPYM02vvbdSs+MxVEDgN9k2/i0QC4BrUMstP2VtYEg==",
"hasInstallScript": true,
"dependencies": {
- "playwright-core": "1.38.0"
+ "playwright-core": "1.38.1"
},
"bin": {
"playwright": "cli.js"
@@ -1032,16 +1032,16 @@
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
"playwright-core": {
- "version": "1.38.0",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.0.tgz",
- "integrity": "sha512-f8z1y8J9zvmHoEhKgspmCvOExF2XdcxMW8jNRuX4vkQFrzV4MlZ55iwb5QeyiFQgOFCUolXiRHgpjSEnqvO48g=="
+ "version": "1.38.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz",
+ "integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg=="
},
"playwright-firefox": {
- "version": "1.38.0",
- "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.38.0.tgz",
- "integrity": "sha512-uNXdvj17JHbKir/EmdLtYEHkzI0ttFMX/3+HO/TW5z1hRmN5CydDNINm9xL/0AwvFSa5unZPM7S7+mUa3EiniA==",
+ "version": "1.38.1",
+ "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.38.1.tgz",
+ "integrity": "sha512-IhOwSlhz8wpJnuzTCxZSQWgLGb+VX/8FjoUE7HuARbLXNPYM02vvbdSs+MxVEDgN9k2/i0QC4BrUMstP2VtYEg==",
"requires": {
- "playwright-core": "1.38.0"
+ "playwright-core": "1.38.1"
}
},
"puppeteer-extra-plugin": {
diff --git a/package.json b/package.json
index 9a91f7f..d447382 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
"enquirer": "^2.4.1",
"lowdb": "^6.0.1",
"otplib": "^12.0.1",
- "playwright-firefox": "^1.38.0",
+ "playwright-firefox": "^1.38.1",
"puppeteer-extra-plugin-stealth": "^2.11.2"
},
"repository": {
From 0040d9d96c699d515abe2ff69a5fdf81f32bee4f Mon Sep 17 00:00:00 2001
From: 4n4n4s
Date: Tue, 26 Sep 2023 19:12:09 +0000
Subject: [PATCH 085/253] Sonarqube support with ESLint
---
.eslintrc.cjs | 27 +
.github/workflows/sonar.yml | 28 +
package-lock.json | 1518 +++++++++++++++++++++++++++++++++++
package.json | 5 +-
sonar-project.properties | 9 +
5 files changed, 1586 insertions(+), 1 deletion(-)
create mode 100644 .eslintrc.cjs
create mode 100644 .github/workflows/sonar.yml
create mode 100644 sonar-project.properties
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
new file mode 100644
index 0000000..c2d8ac6
--- /dev/null
+++ b/.eslintrc.cjs
@@ -0,0 +1,27 @@
+module.exports = {
+ 'env': {
+ 'es2021': true,
+ 'node': true,
+ },
+ "extends": "eslint:recommended",
+ 'overrides': [
+ {
+ 'env': {
+ 'node': true,
+ },
+ 'files': [
+ '.eslintrc.{js,cjs}',
+ ],
+ 'parserOptions': {
+ 'sourceType': 'script',
+ },
+ },
+ ],
+ 'parserOptions': {
+ 'ecmaVersion': 'latest',
+ 'sourceType': 'module',
+ },
+ 'rules': {
+ 'semi': 'error',
+ },
+};
diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml
new file mode 100644
index 0000000..62067f3
--- /dev/null
+++ b/.github/workflows/sonar.yml
@@ -0,0 +1,28 @@
+on:
+ # Trigger analysis when pushing in master or pull requests, and when creating a pull request.
+ push:
+ branches:
+ - main
+ pull_request:
+ types: [opened, synchronize, reopened]
+name: Sonar
+jobs:
+ sonarcloud:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ # Disabling shallow clone is recommended for improving relevancy of reporting
+ fetch-depth: 0
+ - name: Install modules
+ run: npm install -g eslint
+ - name: Run ESLint
+ continue-on-error: true
+ run: eslint . --ext .js,.ts -f json -o eslint_report.json
+ - name: Fix ESLint paths
+ run: sed -i 's+/home/runner/work/free-games-claimer/free-games-claimer+/github/workspace+g' eslint_report.json
+ - name: SonarCloud Scan
+ uses: sonarsource/sonarcloud-github-action@master
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
diff --git a/package-lock.json b/package-lock.json
index 8078e6d..4f249f1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,6 +16,142 @@
"otplib": "^12.0.1",
"playwright-firefox": "^1.38.0",
"puppeteer-extra-plugin-stealth": "^2.11.2"
+ },
+ "devDependencies": {
+ "eslint": "^8.50.0"
+ }
+ },
+ "node_modules/@aashutoshrathi/word-wrap": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
+ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.8.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.1.tgz",
+ "integrity": "sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==",
+ "dev": true,
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz",
+ "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz",
+ "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.11.11",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",
+ "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==",
+ "dev": true,
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^1.2.1",
+ "debug": "^4.1.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+ "dev": true
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
}
},
"node_modules/@otplib/core": {
@@ -73,6 +209,43 @@
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA=="
},
+ "node_modules/acorn": {
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
+ "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
"node_modules/ansi-colors": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
@@ -89,6 +262,27 @@
"node": ">=8"
}
},
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
"node_modules/arr-union": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
@@ -111,6 +305,31 @@
"concat-map": "0.0.1"
}
},
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
"node_modules/clone-deep": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz",
@@ -126,6 +345,24 @@
"node": ">=0.10.0"
}
},
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -177,6 +414,12 @@
}
}
},
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
"node_modules/deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
@@ -185,6 +428,18 @@
"node": ">=0.10.0"
}
},
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/dotenv": {
"version": "16.3.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
@@ -208,6 +463,234 @@
"node": ">=8.6"
}
},
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz",
+ "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.2",
+ "@eslint/js": "8.50.0",
+ "@humanwhocodes/config-array": "^0.11.11",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+ "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "node_modules/fastq": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+ "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz",
+ "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.2.7",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.2.9",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz",
+ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
+ "dev": true
+ },
"node_modules/for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
@@ -264,11 +747,87 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "13.22.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz",
+ "integrity": "sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
},
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.2.4",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
+ "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -296,6 +855,36 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-plain-object": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
@@ -320,6 +909,36 @@
"node": ">=0.10.0"
}
},
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
"node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
@@ -331,6 +950,15 @@
"graceful-fs": "^4.1.6"
}
},
+ "node_modules/keyv": {
+ "version": "4.5.3",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz",
+ "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==",
+ "dev": true,
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
"node_modules/kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
@@ -350,6 +978,40 @@
"node": ">=0.10.0"
}
},
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
"node_modules/lowdb": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/lowdb/-/lowdb-6.0.1.tgz",
@@ -413,6 +1075,12 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -421,6 +1089,23 @@
"wrappy": "1"
}
},
+ "node_modules/optionator": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
+ "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+ "dev": true,
+ "dependencies": {
+ "@aashutoshrathi/word-wrap": "^1.2.3",
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/otplib": {
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/otplib/-/otplib-12.0.1.tgz",
@@ -431,6 +1116,57 @@
"@otplib/preset-v11": "^12.0.1"
}
},
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -473,6 +1209,24 @@
"node": ">=16"
}
},
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
+ "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/puppeteer-extra-plugin": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/puppeteer-extra-plugin/-/puppeteer-extra-plugin-3.2.3.tgz",
@@ -575,6 +1329,45 @@
}
}
},
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@@ -589,6 +1382,29 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
"node_modules/shallow-clone": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz",
@@ -663,6 +1479,36 @@
"node": ">=8"
}
},
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
+ },
"node_modules/thirty-two": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz",
@@ -671,6 +1517,30 @@
"node": ">=0.2.6"
}
},
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
@@ -679,6 +1549,15 @@
"node": ">= 10.0.0"
}
},
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -697,9 +1576,114 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
}
},
"dependencies": {
+ "@aashutoshrathi/word-wrap": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
+ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
+ "dev": true
+ },
+ "@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^3.3.0"
+ }
+ },
+ "@eslint-community/regexpp": {
+ "version": "4.8.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.1.tgz",
+ "integrity": "sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==",
+ "dev": true
+ },
+ "@eslint/eslintrc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz",
+ "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ }
+ },
+ "@eslint/js": {
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz",
+ "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==",
+ "dev": true
+ },
+ "@humanwhocodes/config-array": {
+ "version": "0.11.11",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",
+ "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==",
+ "dev": true,
+ "requires": {
+ "@humanwhocodes/object-schema": "^1.2.1",
+ "debug": "^4.1.1",
+ "minimatch": "^3.0.5"
+ }
+ },
+ "@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true
+ },
+ "@humanwhocodes/object-schema": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+ "dev": true
+ },
+ "@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ }
+ },
+ "@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true
+ },
+ "@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ }
+ },
"@otplib/core": {
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/@otplib/core/-/core-12.0.1.tgz",
@@ -755,6 +1739,31 @@
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA=="
},
+ "acorn": {
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
+ "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+ "dev": true
+ },
+ "acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
"ansi-colors": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
@@ -765,6 +1774,21 @@
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
"arr-union": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
@@ -784,6 +1808,22 @@
"concat-map": "0.0.1"
}
},
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
"clone-deep": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz",
@@ -796,6 +1836,21 @@
"shallow-clone": "^0.1.2"
}
},
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -827,11 +1882,26 @@
"ms": "2.1.2"
}
},
+ "deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
"deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
},
+ "doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
"dotenv": {
"version": "16.3.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
@@ -846,6 +1916,177 @@
"strip-ansi": "^6.0.1"
}
},
+ "escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true
+ },
+ "eslint": {
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz",
+ "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==",
+ "dev": true,
+ "requires": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.2",
+ "@eslint/js": "8.50.0",
+ "@humanwhocodes/config-array": "^0.11.11",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ }
+ },
+ "eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true
+ },
+ "espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "requires": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ }
+ },
+ "esquery": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+ "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.1.0"
+ }
+ },
+ "esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.2.0"
+ }
+ },
+ "estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true
+ },
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "fastq": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+ "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+ "dev": true,
+ "requires": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^3.0.4"
+ }
+ },
+ "find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "flat-cache": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz",
+ "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==",
+ "dev": true,
+ "requires": {
+ "flatted": "^3.2.7",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ }
+ },
+ "flatted": {
+ "version": "3.2.9",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz",
+ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
+ "dev": true
+ },
"for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
@@ -887,11 +2128,63 @@
"path-is-absolute": "^1.0.0"
}
},
+ "glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.3"
+ }
+ },
+ "globals": {
+ "version": "13.22.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz",
+ "integrity": "sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.20.2"
+ }
+ },
"graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
},
+ "graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "ignore": {
+ "version": "5.2.4",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
+ "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+ "dev": true
+ },
+ "import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true
+ },
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -916,6 +2209,27 @@
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
"integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="
},
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true
+ },
"is-plain-object": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
@@ -934,6 +2248,33 @@
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
"integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="
},
+ "js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "requires": {
+ "argparse": "^2.0.1"
+ }
+ },
+ "json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
"jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
@@ -943,6 +2284,15 @@
"universalify": "^2.0.0"
}
},
+ "keyv": {
+ "version": "4.5.3",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz",
+ "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==",
+ "dev": true,
+ "requires": {
+ "json-buffer": "3.0.1"
+ }
+ },
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
@@ -956,6 +2306,31 @@
"resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
"integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ=="
},
+ "levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ }
+ },
+ "locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^5.0.0"
+ }
+ },
+ "lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
"lowdb": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/lowdb/-/lowdb-6.0.1.tgz",
@@ -1003,6 +2378,12 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -1011,6 +2392,20 @@
"wrappy": "1"
}
},
+ "optionator": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
+ "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+ "dev": true,
+ "requires": {
+ "@aashutoshrathi/word-wrap": "^1.2.3",
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0"
+ }
+ },
"otplib": {
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/otplib/-/otplib-12.0.1.tgz",
@@ -1021,6 +2416,39 @@
"@otplib/preset-v11": "^12.0.1"
}
},
+ "p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "requires": {
+ "yocto-queue": "^0.1.0"
+ }
+ },
+ "p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^3.0.2"
+ }
+ },
+ "parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -1044,6 +2472,18 @@
"playwright-core": "1.38.0"
}
},
+ "prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true
+ },
+ "punycode": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
+ "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+ "dev": true
+ },
"puppeteer-extra-plugin": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/puppeteer-extra-plugin/-/puppeteer-extra-plugin-3.2.3.tgz",
@@ -1086,6 +2526,24 @@
"puppeteer-extra-plugin-user-data-dir": "^2.4.1"
}
},
+ "queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true
+ },
+ "resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true
+ },
+ "reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true
+ },
"rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@@ -1094,6 +2552,15 @@
"glob": "^7.1.3"
}
},
+ "run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "requires": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
"shallow-clone": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz",
@@ -1146,16 +2613,61 @@
"ansi-regex": "^5.0.1"
}
},
+ "strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
+ },
"thirty-two": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz",
"integrity": "sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA=="
},
+ "type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1"
+ }
+ },
+ "type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true
+ },
"universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ=="
},
+ "uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -1168,6 +2680,12 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+ },
+ "yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true
}
}
}
diff --git a/package.json b/package.json
index 9a91f7f..c10bb1c 100644
--- a/package.json
+++ b/package.json
@@ -23,5 +23,8 @@
"url": "https://github.com/vogler/free-games-claimer.git"
},
"author": "Ralf Vogler",
- "license": "MIT"
+ "license": "MIT",
+ "devDependencies": {
+ "eslint": "^8.50.0"
+ }
}
diff --git a/sonar-project.properties b/sonar-project.properties
new file mode 100644
index 0000000..677d6b3
--- /dev/null
+++ b/sonar-project.properties
@@ -0,0 +1,9 @@
+sonar.organization=vogler
+sonar.projectKey=vogler_free-games-claimer
+
+# relative paths to source directories. More details and properties are described
+# in https://sonarcloud.io/documentation/project-administration/narrowing-the-focus/
+sonar.sources=.
+
+#Eslint issues
+sonar.eslint.reportPaths = eslint_report.json
From 9fc68b881fd6e786f50f2be10fefdab4e2061096 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 10 Oct 2023 22:01:23 +0200
Subject: [PATCH 086/253] pg: legacygames: don't wait for response, just text
---
prime-gaming.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 8fcfa2b..66a9594 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -258,7 +258,7 @@ try {
await page2.uncheck('[name=newsletter_sub]');
await page2.click('[type="submit"]');
try {
- await page2.waitForResponse(r => r.url().startsWith('https://promo.legacygames.com/promotion-processing/order-management.php'));
+ // await page2.waitForResponse(r => r.url().startsWith('https://promo.legacygames.com/promotion-processing/order-management.php')); // status code 302
await page2.waitForSelector('h2:has-text("Thanks for redeeming")');
redeem_action = 'redeemed';
db.data[user][title].status = 'claimed and redeemed';
From a8ab989a7ff6c156a241c65f736b7aefb343850f Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 10 Oct 2023 22:12:59 +0200
Subject: [PATCH 087/253] pg: external: check for 'Link account' besides 'Link
game account'
---
prime-gaming.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 66a9594..cf2b532 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -162,7 +162,8 @@ try {
db.data[user][title] ||= { title, time: datetime(), url, store };
const notify_game = { title, url };
notify_games.push(notify_game); // status is updated below
- if (await page.locator('div:has-text("Link game account")').count()) {
+ 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()) {
console.error(' Account linking is required to claim this offer!');
notify_game.status = `failed: need account linking for ${store}`;
db.data[user][title].status = 'failed: need account linking';
From ad2301c3fd50766153a4de4b40eee4c920180444 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 10 Oct 2023 22:42:38 +0200
Subject: [PATCH 088/253] pg: eg: fix detecting successful claim
---
prime-gaming.js | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index cf2b532..89691b8 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -137,7 +137,7 @@ try {
if (cfg.debug) await page.pause();
if (cfg.dryrun) continue;
if (cfg.interactive && !await confirm()) continue;
- await Promise.any([page.click('button:has-text("Get game")'), page.click('button:has-text("Claim now")'), page.click('button:has-text("Complete Claim")'), page.waitForSelector('div:has-text("Link game account")')]); // waits for navigation
+ await Promise.any([page.click('button:has-text("Get game")'), page.click('button:has-text("Claim now")'), page.click('button:has-text("Complete Claim")'), page.waitForSelector('div:has-text("Link game account")'), page.waitForSelector('.thank-you-title:has-text("Success")')]); // waits for navigation
// TODO would be simpler than the below, but will block for linked stores without code
// const redeem_text = await page.textContent('text=/ code on /'); // FAQ: How do I redeem my code?
@@ -167,6 +167,11 @@ try {
console.error(' Account linking is required to claim this offer!');
notify_game.status = `failed: need account linking for ${store}`;
db.data[user][title].status = 'failed: need account linking';
+ // await page.pause();
+ // await page.click('[data-a-target="LinkAccountModal"] [data-a-target="LinkAccountButton"]');
+ // TODO login for epic games also needed if already logged in
+ // wait for https://www.epicgames.com/id/authorize?redirect_uri=https%3A%2F%2Fservice.link.amazon.gg...
+ // await page.click('button[aria-label="Allow"]');
} else {
db.data[user][title].status = 'claimed';
// print code if there is one
@@ -213,19 +218,19 @@ try {
console.error(' Code was not found!');
} else { // TODO not logged in? need valid unused code to test.
redeem_action = 'redeemed?';
- console.log(' Redeemed successfully? Please report your Responses (if new) in https://github.com/vogler/free-games-claimer/issues/5');
+ // console.log(' Redeemed successfully? Please report your Responses (if new) in https://github.com/vogler/free-games-claimer/issues/5');
console.debug(` Response 1: ${r1t}`);
// then after the click on Redeem there is a POST request which should return {} if claimed successfully
const r2 = page2.waitForResponse(r => r.request().method() == 'POST' && r.url().startsWith('https://redeem.gog.com/'));
await page2.click('[type="submit"]'); // click Redeem
const r2t = await (await r2).text();
- console.debug(` Response 2: ${r2t}`);
if (r2t == '{}') {
redeem_action = 'redeemed';
console.log(' Redeemed successfully.');
db.data[user][title].status = 'claimed and redeemed';
} else {
redeem_action = 'redeemed?';
+ console.debug(` Response 2: ${r2t}`);
console.log(' Unknown Response 2 - please report in https://github.com/vogler/free-games-claimer/issues/5');
}
}
From 04787909c7b83b842ebc605ee0ba9e51bca893cd Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Fri, 13 Oct 2023 00:03:11 +0200
Subject: [PATCH 089/253] eg: waitFor order confirmation to be attached instead
of visible, #233
---
epic-games.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/epic-games.js b/epic-games.js
index 16984a3..78675e3 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -227,7 +227,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.waitForSelector('text=Thanks for your order!');
+ 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!');
From d73a523fe7f76608af5db4e72b567f058814fea1 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 25 Oct 2023 19:44:52 +0200
Subject: [PATCH 090/253] eg: fix sign in, user displayname, #236
---
epic-games.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index 78675e3..d0dd537 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -72,7 +72,7 @@ try {
// page.click('button:has-text("Accept All Cookies")').catch(_ => { }); // Not needed anymore since we set the cookie above. Clicking this did not always work since the message was animated in too slowly.
- while (await page.locator('a[role="button"]:has-text("Sign In")').count() > 0) {
+ while (await page.locator('egs-navigation').getAttribute('isloggedin') != 'true') {
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
@@ -114,7 +114,7 @@ try {
await page.waitForURL(URL_CLAIM);
if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
}
- user = await page.locator('#user span').first().innerHTML();
+ user = await page.locator('egs-navigation').getAttribute('displayname'); // 'null' if !isloggedin
console.log(`Signed in as ${user}`);
db.data[user] ||= {};
if (cfg.time) console.timeEnd('login');
From a374d483451f1fa19c0f9f461e32cb37a4b46fd7 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 25 Oct 2023 19:48:58 +0200
Subject: [PATCH 091/253] eg: fix login (email/password split), closes #236
---
epic-games.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/epic-games.js b/epic-games.js
index d0dd537..0cafd1f 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -83,8 +83,9 @@ try {
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.click('text=Sign in with Epic Games');
await page.fill('#email', email);
+ await page.click('button[type="submit"]');
await page.fill('#password', password);
await page.click('button[type="submit"]');
page.waitForSelector('#h_captcha_challenge_login_prod iframe').then(async () => {
From 4137bb5569221812ee065b3307e533774986722a Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 26 Oct 2023 15:01:04 +0200
Subject: [PATCH 092/253] DEBUG=1 as alternative to PWDEBUG=1 (also shows
Playwright debugger)
---
config.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/config.js b/config.js
index e604d0f..336db99 100644
--- a/config.js
+++ b/config.js
@@ -5,7 +5,7 @@ dotenv.config({ path: 'data/config.env' }); // loads env vars from file - will n
// Options - also see table in README.md
export const cfg = {
- debug: process.env.PWDEBUG == '1', // runs non-headless and opens https://playwright.dev/docs/inspector
+ debug: process.env.DEBUG == '1' || process.env.PWDEBUG == '1', // runs non-headless and opens https://playwright.dev/docs/inspector
record: process.env.RECORD == '1', // `recordHar` (network) + `recordVideo`
time: process.env.TIME == '1', // log duration of each step
dryrun: process.env.DRYRUN == '1', // don't claim anything
From 1dbe2f1457f500a382e0326837a5f004c5825052 Mon Sep 17 00:00:00 2001
From: 4n4n4s
Date: Fri, 27 Oct 2023 11:37:32 +0000
Subject: [PATCH 093/253] Allow forks to create builds and fix failing build
---
.dockerignore | 2 ++
.github/workflows/docker.yml | 32 +++++++++++++++++++-------------
CONTRIBUTING.md | 6 ++++++
3 files changed, 27 insertions(+), 13 deletions(-)
create mode 100644 CONTRIBUTING.md
diff --git a/.dockerignore b/.dockerignore
index ffd3c43..4971835 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -4,3 +4,5 @@ data
.gitignore
**Dockerfile**
.dockerignore
+
+.github
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 25470d5..5c2dade 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -3,15 +3,15 @@ name: Build and push Docker image (amd64, arm64 to hub.docker.com and ghcr.io)
on:
workflow_dispatch: # allow manual trigger
# https://github.com/orgs/community/discussions/26276
- push: # on every branch, but not for PRs from forks?
- paths:
- - '**'
- - '!README.md'
- - '!.github/**'
- - '.github/workflows/docker.yml'
- pull_request: # includes PRs from forks but only triggers on creation, not pushes?
+ push:
branches:
- - "main" # only PRs against main
+ - "main"
+ - "v*"
+ tags:
+ - "v*"
+ pull_request:
+ branches:
+ - "main"
jobs:
docker:
@@ -25,6 +25,11 @@ jobs:
run: |
echo "BRANCH=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV
echo "NOW=$(date -R)" >> $GITHUB_ENV # date -Iseconds; date +'%Y-%m-%dT%H:%M:%S'
+ if [[ "${{ env.BRANCH }}" == "main" ]]; then
+ echo "IMAGE_TAG=latest" >> $GITHUB_ENV
+ else
+ echo "IMAGE_TAG=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV
+ fi
-
name: Set up QEMU
uses: docker/setup-qemu-action@v3
@@ -34,7 +39,7 @@ jobs:
-
name: Login to Docker Hub
uses: docker/login-action@v3
- # if: ${{ secrets.DOCKERHUB_USERNAME && secrets.DOCKERHUB_TOKEN }}
+ if: github.event_name != 'pull_request'
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
@@ -43,21 +48,22 @@ jobs:
uses: docker/login-action@v3
with:
registry: ghcr.io
- username: ${{ github.repository_owner }}
+ username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v5
+ if: github.event_name != 'pull_request'
with:
context: .
- push: true
+ push: ${{ github.event_name != 'pull_request' }}
build-args: |
COMMIT=${{ github.sha }}
BRANCH=${{ env.BRANCH }}
NOW=${{ env.NOW }}
platforms: linux/amd64,linux/arm64 # ,linux/arm/v7
tags: |
- voglerr/free-games-claimer:latest
- ghcr.io/vogler/free-games-claimer:latest
+ ${{ secrets.DOCKERHUB_USERNAME }}/free-games-claimer:${{env.IMAGE_TAG}}
+ ghcr.io/${{ github.actor }}/free-games-claimer:${{env.IMAGE_TAG}}
cache-from: type=gha
cache-to: type=gha,mode=max
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..aaf8218
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,6 @@
+# Contribute
+
+## Building and publishing docker images
+Setup the secrets for DOCKERHUB_USERNAME and [DOCKERHUB_TOKEN](https://hub.docker.com/settings/security) in https://github.com/YOUR_USERNAME/free-games-claimer/settings/secrets/actions to be able to run the docker.yml workflows.
+
+Check if under Workflow Permissions in https://github.com/YOUR_USERNAME/free-games-claimer/settings/actions the radio button is set to "Read and write permissions". In case that's not set the push to ghcr.io will fail.
\ No newline at end of file
From 280ab709752987c316016bdcc71ec26c0f3cb62f Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 2 Nov 2023 16:01:08 +0100
Subject: [PATCH 094/253] README.md: recommend to run without docker until #183
is fixed
---
README.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/README.md b/README.md
index 7cb4393..2f2ecd9 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,9 @@ Easy option: [install Docker](https://docs.docker.com/get-docker/) (or [podman](
```
docker run --rm -it -p 6080:6080 -v fgc:/fgc/data --pull=always ghcr.io/vogler/free-games-claimer
```
+
+_This currently gives you a captcha challenge for epic-games. Until [issue #183](https://github.com/vogler/free-games-claimer/issues/183) is fixed, it is recommended to just run `node epic-games` without docker (see below)._
+
This will run `node epic-games; node prime-gaming; node gog` - if you only want to claim games for one of the stores, you can override the default command by appending e.g. `node epic-games` at the end of the `docker run` command, or if you want several `bash -c "node epic-games.js; node gog.js"`.
Data (including json files with claimed games, codes to redeem, screenshots) is stored in the Docker volume `fgc`.
@@ -35,6 +38,7 @@ Data (including json files with claimed games, codes to redeem, screenshots) is
3. Run `npm install`
4. Run `pip install apprise` to install [apprise](https://github.com/caronc/apprise) if you want notifications
5. To get updates: `git pull; npm install`
+6. Run `node epic-games`, `node prime-gaming`, `node gog`...
During `npm install` Playwright will download its Firefox to a cache in home ([doc](https://playwright.dev/docs/browsers#managing-browser-binaries)).
If you are missing some dependencies for the browser on your system, you can use `sudo npx playwright install firefox --with-deps`.
From bf870919a66f3872f96328772ad219aa32c684f7 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 2 Nov 2023 16:18:25 +0100
Subject: [PATCH 095/253] upgrade to lowdb 6.1.1 and use JSONPreset
See example in https://github.com/typicode/lowdb/releases/tag/v6.1.0
---
package-lock.json | 32 ++++++++++++++++----------------
package.json | 2 +-
util.js | 10 ++--------
3 files changed, 19 insertions(+), 25 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 4cb3c34..320fab8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,7 +12,7 @@
"cross-env": "^7.0.3",
"dotenv": "^16.3.1",
"enquirer": "^2.4.1",
- "lowdb": "^6.0.1",
+ "lowdb": "^6.1.1",
"otplib": "^12.0.1",
"playwright-firefox": "^1.38.1",
"puppeteer-extra-plugin-stealth": "^2.11.2"
@@ -351,11 +351,11 @@
}
},
"node_modules/lowdb": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-6.0.1.tgz",
- "integrity": "sha512-1ktuKYLlQzAWwl4/PQkIr8JzNXgcTM6rAhpXaQ6BR+VwI98Q8ZwMFhBOn9u0ldcW3K/WWzhYpS3xyGTshgVGzA==",
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-6.1.1.tgz",
+ "integrity": "sha512-HO13FCxI8SCwfj2JRXOKgXggxnmfSc+l0aJsZ5I34X3pwzG/DPBSKyKu3Zkgg/pNmx854SVgE2la0oUeh6wzNw==",
"dependencies": {
- "steno": "^3.0.0"
+ "steno": "^3.1.1"
},
"engines": {
"node": ">=16"
@@ -642,11 +642,11 @@
}
},
"node_modules/steno": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/steno/-/steno-3.0.0.tgz",
- "integrity": "sha512-uZtn7Ht9yXLiYgOsmo8btj4+f7VxyYheMt8g6F1ANjyqByQXEE2Gygjgenp3otHH1TlHsS4JAaRGv5wJ1wvMNw==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/steno/-/steno-3.1.1.tgz",
+ "integrity": "sha512-B7c6EVH7oEiaMRW36SjUnktkDwp/qd4pQiduylyiqvcZEZDeX0IIFZRBZdwO/RaVo60M0wkDwC0e8yeKaR4VGg==",
"engines": {
- "node": ">=14.16"
+ "node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/typicode"
@@ -957,11 +957,11 @@
"integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ=="
},
"lowdb": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-6.0.1.tgz",
- "integrity": "sha512-1ktuKYLlQzAWwl4/PQkIr8JzNXgcTM6rAhpXaQ6BR+VwI98Q8ZwMFhBOn9u0ldcW3K/WWzhYpS3xyGTshgVGzA==",
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-6.1.1.tgz",
+ "integrity": "sha512-HO13FCxI8SCwfj2JRXOKgXggxnmfSc+l0aJsZ5I34X3pwzG/DPBSKyKu3Zkgg/pNmx854SVgE2la0oUeh6wzNw==",
"requires": {
- "steno": "^3.0.0"
+ "steno": "^3.1.1"
}
},
"merge-deep": {
@@ -1134,9 +1134,9 @@
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
},
"steno": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/steno/-/steno-3.0.0.tgz",
- "integrity": "sha512-uZtn7Ht9yXLiYgOsmo8btj4+f7VxyYheMt8g6F1ANjyqByQXEE2Gygjgenp3otHH1TlHsS4JAaRGv5wJ1wvMNw=="
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/steno/-/steno-3.1.1.tgz",
+ "integrity": "sha512-B7c6EVH7oEiaMRW36SjUnktkDwp/qd4pQiduylyiqvcZEZDeX0IIFZRBZdwO/RaVo60M0wkDwC0e8yeKaR4VGg=="
},
"strip-ansi": {
"version": "6.0.1",
diff --git a/package.json b/package.json
index d447382..94a0493 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,7 @@
"cross-env": "^7.0.3",
"dotenv": "^16.3.1",
"enquirer": "^2.4.1",
- "lowdb": "^6.0.1",
+ "lowdb": "^6.1.1",
"otplib": "^12.0.1",
"playwright-firefox": "^1.38.1",
"puppeteer-extra-plugin-stealth": "^2.11.2"
diff --git a/util.js b/util.js
index 94cccfd..9904486 100644
--- a/util.js
+++ b/util.js
@@ -11,14 +11,8 @@ export const dataDir = s => path.resolve(__dirname, 'data', s);
export const resolve = (...a) => a.length && a[0] == '0' ? null : path.resolve(...a);
// json database
-import { Low } from 'lowdb';
-import { JSONFile } from 'lowdb/node';
-export const jsonDb = async (file, defaultData) => {
- const db = new Low(new JSONFile(dataDir(file)), defaultData);
- await db.read();
- return db;
-};
-
+import { JSONPreset } from 'lowdb/node';
+export const jsonDb = (file, defaultData) => JSONPreset(dataDir(file), defaultData);
export const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
// date and time as UTC (no timezone offset) in nicely readable and sortable format, e.g., 2022-10-06 12:05:27.313
From 75f7d774456d481b0c567d2bb4c00759caebfd0d Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 2 Nov 2023 16:23:46 +0100
Subject: [PATCH 096/253] upgrade playwright-firefox 1.38.1 -> 1.39.0
---
package-lock.json | 30 +++++++++++++++---------------
package.json | 2 +-
2 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 320fab8..2570777 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,7 +14,7 @@
"enquirer": "^2.4.1",
"lowdb": "^6.1.1",
"otplib": "^12.0.1",
- "playwright-firefox": "^1.38.1",
+ "playwright-firefox": "^1.39.0",
"puppeteer-extra-plugin-stealth": "^2.11.2"
}
},
@@ -448,9 +448,9 @@
}
},
"node_modules/playwright-core": {
- "version": "1.38.1",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz",
- "integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==",
+ "version": "1.39.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz",
+ "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==",
"bin": {
"playwright-core": "cli.js"
},
@@ -459,12 +459,12 @@
}
},
"node_modules/playwright-firefox": {
- "version": "1.38.1",
- "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.38.1.tgz",
- "integrity": "sha512-IhOwSlhz8wpJnuzTCxZSQWgLGb+VX/8FjoUE7HuARbLXNPYM02vvbdSs+MxVEDgN9k2/i0QC4BrUMstP2VtYEg==",
+ "version": "1.39.0",
+ "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.39.0.tgz",
+ "integrity": "sha512-DFJRVL8mfOPyfiK8on34kYdvFeV0a0aNGRNUTPuHD2sv2+pMITPSosxRCgPvnFO3oWzEwVBVv/+c5E9fICY7gg==",
"hasInstallScript": true,
"dependencies": {
- "playwright-core": "1.38.1"
+ "playwright-core": "1.39.0"
},
"bin": {
"playwright": "cli.js"
@@ -1032,16 +1032,16 @@
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
"playwright-core": {
- "version": "1.38.1",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz",
- "integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg=="
+ "version": "1.39.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz",
+ "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw=="
},
"playwright-firefox": {
- "version": "1.38.1",
- "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.38.1.tgz",
- "integrity": "sha512-IhOwSlhz8wpJnuzTCxZSQWgLGb+VX/8FjoUE7HuARbLXNPYM02vvbdSs+MxVEDgN9k2/i0QC4BrUMstP2VtYEg==",
+ "version": "1.39.0",
+ "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.39.0.tgz",
+ "integrity": "sha512-DFJRVL8mfOPyfiK8on34kYdvFeV0a0aNGRNUTPuHD2sv2+pMITPSosxRCgPvnFO3oWzEwVBVv/+c5E9fICY7gg==",
"requires": {
- "playwright-core": "1.38.1"
+ "playwright-core": "1.39.0"
}
},
"puppeteer-extra-plugin": {
diff --git a/package.json b/package.json
index 94a0493..eeb87de 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
"enquirer": "^2.4.1",
"lowdb": "^6.1.1",
"otplib": "^12.0.1",
- "playwright-firefox": "^1.38.1",
+ "playwright-firefox": "^1.39.0",
"puppeteer-extra-plugin-stealth": "^2.11.2"
},
"repository": {
From b99a1542672ce8582fe9527554f2e46c8d71b69d Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 2 Nov 2023 16:26:45 +0100
Subject: [PATCH 097/253] Revert "workaround for recordVideo broken in
Playwright 1.36"
This reverts commit 13b2917dd03c675d6ab9853fe188f15c52782688.
Fine to do after upgrade to 1.39 in 75f7d774456d481b0c567d2bb4c00759caebfd0d.
which included https://github.com/microsoft/playwright/issues/27086
---
epic-games.js | 2 +-
gog.js | 3 +--
prime-gaming.js | 3 +--
unrealengine.js | 2 +-
4 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index 0cafd1f..5785b99 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -29,7 +29,7 @@ const context = await firefox.launchPersistentContext(cfg.dir.browser, {
// 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
- recordVideo: cfg.record ? { dir: path.resolve('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
+ 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
args: [ // https://peter.sh/experiments/chromium-command-line-switches
diff --git a/gog.js b/gog.js
index be4514d..aaffd7b 100644
--- a/gog.js
+++ b/gog.js
@@ -1,5 +1,4 @@
import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdated playwright-extra
-import path from 'path';
import { resolve, jsonDb, datetime, filenamify, prompt, notify, html_game_list, handleSIGINT } from './util.js';
import { cfg } from './config.js';
@@ -16,7 +15,7 @@ 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
- recordVideo: cfg.record ? { dir: path.resolve('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
+ 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
});
diff --git a/prime-gaming.js b/prime-gaming.js
index 89691b8..8e630e6 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -1,6 +1,5 @@
import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdated playwright-extra
import { authenticator } from 'otplib';
-import path from 'path';
import { resolve, jsonDb, datetime, stealth, filenamify, prompt, confirm, notify, html_game_list, handleSIGINT } from './util.js';
import { cfg } from './config.js';
@@ -18,7 +17,7 @@ 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
- recordVideo: cfg.record ? { dir: path.resolve('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
+ 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/pg-${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
});
diff --git a/unrealengine.js b/unrealengine.js
index f7ee6fa..64b9c55 100644
--- a/unrealengine.js
+++ b/unrealengine.js
@@ -24,7 +24,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 for firefox: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:106.0) Gecko/20100101 Firefox/106.0
locale: "en-US", // ignore OS locale to be sure to have english text for locators
- recordVideo: cfg.record ? { dir: path.resolve('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
+ 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/ue-${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
});
From 32cd0d8990a243375928682b0cfa83269a3df4a9 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Mon, 6 Nov 2023 19:12:16 +0100
Subject: [PATCH 098/253] Create LICENSE - AGPL-3.0
https://choosealicense.com/licenses/agpl-3.0/
---
LICENSE | 661 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 661 insertions(+)
create mode 100644 LICENSE
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0ad25db
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+.
From 28a1e42cc4ad4abbd860bd163894f8991349f03a Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Mon, 6 Nov 2023 19:15:09 +0100
Subject: [PATCH 099/253] npm package*.json license MIT -> AGPL-3.0-only
---
package-lock.json | 2 +-
package.json | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 2570777..40d4246 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7,7 +7,7 @@
"": {
"name": "free-games-claimer",
"version": "1.4.0",
- "license": "MIT",
+ "license": "AGPL-3.0-only",
"dependencies": {
"cross-env": "^7.0.3",
"dotenv": "^16.3.1",
diff --git a/package.json b/package.json
index eeb87de..a67392f 100644
--- a/package.json
+++ b/package.json
@@ -23,5 +23,5 @@
"url": "https://github.com/vogler/free-games-claimer.git"
},
"author": "Ralf Vogler",
- "license": "MIT"
+ "license": "AGPL-3.0-only"
}
From 0ab9935fb5b38791d93199093156e647d06112c6 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Mon, 6 Nov 2023 19:22:43 +0100
Subject: [PATCH 100/253] eg: catch timeout in case there are no free games
available, #210
---
epic-games.js | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/epic-games.js b/epic-games.js
index 5785b99..4f70a6e 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -123,7 +123,13 @@ try {
// Detect free games
const game_loc = page.locator('a:has(span:text-is("Free Now"))');
- await game_loc.last().waitFor();
+ await game_loc.last().waitFor().catch(_ => {
+ // rarely there are no free games available -> catch Timeout
+ // TODO would be better to wait for alternative like 'coming soon' instead of waiting for timeout
+ // see https://github.com/vogler/free-games-claimer/issues/210#issuecomment-1727420943
+ console.error('Seems like currently there are no free games available in your region...')
+ // urls below should then be an empty list
+ });
// 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
From 584130f5d12c8e33739dd07597c46a24f2e2e385 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 7 Nov 2023 11:31:14 +0100
Subject: [PATCH 101/253] edits for #229, build image for PRs from forks?
---
.github/workflows/docker.yml | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 5c2dade..169c3a0 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -9,9 +9,13 @@ on:
- "v*"
tags:
- "v*"
- pull_request:
+ paths:
+ - '**'
+ - '!README.md'
+ - '!.github/**'
+ pull_request: # runs when opened/reopned or when the head branch is updated, see https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request
branches:
- - "main"
+ - "main" # only PRs against main
jobs:
docker:
@@ -39,7 +43,7 @@ jobs:
-
name: Login to Docker Hub
uses: docker/login-action@v3
- if: github.event_name != 'pull_request'
+ if: github.event_name != 'pull_request' # TODO if DOCKERHUB_* are set?
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
@@ -48,22 +52,23 @@ jobs:
uses: docker/login-action@v3
with:
registry: ghcr.io
- username: ${{ github.actor }}
+ username: ${{ github.actor }} # actor is user that opened PR, was repository_owner before
password: ${{ secrets.GITHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v5
- if: github.event_name != 'pull_request'
+ # if: github.event_name != 'pull_request' # still want to build image
with:
context: .
- push: ${{ github.event_name != 'pull_request' }}
+ push: ${{ github.event_name != 'pull_request' }} # TODO push for forks?
build-args: |
COMMIT=${{ github.sha }}
BRANCH=${{ env.BRANCH }}
NOW=${{ env.NOW }}
platforms: linux/amd64,linux/arm64 # ,linux/arm/v7
tags: |
- ${{ secrets.DOCKERHUB_USERNAME }}/free-games-claimer:${{env.IMAGE_TAG}}
+ ${{ secrets.DOCKERHUB_USERNAME }}/free-games-claimer:${{env.IMAGE_TAG}} # TODO if DOCKERHUB_* are set?
+
ghcr.io/${{ github.actor }}/free-games-claimer:${{env.IMAGE_TAG}}
cache-from: type=gha
cache-to: type=gha,mode=max
From 92ce3d405d41e7ff141acc5ec72d33f706c967bf Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 7 Nov 2023 11:36:17 +0100
Subject: [PATCH 102/253] run docker workflow if its defition changed
---
.github/workflows/docker.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 169c3a0..0d75b1e 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -9,10 +9,10 @@ on:
- "v*"
tags:
- "v*"
- paths:
+ paths: # ignore changes to certain files
- '**'
- - '!README.md'
- - '!.github/**'
+ - '!*.md'
+ # - '!.github/**'
pull_request: # runs when opened/reopned or when the head branch is updated, see https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request
branches:
- "main" # only PRs against main
From e192365b48c57e4da84e463a444715f8edbef153 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 7 Nov 2023 11:39:08 +0100
Subject: [PATCH 103/253] can't have comment in yml list?
---
.github/workflows/docker.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 0d75b1e..a54117c 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -66,9 +66,9 @@ jobs:
BRANCH=${{ env.BRANCH }}
NOW=${{ env.NOW }}
platforms: linux/amd64,linux/arm64 # ,linux/arm/v7
+ # TODO docker tag only if DOCKERHUB_* are set?
tags: |
- ${{ secrets.DOCKERHUB_USERNAME }}/free-games-claimer:${{env.IMAGE_TAG}} # TODO if DOCKERHUB_* are set?
-
+ ${{ secrets.DOCKERHUB_USERNAME }}/free-games-claimer:${{env.IMAGE_TAG}}
ghcr.io/${{ github.actor }}/free-games-claimer:${{env.IMAGE_TAG}}
cache-from: type=gha
cache-to: type=gha,mode=max
From a62aa8c0c836b98a5330f5e0ae5bcbfab9761a27 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 7 Nov 2023 11:53:29 +0100
Subject: [PATCH 104/253] sonarcloud fix docker: Remove cache after installing
packages
https://sonarcloud.io/project/issues?resolved=false&types=CODE_SMELL&id=vogler_free-games-claimer&open=AYupZi3__aoWVkCdISRI
https://sonarcloud.io/organizations/vogler/rules?open=docker%3AS6587&rule_key=docker%3AS6587&tab=how_to_fix
https://askubuntu.com/questions/3167/what-is-difference-between-the-options-autoclean-autoremove-and-clean
---
Dockerfile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dockerfile b/Dockerfile
index 7a291b3..f44a893 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -36,8 +36,8 @@ RUN apt-get update \
libgdk-pixbuf-2.0-0 \
libdbus-glib-1-2 \
libxcursor1 \
- && apt-get autoclean -y \
&& apt-get autoremove -y \
+ && apt-get clean \
&& rm -rf \
/tmp/* \
/usr/share/doc/* \
From a6b9ec96094e1a448b815594a97e1c7164a028ea Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 7 Nov 2023 12:02:33 +0100
Subject: [PATCH 105/253] sonarcloud fix reject(error)
https://sonarcloud.io/project/issues?cleanCodeAttributeCategories=CONSISTENT&resolved=false&id=vogler_free-games-claimer&open=AYupZi4O_aoWVkCdISRb&tab=code
---
version.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.js b/version.js
index 7b9ebef..1df93a1 100644
--- a/version.js
+++ b/version.js
@@ -13,7 +13,7 @@ const execp = (cmd) => new Promise((resolve, reject) => {
if (error.message.includes('command not found')) {
console.info('Install git to check for updates!');
}
- return reject();
+ return reject(error);
}
resolve(stdout.trim());
});
From b5ef699f4f0387a48f46eecae5536856c94112e9 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 7 Nov 2023 12:03:15 +0100
Subject: [PATCH 106/253] sort .dockerignore
---
.dockerignore | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/.dockerignore b/.dockerignore
index 4971835..7fd39f7 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,8 +1,10 @@
-node_modules
-data
+node_modules/
+data/
+*.env
.gitignore
+.github/
+
**Dockerfile**
.dockerignore
-.github
From e5935faa131187f54cb8b8afe726dd96fcb11fce Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 7 Nov 2023 15:20:58 +0100
Subject: [PATCH 107/253] sonarcloud fixes
---
Dockerfile | 2 +-
prime-gaming.js | 3 +--
util.js | 15 ---------------
xbox.js | 2 --
4 files changed, 2 insertions(+), 20 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index f44a893..a8c5e24 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -64,7 +64,7 @@ RUN npm install
COPY . .
# Shell scripts need Linux line endings. On Windows, git might be configured to check out dos/CRLF line endings, so we convert them for those people in case they want to build the image. They could also use --config core.autocrlf=input
-RUN dos2unix *.sh && chmod +x *.sh
+RUN dos2unix ./*.sh && chmod +x ./*.sh
COPY docker-entrypoint.sh /usr/local/bin/
ARG COMMIT=""
diff --git a/prime-gaming.js b/prime-gaming.js
index 8e630e6..5acdb06 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -206,7 +206,7 @@ try {
// {"reason":"Invalid or no captcha"}
// {"reason":"code_used"}
// {"reason":"code_not_found"}
- if (reason && reason.includes('captcha')) {
+ if (reason?.includes('captcha')) {
redeem_action = 'redeem (got captcha)';
console.error(' Got captcha; could not redeem!');
} else if (reason == 'code_used') {
@@ -228,7 +228,6 @@ try {
console.log(' Redeemed successfully.');
db.data[user][title].status = 'claimed and redeemed';
} else {
- redeem_action = 'redeemed?';
console.debug(` Response 2: ${r2t}`);
console.log(' Unknown Response 2 - please report in https://github.com/vogler/free-games-claimer/issues/5');
}
diff --git a/util.js b/util.js
index 9904486..5b8bef0 100644
--- a/util.js
+++ b/util.js
@@ -27,21 +27,6 @@ export const handleSIGINT = (context = null) => process.on('SIGINT', async () =>
if (context) await context.close(); // in order to save recordings also on SIGINT, we need to disable Playwright's handleSIGINT and close the context ourselves
});
-// stealth with playwright: https://github.com/berstend/puppeteer-extra/issues/454#issuecomment-917437212
-// gets userAgent and then removes "Headless" from it
-const newStealthContext = async (browser, contextOptions = {}, debug = false) => {
- if (!debug) { // only need to fix userAgent in headless mode
- const dummyContext = await browser.newContext();
- const originalUserAgent = await (await dummyContext.newPage()).evaluate(() => navigator.userAgent);
- await dummyContext.close();
- // console.log('originalUserAgent:', originalUserAgent); // Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/96.0.4664.110 Safari/537.36
- contextOptions = {
- ...contextOptions,
- userAgent: originalUserAgent.replace("Headless", ""), // HeadlessChrome -> Chrome, TODO needed?
- };
- }
-};
-
export const stealth = async (context) => {
// stealth with playwright: https://github.com/berstend/puppeteer-extra/issues/454#issuecomment-917437212
// https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra-plugin-stealth/evasions
diff --git a/xbox.js b/xbox.js
index 501f4f1..a2150f1 100644
--- a/xbox.js
+++ b/xbox.js
@@ -8,8 +8,6 @@ import {
notify,
prompt,
} from "./util.js";
-import path from "path";
-import { existsSync, writeFileSync } from "fs";
import { cfg } from "./config.js";
// ### SETUP
From 798b130c9274a6abdd08b987393db2b1f0d2bf45 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 7 Nov 2023 16:45:17 +0100
Subject: [PATCH 108/253] fix vscode problem in jsconfig with module vs.
moduleResolution
Option 'module' must be set to 'NodeNext' when option 'moduleResolution' is set to 'NodeNext'.ts
Specify what module code is generated.
See more: https://www.typescriptlang.org/tsconfig#module
---
jsconfig.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/jsconfig.json b/jsconfig.json
index 1b438cb..2e21de9 100644
--- a/jsconfig.json
+++ b/jsconfig.json
@@ -2,7 +2,7 @@
"compilerOptions": {
"checkJs": true,
"target": "es2021",
- "module": "esnext",
+ "module": "NodeNext",
"moduleResolution": "NodeNext", // https://github.com/typicode/lowdb/issues/554
},
"exclude": ["node_modules", "**/node_modules"]
From 2eaf6f059855187200d60bbf2876ec0d6638acf3 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 7 Nov 2023 17:57:10 +0100
Subject: [PATCH 109/253] .eslintignore -> .gitignore, no-unused-vars: ignore
args starting with _
https://eslint.org/docs/latest/rules/no-unused-vars#argsignorepattern
https://eslint.org/docs/latest/rules/no-undef
---
.eslintignore | 1 +
.eslintrc.cjs | 1 +
epic-games.js | 1 +
3 files changed, 3 insertions(+)
create mode 120000 .eslintignore
diff --git a/.eslintignore b/.eslintignore
new file mode 120000
index 0000000..3e4e48b
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1 @@
+.gitignore
\ No newline at end of file
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index c2d8ac6..21ca7c5 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -23,5 +23,6 @@ module.exports = {
},
'rules': {
'semi': 'error',
+ 'no-unused-vars': ["error", { "argsIgnorePattern": "^_" }],
},
};
diff --git a/epic-games.js b/epic-games.js
index 4f70a6e..8fc78ca 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -51,6 +51,7 @@ if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
const page = context.pages().length ? context.pages()[0] : await context.newPage(); // should always exist
// console.debug('userAgent:', await page.evaluate(() => navigator.userAgent));
+// eslint-disable-next-line no-undef
if (cfg.debug) console.debug(await page.evaluate(() => window.screen));
if (cfg.record && cfg.debug) {
// const filter = _ => true;
From 5083ea408df9b7ce7b4f5fb198267f796d699a9a Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 7 Nov 2023 17:58:36 +0100
Subject: [PATCH 110/253] ncu -u: eslint 8.50.0 -> 8.53.0
---
package-lock.json | 128 +++++++++++++++++++++++++---------------------
package.json | 2 +-
2 files changed, 72 insertions(+), 58 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index b725f4d..ec60dff 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,7 +18,7 @@
"puppeteer-extra-plugin-stealth": "^2.11.2"
},
"devDependencies": {
- "eslint": "^8.50.0"
+ "eslint": "^8.53.0"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -55,9 +55,9 @@
}
},
"node_modules/@eslint/eslintrc": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz",
- "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==",
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz",
+ "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==",
"dev": true,
"dependencies": {
"ajv": "^6.12.4",
@@ -78,21 +78,21 @@
}
},
"node_modules/@eslint/js": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz",
- "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==",
+ "version": "8.53.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz",
+ "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@humanwhocodes/config-array": {
- "version": "0.11.11",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",
- "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==",
+ "version": "0.11.13",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
+ "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==",
"dev": true,
"dependencies": {
- "@humanwhocodes/object-schema": "^1.2.1",
+ "@humanwhocodes/object-schema": "^2.0.1",
"debug": "^4.1.1",
"minimatch": "^3.0.5"
},
@@ -114,9 +114,9 @@
}
},
"node_modules/@humanwhocodes/object-schema": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
- "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz",
+ "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
"dev": true
},
"node_modules/@nodelib/fs.scandir": {
@@ -209,10 +209,16 @@
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA=="
},
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+ "dev": true
+ },
"node_modules/acorn": {
- "version": "8.10.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
- "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+ "version": "8.11.2",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
+ "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==",
"dev": true,
"bin": {
"acorn": "bin/acorn"
@@ -476,18 +482,19 @@
}
},
"node_modules/eslint": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz",
- "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==",
+ "version": "8.53.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz",
+ "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
- "@eslint/eslintrc": "^2.1.2",
- "@eslint/js": "8.50.0",
- "@humanwhocodes/config-array": "^0.11.11",
+ "@eslint/eslintrc": "^2.1.3",
+ "@eslint/js": "8.53.0",
+ "@humanwhocodes/config-array": "^0.11.13",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
+ "@ungap/structured-clone": "^1.2.0",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
@@ -760,9 +767,9 @@
}
},
"node_modules/globals": {
- "version": "13.22.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz",
- "integrity": "sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==",
+ "version": "13.23.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz",
+ "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==",
"dev": true,
"dependencies": {
"type-fest": "^0.20.2"
@@ -1219,9 +1226,9 @@
}
},
"node_modules/punycode": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
- "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"engines": {
"node": ">=6"
@@ -1613,9 +1620,9 @@
"dev": true
},
"@eslint/eslintrc": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz",
- "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==",
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz",
+ "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==",
"dev": true,
"requires": {
"ajv": "^6.12.4",
@@ -1630,18 +1637,18 @@
}
},
"@eslint/js": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz",
- "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==",
+ "version": "8.53.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz",
+ "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==",
"dev": true
},
"@humanwhocodes/config-array": {
- "version": "0.11.11",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",
- "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==",
+ "version": "0.11.13",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
+ "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==",
"dev": true,
"requires": {
- "@humanwhocodes/object-schema": "^1.2.1",
+ "@humanwhocodes/object-schema": "^2.0.1",
"debug": "^4.1.1",
"minimatch": "^3.0.5"
}
@@ -1653,9 +1660,9 @@
"dev": true
},
"@humanwhocodes/object-schema": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
- "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz",
+ "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
"dev": true
},
"@nodelib/fs.scandir": {
@@ -1739,10 +1746,16 @@
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA=="
},
+ "@ungap/structured-clone": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+ "dev": true
+ },
"acorn": {
- "version": "8.10.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
- "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+ "version": "8.11.2",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
+ "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==",
"dev": true
},
"acorn-jsx": {
@@ -1923,18 +1936,19 @@
"dev": true
},
"eslint": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz",
- "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==",
+ "version": "8.53.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz",
+ "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==",
"dev": true,
"requires": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
- "@eslint/eslintrc": "^2.1.2",
- "@eslint/js": "8.50.0",
- "@humanwhocodes/config-array": "^0.11.11",
+ "@eslint/eslintrc": "^2.1.3",
+ "@eslint/js": "8.53.0",
+ "@humanwhocodes/config-array": "^0.11.13",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
+ "@ungap/structured-clone": "^1.2.0",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
@@ -2138,9 +2152,9 @@
}
},
"globals": {
- "version": "13.22.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz",
- "integrity": "sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==",
+ "version": "13.23.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz",
+ "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==",
"dev": true,
"requires": {
"type-fest": "^0.20.2"
@@ -2479,9 +2493,9 @@
"dev": true
},
"punycode": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
- "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true
},
"puppeteer-extra-plugin": {
diff --git a/package.json b/package.json
index 6771ee4..189e38e 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,6 @@
"author": "Ralf Vogler",
"license": "AGPL-3.0-only",
"devDependencies": {
- "eslint": "^8.50.0"
+ "eslint": "^8.53.0"
}
}
From 6a34cb541de3504668aad14293e14d0642ca6d3b Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 7 Nov 2023 18:03:54 +0100
Subject: [PATCH 111/253] format sonar.yml
---
.github/workflows/sonar.yml | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml
index 62067f3..bb2f89f 100644
--- a/.github/workflows/sonar.yml
+++ b/.github/workflows/sonar.yml
@@ -1,5 +1,5 @@
on:
- # Trigger analysis when pushing in master or pull requests, and when creating a pull request.
+ # Trigger analysis when pushing in main or pull requests, and when creating a pull request.
push:
branches:
- main
@@ -10,18 +10,23 @@ jobs:
sonarcloud:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ -
+ uses: actions/checkout@v4
with:
# Disabling shallow clone is recommended for improving relevancy of reporting
fetch-depth: 0
- - name: Install modules
+ -
+ name: Install modules
run: npm install -g eslint
- - name: Run ESLint
+ -
+ name: Run ESLint
continue-on-error: true
run: eslint . --ext .js,.ts -f json -o eslint_report.json
- - name: Fix ESLint paths
+ -
+ name: Fix ESLint paths
run: sed -i 's+/home/runner/work/free-games-claimer/free-games-claimer+/github/workspace+g' eslint_report.json
- - name: SonarCloud Scan
+ -
+ name: SonarCloud Scan
uses: sonarsource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
From e4e4f2eafa2a186bf25942bac5b06e9053435910 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 7 Nov 2023 18:10:22 +0100
Subject: [PATCH 112/253] fix eslint errors
---
epic-games.js | 10 +++++-----
notify-test.js | 1 +
prime-gaming.js | 2 +-
util.js | 7 ++++---
version.js | 7 +++----
5 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index 8fc78ca..dcfce73 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -94,7 +94,7 @@ try {
await notify('epic-games: got captcha during login. Please check.');
}).catch(_ => { });
page.waitForSelector('h6:has-text("Incorrect response.")').then(async () => {
- console.error('Incorrect repsonse for captcha!')
+ console.error('Incorrect repsonse for captcha!');
}).catch(_ => { });
// handle MFA, but don't await it
page.waitForURL('**/id/login/mfa**').then(async () => {
@@ -128,7 +128,7 @@ try {
// rarely there are no free games available -> catch Timeout
// TODO would be better to wait for alternative like 'coming soon' instead of waiting for timeout
// see https://github.com/vogler/free-games-claimer/issues/210#issuecomment-1727420943
- console.error('Seems like currently there are no free games available in your region...')
+ console.error('Seems like currently there are no free games available in your region...');
// urls below should then be an empty list
});
// clicking on `game_sel` sometimes led to a 404, see https://github.com/vogler/free-games-claimer/issues/25
@@ -227,8 +227,8 @@ try {
const captcha = iframe.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.')
- await notify('epic-games: got captcha challenge right before claim. Use VNC to solve it manually.')
+ 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.');
+ await notify('epic-games: got captcha challenge right before claim. Use VNC to solve it manually.');
// await page.waitForTimeout(2000);
// const p = path.resolve(cfg.dir.screenshots, 'epic-games', 'captcha', `${filenamify(datetime())}.png`);
// await captcha.screenshot({ path: p });
@@ -269,5 +269,5 @@ try {
}
}
if (cfg.debug) writeFileSync(path.resolve(cfg.dir.browser, 'cookies.json'), JSON.stringify(await context.cookies()));
-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();
diff --git a/notify-test.js b/notify-test.js
index d8f4dab..d138657 100644
--- a/notify-test.js
+++ b/notify-test.js
@@ -1,3 +1,4 @@
+/* eslint-disable no-constant-condition */
import { delay, html_game_list, notify } from "./util.js";
const URL_CLAIM = 'https://gaming.amazon.com/home'; // dummy URL
diff --git a/prime-gaming.js b/prime-gaming.js
index 5acdb06..ae7879e 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -394,5 +394,5 @@ try {
notify(`prime-gaming (${user}):
${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();
diff --git a/util.js b/util.js
index 5b8bef0..0ab0912 100644
--- a/util.js
+++ b/util.js
@@ -74,11 +74,12 @@ const timeoutPlugin = timeout => enquirer => { // cancel prompt after timeout ms
prompt.on('submit', _ => clearTimeout(t));
prompt.on('cancel', _ => clearTimeout(t));
});
-}
+};
enquirer.use(timeoutPlugin(cfg.login_timeout)); // TODO may not want to have this timeout for all prompts; better extend Prompt and add a timeout prompt option
// single prompt that just returns the non-empty value instead of an object
+// @ts-ignore
export const prompt = o => enquirer.prompt({name: 'name', type: 'input', message: 'Enter value', ...o}).then(r => r.name).catch(_ => {});
-export const confirm = o => prompt({type: 'confirm', message: 'Continue?', ...o})
+export const confirm = o => prompt({type: 'confirm', message: 'Continue?', ...o});
// notifications via apprise CLI
import { exec } from 'child_process';
@@ -93,7 +94,7 @@ export const notify = (html) => new Promise((resolve, reject) => {
if (error.message.includes('command not found')) {
console.info('Run `pip install apprise`. See https://github.com/vogler/free-games-claimer#notifications');
}
- return resolve();
+ return reject(error);
}
if (stderr) console.error(`stderr: ${stderr}`);
if (stdout) console.log(`stdout: ${stdout}`);
diff --git a/version.js b/version.js
index 1df93a1..d992afb 100644
--- a/version.js
+++ b/version.js
@@ -1,7 +1,6 @@
// check if running the latest version
import {log} from 'console';
-import { readFileSync } from 'fs';
import { exec } from 'child_process';
const execp = (cmd) => new Promise((resolve, reject) => {
@@ -19,7 +18,7 @@ const execp = (cmd) => new Promise((resolve, reject) => {
});
});
-const git_main = () => readFileSync('.git/refs/heads/main').toString().trim();
+// const git_main = () => readFileSync('.git/refs/heads/main').toString().trim();
let sha, date;
// if (existsSync('/.dockerenv')) { // did not work
@@ -44,7 +43,7 @@ log('Local commit:', sha, new Date(date));
log('Online commit:', gh.sha, new Date(gh.commit.committer.date));
if (sha == gh.sha) {
- log('Running the latest version!')
+ log('Running the latest version!');
} else {
- log('Not running the latest version!')
+ log('Not running the latest version!');
}
From efe4faab3e3027fb347b39f6ac349fcd55ff3a4d Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 7 Nov 2023 18:24:32 +0100
Subject: [PATCH 113/253] add sonarcloud badge for code smells
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 18cf520..5c9e90f 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,7 @@
+[](https://sonarcloud.io/project/overview?id=vogler_free-games-claimer)
# free-games-claimer
Claims free games periodically on
From 011eddf97a096041346cc390e340b70fea5d1290 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 8 Nov 2023 01:02:43 +0100
Subject: [PATCH 114/253] eslint: enable most stylistic rules
https://eslint.style/packages/js
---
.eslintrc.cjs | 82 ++++++++++++++++++++++++++++++++++++++---------
package-lock.json | 31 ++++++++++++++++++
package.json | 1 +
3 files changed, 98 insertions(+), 16 deletions(-)
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 21ca7c5..04989aa 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -1,28 +1,78 @@
module.exports = {
- 'env': {
- 'es2021': true,
- 'node': true,
+ env: {
+ es2021: true,
+ node: true,
},
- "extends": "eslint:recommended",
- 'overrides': [
+ extends: 'eslint:recommended',
+ overrides: [
{
- 'env': {
- 'node': true,
+ env: {
+ node: true,
},
- 'files': [
+ files: [
'.eslintrc.{js,cjs}',
],
- 'parserOptions': {
- 'sourceType': 'script',
+ parserOptions: {
+ sourceType: 'script',
},
},
],
- 'parserOptions': {
- 'ecmaVersion': 'latest',
- 'sourceType': 'module',
+ parserOptions: {
+ ecmaVersion: 'latest',
+ sourceType: 'module',
},
- 'rules': {
- 'semi': 'error',
- 'no-unused-vars': ["error", { "argsIgnorePattern": "^_" }],
+ plugins: [
+ '@stylistic/js',
+ ],
+ // https://eslint.org/docs/latest/rules/
+ // https://eslint.style/packages/js
+ rules: {
+ 'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
+ '@stylistic/js/array-bracket-newline': ['error', 'consistent'],
+ '@stylistic/js/array-bracket-spacing': 'error',
+ '@stylistic/js/array-element-newline': ['error', 'consistent'],
+ '@stylistic/js/arrow-parens': ['error', 'as-needed'],
+ '@stylistic/js/arrow-spacing': 'error',
+ '@stylistic/js/block-spacing': 'error',
+ '@stylistic/js/brace-style': 'error',
+ '@stylistic/js/comma-dangle': ['error', 'always-multiline'],
+ '@stylistic/js/comma-spacing': 'error',
+ '@stylistic/js/comma-style': 'error',
+ '@stylistic/js/eol-last': 'error',
+ '@stylistic/js/func-call-spacing': 'error',
+ '@stylistic/js/function-paren-newline': ['error', 'consistent'],
+ '@stylistic/js/implicit-arrow-linebreak': 'error',
+ '@stylistic/js/indent': ['error', 2],
+ '@stylistic/js/key-spacing': 'error',
+ '@stylistic/js/keyword-spacing': 'error',
+ '@stylistic/js/linebreak-style': 'error',
+ '@stylistic/js/no-extra-parens': 'error',
+ '@stylistic/js/no-extra-semi': 'error',
+ '@stylistic/js/no-mixed-spaces-and-tabs': 'error',
+ '@stylistic/js/no-multi-spaces': 'error',
+ '@stylistic/js/no-multiple-empty-lines': 'error',
+ '@stylistic/js/no-tabs': 'error',
+ '@stylistic/js/no-trailing-spaces': 'error',
+ '@stylistic/js/no-whitespace-before-property': 'error',
+ '@stylistic/js/nonblock-statement-body-position': 'error',
+ '@stylistic/js/object-curly-newline': 'error',
+ '@stylistic/js/object-curly-spacing': ['error', 'always'],
+ '@stylistic/js/object-property-newline': ['error', { allowAllPropertiesOnSameLine: true }],
+ '@stylistic/js/quote-props': ['error', 'as-needed'],
+ '@stylistic/js/quotes': ['error', 'single'],
+ '@stylistic/js/rest-spread-spacing': 'error',
+ '@stylistic/js/semi': 'error',
+ '@stylistic/js/semi-spacing': 'error',
+ '@stylistic/js/semi-style': 'error',
+ '@stylistic/js/space-before-blocks': 'error',
+ '@stylistic/js/space-before-function-paren': ['error', { anonymous: 'never', named: 'never', asyncArrow: 'always' }],
+ '@stylistic/js/space-in-parens': 'error',
+ '@stylistic/js/space-infix-ops': 'error',
+ '@stylistic/js/space-unary-ops': 'error',
+ '@stylistic/js/spaced-comment': 'error',
+ '@stylistic/js/switch-colon-spacing': 'error',
+ '@stylistic/js/template-curly-spacing': 'error',
+ '@stylistic/js/template-tag-spacing': 'error',
+ '@stylistic/js/wrap-regex': 'error',
},
};
diff --git a/package-lock.json b/package-lock.json
index ec60dff..4736f13 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,6 +18,7 @@
"puppeteer-extra-plugin-stealth": "^2.11.2"
},
"devDependencies": {
+ "@stylistic/eslint-plugin-js": "^1.0.1",
"eslint": "^8.53.0"
}
},
@@ -196,6 +197,21 @@
"@otplib/plugin-thirty-two": "^12.0.1"
}
},
+ "node_modules/@stylistic/eslint-plugin-js": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.0.1.tgz",
+ "integrity": "sha512-SfJlEnmBowaWx9GyN/7vQ/7jQP2wVQe5CcaoVL6V5nmCWl9Q+VSeJPSBOjB7XOYSYL1HoEQsvA+8Hy7Zt2XrnA==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "acorn": "^8.11.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esutils": "^2.0.3",
+ "graphemer": "^1.4.0"
+ }
+ },
"node_modules/@types/debug": {
"version": "4.1.8",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz",
@@ -1733,6 +1749,21 @@
"@otplib/plugin-thirty-two": "^12.0.1"
}
},
+ "@stylistic/eslint-plugin-js": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.0.1.tgz",
+ "integrity": "sha512-SfJlEnmBowaWx9GyN/7vQ/7jQP2wVQe5CcaoVL6V5nmCWl9Q+VSeJPSBOjB7XOYSYL1HoEQsvA+8Hy7Zt2XrnA==",
+ "dev": true,
+ "requires": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "acorn": "^8.11.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esutils": "^2.0.3",
+ "graphemer": "^1.4.0"
+ }
+ },
"@types/debug": {
"version": "4.1.8",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz",
diff --git a/package.json b/package.json
index 189e38e..fe2b52b 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,7 @@
"author": "Ralf Vogler",
"license": "AGPL-3.0-only",
"devDependencies": {
+ "@stylistic/eslint-plugin-js": "^1.0.1",
"eslint": "^8.53.0"
}
}
From 0832ae57f5e5746b1ee03abafdea1dbd66055aa8 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 8 Nov 2023 01:16:49 +0100
Subject: [PATCH 115/253] run `eslint --fix .`
---
config.js | 6 +-
epic-games.js | 19 ++-
gog.js | 25 ++--
migrate.js | 3 +-
notify-test.js | 2 +-
prime-gaming.js | 29 ++--
unrealengine.js | 25 ++--
util.js | 19 ++-
version.js | 8 +-
xbox.js | 386 ++++++++++++++++++++++++------------------------
10 files changed, 259 insertions(+), 263 deletions(-)
diff --git a/config.js b/config.js
index f12f99e..0962159 100644
--- a/config.js
+++ b/config.js
@@ -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
@@ -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,
diff --git a/epic-games.js b/epic-games.js
index dcfce73..06d66cc 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -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
@@ -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
@@ -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);
@@ -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(_ => { });
@@ -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);
@@ -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!');
@@ -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'
diff --git a/gog.js b/gog.js
index aaffd7b..c686851 100644
--- a/gog.js
+++ b/gog.js
@@ -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
@@ -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
@@ -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);
@@ -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(_ => { });
@@ -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.');
@@ -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();
@@ -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}):
${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();
diff --git a/migrate.js b/migrate.js
index 41bbe13..b2db945 100644
--- a/migrate.js
+++ b/migrate.js
@@ -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 ||= {};
diff --git a/notify-test.js b/notify-test.js
index d138657..9d593c0 100644
--- a/notify-test.js
+++ b/notify-test.js
@@ -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
diff --git a/prime-gaming.js b/prime-gaming.js
index ae7879e..e60caf7 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -16,7 +16,7 @@ const db = await jsonDb('prime-gaming.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
+ 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/pg-${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
@@ -44,11 +44,11 @@ try {
console.error('Not signed in anymore.');
await page.click('button:has-text("Sign in")');
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.pg_email && cfg.pg_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.pg_email || await prompt({message: 'Enter email'});
- const password = email && (cfg.pg_password || await prompt({type: 'password', message: 'Enter password'}));
+ const email = cfg.pg_email || await prompt({ message: 'Enter email' });
+ const password = email && (cfg.pg_password || await prompt({ type: 'password', message: 'Enter password' }));
if (email && password) {
await page.fill('[name=email]', email);
await page.fill('[name=password]', password);
@@ -66,7 +66,7 @@ try {
page.waitForURL('**/ap/mfa**').then(async () => {
console.log('Two-Step Verification - enter the One Time Password (OTP), e.g. generated by your Authenticator App');
await page.check('[name=rememberDevice]');
- 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
+ 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 page.locator('input[name=otpCode]').pressSequentially(otp.toString());
await page.click('input[type="submit"]');
}).catch(_ => { });
@@ -128,10 +128,10 @@ try {
const slug = await card.locator('a:has-text("Claim")').first().getAttribute('href');
const url = 'https://gaming.amazon.com' + slug.split('?')[0];
// await (await card.$('text=Claim')).click(); // goes to URL of game, no need to wait
- external_info.push({title, url});
+ external_info.push({ title, url });
}
- for (const {title, url} of external_info) {
- console.log('Current free game:', title); //, url);
+ for (const { title, url } of external_info) {
+ console.log('Current free game:', title); // , url);
await page.goto(url, { waitUntil: 'domcontentloaded' });
if (cfg.debug) await page.pause();
if (cfg.dryrun) continue;
@@ -224,9 +224,9 @@ try {
await page2.click('[type="submit"]'); // click Redeem
const r2t = await (await r2).text();
if (r2t == '{}') {
- redeem_action = 'redeemed';
- console.log(' Redeemed successfully.');
- db.data[user][title].status = 'claimed and redeemed';
+ redeem_action = 'redeemed';
+ console.log(' Redeemed successfully.');
+ db.data[user][title].status = 'claimed and redeemed';
} else {
console.debug(` Response 2: ${r2t}`);
console.log(' Unknown Response 2 - please report in https://github.com/vogler/free-games-claimer/issues/5');
@@ -298,7 +298,7 @@ try {
await page.keyboard.press('End'); // scroll to bottom to show all games
await page.waitForTimeout(1000); // wait for fade in animation
const viewportSize = page.viewportSize(); // current viewport size
- await page.setViewportSize({...viewportSize, height: 3000}); // increase height, otherwise element screenshot is cut off at the top and bottom
+ await page.setViewportSize({ ...viewportSize, height: 3000 }); // increase height, otherwise element screenshot is cut off at the top and bottom
await games.screenshot({ path: p }); // screenshot of all claimed games
}
@@ -357,7 +357,7 @@ try {
console.debug(' LinkAccountButton label:', unlinked_store);
const match = unlinked_store.match(/Link (.*) account/);
if (match && match.length == 2) unlinked_store = match[1];
- } else if(await page.locator('text=Link game account').count()) { // epic-games only?
+ } else if (await page.locator('text=Link game account').count()) { // epic-games only?
console.error(' Missing account linking (epic-games specific button?):', await page.locator('button[data-a-target="gms-cta"]').innerText()); // TODO needed?
unlinked_store = 'epic-games';
}
@@ -386,8 +386,7 @@ try {
process.exitCode ||= 1;
console.error('--- Exception:');
console.error(error); // .toString()?
- if (error.message && process.exitCode != 130)
- notify(`prime-gaming failed: ${error.message.split('\n')[0]}`);
+ if (error.message && process.exitCode != 130) notify(`prime-gaming failed: ${error.message.split('\n')[0]}`);
} finally {
await db.write(); // write out json db
if (notify_games.length) { // list should only include claimed games
diff --git a/unrealengine.js b/unrealengine.js
index 64b9c55..5bbf988 100644
--- a/unrealengine.js
+++ b/unrealengine.js
@@ -23,7 +23,7 @@ const context = await firefox.launchPersistentContext(cfg.dir.browser, {
viewport: { width: cfg.width, height: cfg.height },
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 for firefox: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:106.0) Gecko/20100101 Firefox/106.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/ue-${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
@@ -42,7 +42,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
@@ -52,12 +52,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);
@@ -71,7 +71,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(_ => { });
@@ -105,7 +105,7 @@ try {
const notify_game = { title, url, status: 'failed' };
notify_games.push(notify_game); // status is updated below
// if (await p.locator('.btn .add-review-btn').count()) { // did not work
- if((await p.getAttribute('class')).includes('asset--owned')) {
+ if ((await p.getAttribute('class')).includes('asset--owned')) {
console.log(' ↳ Already claimed');
if (db.data[user][id].status != 'claimed') {
db.data[user][id].status = 'existed';
@@ -128,7 +128,7 @@ try {
const price = (await page.locator('.shopping-cart .total .price').innerText()).split(' ');
console.log('Price: ', price[1], 'instead of', price[0]);
if (price[1] != '0') {
- const err = 'Price is not 0! Exit! Please report.'
+ const err = 'Price is not 0! Exit! Please report.';
console.error(err);
notify('unrealengine: ' + err);
process.exit(1);
@@ -142,7 +142,7 @@ try {
// maybe: Accept End User License Agreement
page.locator('[name=accept-label]').check().then(() => {
console.log('Accept End User License Agreement');
- page.locator('span:text-is("Accept")').click() // otherwise matches 'Accept All Cookies'
+ page.locator('span:text-is("Accept")').click(); // otherwise matches 'Accept All Cookies'
}).catch(_ => { });
await page.waitForSelector('#webPurchaseContainer iframe'); // TODO needed?
const iframe = page.frameLocator('#webPurchaseContainer iframe');
@@ -165,7 +165,7 @@ try {
const captcha = iframe.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.')
+ 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.');
}).catch(_ => { }); // may time out if not shown
await page.waitForSelector('text=Thank you');
for (const id of ids) {
@@ -192,8 +192,7 @@ try {
process.exitCode ||= 1;
console.error('--- Exception:');
console.error(error); // .toString()?
- if (error.message && process.exitCode != 130)
- notify(`unrealengine failed: ${error.message.split('\n')[0]}`);
+ if (error.message && process.exitCode != 130) notify(`unrealengine 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
@@ -201,5 +200,5 @@ try {
}
}
if (cfg.debug) writeFileSync(path.resolve(cfg.dir.browser, 'cookies.json'), JSON.stringify(await context.cookies()));
-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();
diff --git a/util.js b/util.js
index 0ab0912..16ae806 100644
--- a/util.js
+++ b/util.js
@@ -27,7 +27,7 @@ export const handleSIGINT = (context = null) => process.on('SIGINT', async () =>
if (context) await context.close(); // in order to save recordings also on SIGINT, we need to disable Playwright's handleSIGINT and close the context ourselves
});
-export const stealth = async (context) => {
+export const stealth = async context => {
// stealth with playwright: https://github.com/berstend/puppeteer-extra/issues/454#issuecomment-917437212
// https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra-plugin-stealth/evasions
const enabledEvasions = [
@@ -47,13 +47,13 @@ export const stealth = async (context) => {
'sourceurl',
// 'user-agent-override', // doesn't work since playwright has no page.browser()
'webgl.vendor',
- 'window.outerdimensions'
+ 'window.outerdimensions',
];
const stealth = {
callbacks: [],
async evaluateOnNewDocument(...args) {
this.callbacks.push({ cb: args[0], a: args[1] });
- }
+ },
};
for (const e of enabledEvasions) {
const evasion = await import(`puppeteer-extra-plugin-stealth/evasions/${e}/index.js`);
@@ -70,7 +70,10 @@ export const stealth = async (context) => {
import Enquirer from 'enquirer'; const enquirer = new Enquirer();
const timeoutPlugin = timeout => enquirer => { // cancel prompt after timeout ms
enquirer.on('prompt', prompt => {
- const t = setTimeout(() => { prompt.hint = () => 'timeout'; prompt.cancel(); }, timeout);
+ const t = setTimeout(() => {
+ prompt.hint = () => 'timeout';
+ prompt.cancel();
+ }, timeout);
prompt.on('submit', _ => clearTimeout(t));
prompt.on('cancel', _ => clearTimeout(t));
});
@@ -78,14 +81,14 @@ const timeoutPlugin = timeout => enquirer => { // cancel prompt after timeout ms
enquirer.use(timeoutPlugin(cfg.login_timeout)); // TODO may not want to have this timeout for all prompts; better extend Prompt and add a timeout prompt option
// single prompt that just returns the non-empty value instead of an object
// @ts-ignore
-export const prompt = o => enquirer.prompt({name: 'name', type: 'input', message: 'Enter value', ...o}).then(r => r.name).catch(_ => {});
-export const confirm = o => prompt({type: 'confirm', message: 'Continue?', ...o});
+export const prompt = o => enquirer.prompt({ name: 'name', type: 'input', message: 'Enter value', ...o }).then(r => r.name).catch(_ => {});
+export const confirm = o => prompt({ type: 'confirm', message: 'Continue?', ...o });
// notifications via apprise CLI
import { exec } from 'child_process';
import { cfg } from './config.js';
-export const notify = (html) => new Promise((resolve, reject) => {
+export const notify = html => new Promise((resolve, reject) => {
if (!cfg.notify) return resolve();
const title = cfg.notify_title ? `-t ${cfg.notify_title}` : '';
exec(`apprise ${cfg.notify} -i html '${title}' -b '${html}'`, (error, stdout, stderr) => {
@@ -102,6 +105,6 @@ export const notify = (html) => new Promise((resolve, reject) => {
});
});
-export const escapeHtml = (unsafe) => unsafe.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"').replaceAll("'", ''');
+export const escapeHtml = unsafe => unsafe.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"').replaceAll('\'', ''');
export const html_game_list = games => games.map(g => `- ${escapeHtml(g.title)} (${g.status})`).join('
');
diff --git a/version.js b/version.js
index d992afb..7f875cf 100644
--- a/version.js
+++ b/version.js
@@ -1,9 +1,9 @@
// check if running the latest version
-import {log} from 'console';
+import { log } from 'console';
import { exec } from 'child_process';
-const execp = (cmd) => new Promise((resolve, reject) => {
+const execp = cmd => new Promise((resolve, reject) => {
exec(cmd, (error, stdout, stderr) => {
if (stderr) console.error(`stderr: ${stderr}`);
// if (stdout) console.log(`stdout: ${stdout}`);
@@ -35,8 +35,8 @@ if (process.env.NOVNC_PORT) {
}
const gh = await (await fetch('https://api.github.com/repos/vogler/free-games-claimer/commits/main', {
- // headers: { accept: 'application/vnd.github.VERSION.sha' }
- })).json();
+ // headers: { accept: 'application/vnd.github.VERSION.sha' }
+})).json();
// log(gh);
log('Local commit:', sha, new Date(date));
diff --git a/xbox.js b/xbox.js
index a2150f1..54cccf2 100644
--- a/xbox.js
+++ b/xbox.js
@@ -1,37 +1,37 @@
-import { firefox } from "playwright-firefox"; // stealth plugin needs no outdated playwright-extra
-import { authenticator } from "otplib";
+import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdated playwright-extra
+import { authenticator } from 'otplib';
import {
- datetime,
- handleSIGINT,
- html_game_list,
- jsonDb,
- notify,
- prompt,
-} from "./util.js";
-import { cfg } from "./config.js";
+ datetime,
+ handleSIGINT,
+ html_game_list,
+ jsonDb,
+ notify,
+ prompt,
+} from './util.js';
+import { cfg } from './config.js';
// ### SETUP
-const URL_CLAIM = "https://www.xbox.com/en-US/live/gold"; // #gameswithgold";
+const URL_CLAIM = 'https://www.xbox.com/en-US/live/gold'; // #gameswithgold";
-console.log(datetime(), "started checking xbox");
+console.log(datetime(), 'started checking xbox');
-const db = await jsonDb("xbox.json");
+const db = await jsonDb('xbox.json');
db.data ||= {};
handleSIGINT();
// https://playwright.dev/docs/auth#multi-factor-authentication
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
+ 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
});
if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
const page = context.pages().length
- ? context.pages()[0]
- : await context.newPage(); // should always exist
+ ? context.pages()[0]
+ : await context.newPage(); // should always exist
const notify_games = [];
let user;
@@ -39,216 +39,212 @@ let user;
main();
async function main() {
- try {
- await performLogin();
- await getAndSaveUser();
- await redeemFreeGames();
- } catch (error) {
- console.error(error);
- process.exitCode ||= 1;
- if (error.message && process.exitCode != 130)
- notify(`xbox 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(`xbox (${user}):
${html_game_list(notify_games)}`);
- }
- await context.close();
+ try {
+ await performLogin();
+ await getAndSaveUser();
+ await redeemFreeGames();
+ } catch (error) {
+ console.error(error);
+ process.exitCode ||= 1;
+ if (error.message && process.exitCode != 130) notify(`xbox 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(`xbox (${user}):
${html_game_list(notify_games)}`);
}
+ await context.close();
+ }
}
async function performLogin() {
- await page.goto(URL_CLAIM, { waitUntil: "domcontentloaded" }); // default 'load' takes forever
+ await page.goto(URL_CLAIM, { waitUntil: 'domcontentloaded' }); // default 'load' takes forever
- const signInLocator = page
- .getByRole("link", {
- name: "Sign in to your account",
- })
- .first();
- const usernameLocator = page
- .getByRole("button", {
- name: "Account manager for",
- })
- .first();
+ const signInLocator = page
+ .getByRole('link', {
+ name: 'Sign in to your account',
+ })
+ .first();
+ const usernameLocator = page
+ .getByRole('button', {
+ name: 'Account manager for',
+ })
+ .first();
- await Promise.any([signInLocator.waitFor(), usernameLocator.waitFor()]);
+ await Promise.any([signInLocator.waitFor(), usernameLocator.waitFor()]);
- if (await usernameLocator.isVisible()) {
- return; // logged in using saved cookie
- } else if (await signInLocator.isVisible()) {
- console.error("Not signed in anymore.");
- await signInLocator.click();
- await signInToXbox();
- } else {
- console.error("lost! where am i?");
- }
+ if (await usernameLocator.isVisible()) {
+ return; // logged in using saved cookie
+ } else if (await signInLocator.isVisible()) {
+ console.error('Not signed in anymore.');
+ await signInLocator.click();
+ await signInToXbox();
+ } else {
+ console.error('lost! where am i?');
+ }
}
async function signInToXbox() {
- page.waitForLoadState("domcontentloaded");
- 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!`);
+ page.waitForLoadState('domcontentloaded');
+ 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!`);
- // ### FETCH EMAIL/PASS
- if (cfg.xbox_email && cfg.xbox_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.xbox_email || (await prompt({ message: "Enter email" }));
- const password =
+ // ### FETCH EMAIL/PASS
+ if (cfg.xbox_email && cfg.xbox_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.xbox_email || await prompt({ message: 'Enter email' });
+ const password =
email &&
(cfg.xbox_password ||
- (await prompt({
- type: "password",
- message: "Enter password",
- })));
+ await prompt({
+ type: 'password',
+ message: 'Enter password',
+ }));
// ### FILL IN EMAIL/PASS
- if (email && password) {
- const usernameLocator = page
- .getByPlaceholder("Email, phone, or Skype")
- .first();
- const passwordLocator = page.getByPlaceholder("Password").first();
+ if (email && password) {
+ const usernameLocator = page
+ .getByPlaceholder('Email, phone, or Skype')
+ .first();
+ const passwordLocator = page.getByPlaceholder('Password').first();
- await Promise.any([
- usernameLocator.waitFor(),
- passwordLocator.waitFor(),
- ]);
+ await Promise.any([
+ usernameLocator.waitFor(),
+ passwordLocator.waitFor(),
+ ]);
- // username may already be saved from before, if so, skip to filling in password
- if (await page.getByPlaceholder("Email, phone, or Skype").isVisible()) {
- await usernameLocator.fill(email);
- await page.getByRole("button", { name: "Next" }).click();
- }
-
- await passwordLocator.fill(password);
- await page.getByRole("button", { name: "Sign in" }).click();
-
- // handle MFA, but don't await it
- page.locator('input[name="otc"]')
- .waitFor()
- .then(async () => {
- console.log("Two-Step Verification - Enter security code");
- console.log(
- await page
- .locator('div[data-bind="text: description"]')
- .innerText()
- );
- const otp =
- (cfg.xbox_otpkey &&
- authenticator.generate(cfg.xbox_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.type('input[name="otc"]', otp.toString());
- await page
- .getByLabel("Don't ask me again on this device")
- .check(); // Trust this Browser
- await page.getByRole("button", { name: "Verify" }).click();
- })
- .catch((_) => {});
-
- // Trust this browser, but don't await it
- page.getByLabel("Don't show this again")
- .waitFor()
- .then(async () => {
- await page.getByLabel("Don't show this again").check();
- await page.getByRole("button", { name: "Yes" }).click();
- })
- .catch((_) => {});
- } else {
- console.log("Waiting for you to login in the browser.");
- await notify(
- "xbox: no longer signed in and not enough options set for automatic login."
- );
- if (cfg.headless) {
- console.log(
- "Run `SHOW=1 node xbox` to login in the opened browser."
- );
- await context.close();
- process.exit(1);
- }
+ // username may already be saved from before, if so, skip to filling in password
+ if (await page.getByPlaceholder('Email, phone, or Skype').isVisible()) {
+ await usernameLocator.fill(email);
+ await page.getByRole('button', { name: 'Next' }).click();
}
- // ### VERIFY SIGNED IN
- await page.waitForURL(`${URL_CLAIM}**`);
+ await passwordLocator.fill(password);
+ await page.getByRole('button', { name: 'Sign in' }).click();
- if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
+ // handle MFA, but don't await it
+ page.locator('input[name="otc"]')
+ .waitFor()
+ .then(async () => {
+ console.log('Two-Step Verification - Enter security code');
+ console.log(
+ await page
+ .locator('div[data-bind="text: description"]')
+ .innerText(),
+ );
+ const otp =
+ cfg.xbox_otpkey &&
+ authenticator.generate(cfg.xbox_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.type('input[name="otc"]', otp.toString());
+ await page
+ .getByLabel('Don\'t ask me again on this device')
+ .check(); // Trust this Browser
+ await page.getByRole('button', { name: 'Verify' }).click();
+ })
+ .catch(_ => {});
+
+ // Trust this browser, but don't await it
+ page.getByLabel('Don\'t show this again')
+ .waitFor()
+ .then(async () => {
+ await page.getByLabel('Don\'t show this again').check();
+ await page.getByRole('button', { name: 'Yes' }).click();
+ })
+ .catch(_ => {});
+ } else {
+ console.log('Waiting for you to login in the browser.');
+ await notify(
+ 'xbox: no longer signed in and not enough options set for automatic login.',
+ );
+ if (cfg.headless) {
+ console.log(
+ 'Run `SHOW=1 node xbox` to login in the opened browser.',
+ );
+ await context.close();
+ process.exit(1);
+ }
+ }
+
+ // ### VERIFY SIGNED IN
+ await page.waitForURL(`${URL_CLAIM}**`);
+
+ if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
}
async function getAndSaveUser() {
- user = await page.locator("#mectrl_currentAccount_primary").innerHTML();
- console.log(`Signed in as '${user}'`);
- db.data[user] ||= {};
+ user = await page.locator('#mectrl_currentAccount_primary').innerHTML();
+ console.log(`Signed in as '${user}'`);
+ db.data[user] ||= {};
}
async function redeemFreeGames() {
- const monthlyGamesLocator = await page.locator(".f-size-large").all();
+ const monthlyGamesLocator = await page.locator('.f-size-large').all();
- const monthlyGamesPageLinks = await Promise.all(
- monthlyGamesLocator.map(
- async (el) => await el.locator("a").getAttribute("href")
- )
- );
- console.log("Free games:", monthlyGamesPageLinks);
+ const monthlyGamesPageLinks = await Promise.all(
+ monthlyGamesLocator.map(
+ async el => await el.locator('a').getAttribute('href'),
+ ),
+ );
+ console.log('Free games:', monthlyGamesPageLinks);
- for (const url of monthlyGamesPageLinks) {
- await page.goto(url);
+ for (const url of monthlyGamesPageLinks) {
+ await page.goto(url);
- const title = await page.locator("h1").first().innerText();
- 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);
- const notify_game = { title, url, status: "failed" };
- notify_games.push(notify_game); // status is updated below
+ const title = await page.locator('h1').first().innerText();
+ 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);
+ const notify_game = { title, url, status: 'failed' };
+ notify_games.push(notify_game); // status is updated below
- // SELECTORS
- const getBtnLocator = page.getByText("GET", { exact: true }).first();
- const installToLocator = page
- .getByText("INSTALL TO", { exact: true })
- .first();
+ // SELECTORS
+ const getBtnLocator = page.getByText('GET', { exact: true }).first();
+ const installToLocator = page
+ .getByText('INSTALL TO', { exact: true })
+ .first();
- await Promise.any([
- getBtnLocator.waitFor(),
- installToLocator.waitFor(),
- ]);
+ await Promise.any([
+ getBtnLocator.waitFor(),
+ installToLocator.waitFor(),
+ ]);
- if (await installToLocator.isVisible()) {
- console.log(" Already in library! Nothing to claim.");
- notify_game.status = "existed";
- db.data[user][game_id].status ||= "existed"; // does not overwrite claimed or failed
- } else if (await getBtnLocator.isVisible()) {
- console.log(" Not in library yet! Click GET.");
- await getBtnLocator.click();
+ if (await installToLocator.isVisible()) {
+ console.log(' Already in library! Nothing to claim.');
+ notify_game.status = 'existed';
+ db.data[user][game_id].status ||= 'existed'; // does not overwrite claimed or failed
+ } else if (await getBtnLocator.isVisible()) {
+ console.log(' Not in library yet! Click GET.');
+ await getBtnLocator.click();
- // wait for popup
- await page
- .locator('iframe[name="purchase-sdk-hosted-iframe"]')
- .waitFor();
- const popupLocator = page.frameLocator(
- "[name=purchase-sdk-hosted-iframe]"
- );
+ // wait for popup
+ await page
+ .locator('iframe[name="purchase-sdk-hosted-iframe"]')
+ .waitFor();
+ const popupLocator = page.frameLocator(
+ '[name=purchase-sdk-hosted-iframe]',
+ );
- const finalGetBtnLocator = popupLocator.getByText("GET");
- await finalGetBtnLocator.waitFor();
- await finalGetBtnLocator.click();
+ const finalGetBtnLocator = popupLocator.getByText('GET');
+ await finalGetBtnLocator.waitFor();
+ await finalGetBtnLocator.click();
- await page.getByText("Thank you for your purchase.").waitFor();
- notify_game.status = "claimed";
- db.data[user][game_id].status = "claimed";
- db.data[user][game_id].time = datetime(); // claimed time overwrites failed/dryrun time
- console.log(" Claimed successfully!");
- }
-
- // notify_game.status = db.data[user][game_id].status; // claimed or failed
-
- // const p = path.resolve(cfg.dir.screenshots, playstation-plus', `${game_id}.png`);
- // if (!existsSync(p)) await page.screenshot({ path: p, fullPage: false }); // fullPage is quite long...
+ await page.getByText('Thank you for your purchase.').waitFor();
+ notify_game.status = 'claimed';
+ db.data[user][game_id].status = 'claimed';
+ db.data[user][game_id].time = datetime(); // claimed time overwrites failed/dryrun time
+ console.log(' Claimed successfully!');
}
+
+ // notify_game.status = db.data[user][game_id].status; // claimed or failed
+
+ // const p = path.resolve(cfg.dir.screenshots, playstation-plus', `${game_id}.png`);
+ // if (!existsSync(p)) await page.screenshot({ path: p, fullPage: false }); // fullPage is quite long...
+ }
}
From d4685ff370e12dea6d7ecfaa6d96c133a87d9fcc Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 8 Nov 2023 01:32:20 +0100
Subject: [PATCH 116/253] gha: sonar: install dev deps for eslint plugins
---
.github/workflows/sonar.yml | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml
index bb2f89f..3300fac 100644
--- a/.github/workflows/sonar.yml
+++ b/.github/workflows/sonar.yml
@@ -10,18 +10,17 @@ jobs:
sonarcloud:
runs-on: ubuntu-latest
steps:
- -
- uses: actions/checkout@v4
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v3
with:
- # Disabling shallow clone is recommended for improving relevancy of reporting
- fetch-depth: 0
+ cache: 'npm'
-
- name: Install modules
- run: npm install -g eslint
+ name: Install dev dependencies which includde ESLint + plugins
+ run: npm install --only=dev
-
name: Run ESLint
continue-on-error: true
- run: eslint . --ext .js,.ts -f json -o eslint_report.json
+ run: npx eslint . -f json -o eslint_report.json
-
name: Fix ESLint paths
run: sed -i 's+/home/runner/work/free-games-claimer/free-games-claimer+/github/workspace+g' eslint_report.json
From c5f75e06c8e583013556240cd9ddda67e2085651 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 8 Nov 2023 01:36:35 +0100
Subject: [PATCH 117/253] gha: sonar: disable shallow clone (irrelevant) to get
rid of warning
---
.github/workflows/sonar.yml | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml
index 3300fac..b532c17 100644
--- a/.github/workflows/sonar.yml
+++ b/.github/workflows/sonar.yml
@@ -10,8 +10,13 @@ jobs:
sonarcloud:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-node@v3
+ -
+ uses: actions/checkout@v4
+ with:
+ # Disabling shallow clone is recommended for improving relevancy of reporting. Otherwise sonarcloud will show a warning.
+ fetch-depth: 0
+ -
+ uses: actions/setup-node@v3
with:
cache: 'npm'
-
From 110b2ea4c643e71cd6986526f2a8a17931b136f9 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 8 Nov 2023 02:28:34 +0100
Subject: [PATCH 118/253] eslint: migrate to new flat config (.eslintrc.cjs ->
eslint.config.js)
https://eslint.org/docs/latest/use/configure/configuration-files-new
https://eslint.org/docs/latest/use/configure/migration-guide
---
.eslintignore | 1 -
.eslintrc.cjs | 78 ------------------------------------------------
eslint.config.js | 75 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 75 insertions(+), 79 deletions(-)
delete mode 120000 .eslintignore
delete mode 100644 .eslintrc.cjs
create mode 100644 eslint.config.js
diff --git a/.eslintignore b/.eslintignore
deleted file mode 120000
index 3e4e48b..0000000
--- a/.eslintignore
+++ /dev/null
@@ -1 +0,0 @@
-.gitignore
\ No newline at end of file
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
deleted file mode 100644
index 04989aa..0000000
--- a/.eslintrc.cjs
+++ /dev/null
@@ -1,78 +0,0 @@
-module.exports = {
- env: {
- es2021: true,
- node: true,
- },
- extends: 'eslint:recommended',
- overrides: [
- {
- env: {
- node: true,
- },
- files: [
- '.eslintrc.{js,cjs}',
- ],
- parserOptions: {
- sourceType: 'script',
- },
- },
- ],
- parserOptions: {
- ecmaVersion: 'latest',
- sourceType: 'module',
- },
- plugins: [
- '@stylistic/js',
- ],
- // https://eslint.org/docs/latest/rules/
- // https://eslint.style/packages/js
- rules: {
- 'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
- '@stylistic/js/array-bracket-newline': ['error', 'consistent'],
- '@stylistic/js/array-bracket-spacing': 'error',
- '@stylistic/js/array-element-newline': ['error', 'consistent'],
- '@stylistic/js/arrow-parens': ['error', 'as-needed'],
- '@stylistic/js/arrow-spacing': 'error',
- '@stylistic/js/block-spacing': 'error',
- '@stylistic/js/brace-style': 'error',
- '@stylistic/js/comma-dangle': ['error', 'always-multiline'],
- '@stylistic/js/comma-spacing': 'error',
- '@stylistic/js/comma-style': 'error',
- '@stylistic/js/eol-last': 'error',
- '@stylistic/js/func-call-spacing': 'error',
- '@stylistic/js/function-paren-newline': ['error', 'consistent'],
- '@stylistic/js/implicit-arrow-linebreak': 'error',
- '@stylistic/js/indent': ['error', 2],
- '@stylistic/js/key-spacing': 'error',
- '@stylistic/js/keyword-spacing': 'error',
- '@stylistic/js/linebreak-style': 'error',
- '@stylistic/js/no-extra-parens': 'error',
- '@stylistic/js/no-extra-semi': 'error',
- '@stylistic/js/no-mixed-spaces-and-tabs': 'error',
- '@stylistic/js/no-multi-spaces': 'error',
- '@stylistic/js/no-multiple-empty-lines': 'error',
- '@stylistic/js/no-tabs': 'error',
- '@stylistic/js/no-trailing-spaces': 'error',
- '@stylistic/js/no-whitespace-before-property': 'error',
- '@stylistic/js/nonblock-statement-body-position': 'error',
- '@stylistic/js/object-curly-newline': 'error',
- '@stylistic/js/object-curly-spacing': ['error', 'always'],
- '@stylistic/js/object-property-newline': ['error', { allowAllPropertiesOnSameLine: true }],
- '@stylistic/js/quote-props': ['error', 'as-needed'],
- '@stylistic/js/quotes': ['error', 'single'],
- '@stylistic/js/rest-spread-spacing': 'error',
- '@stylistic/js/semi': 'error',
- '@stylistic/js/semi-spacing': 'error',
- '@stylistic/js/semi-style': 'error',
- '@stylistic/js/space-before-blocks': 'error',
- '@stylistic/js/space-before-function-paren': ['error', { anonymous: 'never', named: 'never', asyncArrow: 'always' }],
- '@stylistic/js/space-in-parens': 'error',
- '@stylistic/js/space-infix-ops': 'error',
- '@stylistic/js/space-unary-ops': 'error',
- '@stylistic/js/spaced-comment': 'error',
- '@stylistic/js/switch-colon-spacing': 'error',
- '@stylistic/js/template-curly-spacing': 'error',
- '@stylistic/js/template-tag-spacing': 'error',
- '@stylistic/js/wrap-regex': 'error',
- },
-};
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 0000000..bc6447f
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,75 @@
+// https://eslint.org/docs/latest/use/configure/configuration-files-new
+// https://eslint.org/docs/latest/use/configure/migration-guide
+import js from '@eslint/js';
+import globals from 'globals';
+import stylistic from '@stylistic/eslint-plugin-js';
+
+export default [
+ // https://eslint.org/docs/latest/use/configure/configuration-files-new#globally-ignoring-files-with-ignores
+ // object with just `ignores` applies to all configuration objects
+ // had `ln -s .gitignore .eslintignore` before, but .eslintignore no longer supported
+ {
+ ignores: ['data/**'],
+ },
+ js.configs.recommended, // TODO still needed?
+ {
+ // files: ['*.js'],
+ languageOptions: {
+ globals: globals.node,
+ },
+ plugins: {
+ '@stylistic/js': stylistic,
+ },
+ // https://eslint.org/docs/latest/rules/
+ // https://eslint.style/packages/js
+ rules: {
+ 'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
+ '@stylistic/js/array-bracket-newline': ['error', 'consistent'],
+ '@stylistic/js/array-bracket-spacing': 'error',
+ '@stylistic/js/array-element-newline': ['error', 'consistent'],
+ '@stylistic/js/arrow-parens': ['error', 'as-needed'],
+ '@stylistic/js/arrow-spacing': 'error',
+ '@stylistic/js/block-spacing': 'error',
+ '@stylistic/js/brace-style': 'error',
+ '@stylistic/js/comma-dangle': ['error', 'always-multiline'],
+ '@stylistic/js/comma-spacing': 'error',
+ '@stylistic/js/comma-style': 'error',
+ '@stylistic/js/eol-last': 'error',
+ '@stylistic/js/func-call-spacing': 'error',
+ '@stylistic/js/function-paren-newline': ['error', 'consistent'],
+ '@stylistic/js/implicit-arrow-linebreak': 'error',
+ '@stylistic/js/indent': ['error', 2],
+ '@stylistic/js/key-spacing': 'error',
+ '@stylistic/js/keyword-spacing': 'error',
+ '@stylistic/js/linebreak-style': 'error',
+ '@stylistic/js/no-extra-parens': 'error',
+ '@stylistic/js/no-extra-semi': 'error',
+ '@stylistic/js/no-mixed-spaces-and-tabs': 'error',
+ '@stylistic/js/no-multi-spaces': 'error',
+ '@stylistic/js/no-multiple-empty-lines': 'error',
+ '@stylistic/js/no-tabs': 'error',
+ '@stylistic/js/no-trailing-spaces': 'error',
+ '@stylistic/js/no-whitespace-before-property': 'error',
+ '@stylistic/js/nonblock-statement-body-position': 'error',
+ '@stylistic/js/object-curly-newline': 'error',
+ '@stylistic/js/object-curly-spacing': ['error', 'always'],
+ '@stylistic/js/object-property-newline': ['error', { allowAllPropertiesOnSameLine: true }],
+ '@stylistic/js/quote-props': ['error', 'as-needed'],
+ '@stylistic/js/quotes': ['error', 'single'],
+ '@stylistic/js/rest-spread-spacing': 'error',
+ '@stylistic/js/semi': 'error',
+ '@stylistic/js/semi-spacing': 'error',
+ '@stylistic/js/semi-style': 'error',
+ '@stylistic/js/space-before-blocks': 'error',
+ '@stylistic/js/space-before-function-paren': ['error', { anonymous: 'never', named: 'never', asyncArrow: 'always' }],
+ '@stylistic/js/space-in-parens': 'error',
+ '@stylistic/js/space-infix-ops': 'error',
+ '@stylistic/js/space-unary-ops': 'error',
+ '@stylistic/js/spaced-comment': 'error',
+ '@stylistic/js/switch-colon-spacing': 'error',
+ '@stylistic/js/template-curly-spacing': 'error',
+ '@stylistic/js/template-tag-spacing': 'error',
+ '@stylistic/js/wrap-regex': 'error',
+ },
+ },
+];
From 739fb0dbfe19d065a68d39fc1b971e58b83d4b85 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 8 Nov 2023 02:30:21 +0100
Subject: [PATCH 119/253] vscode workspace settings for formatOnSave with
`eslint --fix` using flat config
https://eslint.style/guide/faq#vs-code
---
.vscode/settings.json | 9 +++++++++
1 file changed, 9 insertions(+)
create mode 100644 .vscode/settings.json
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..c2cd6b0
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,9 @@
+{
+ // https://eslint.style/guide/faq#vs-code
+ "editor.formatOnSave": true,
+ "editor.formatOnSaveMode": "modifications",
+ "editor.codeActionsOnSave": {
+ "source.fixAll.eslint": true
+ },
+ "eslint.experimental.useFlatConfig": true,
+}
From a7cc68b6db41a9542684099583c9127a9b703fa9 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 9 Nov 2023 01:41:01 +0100
Subject: [PATCH 120/253] ue: login: fix #248, similar to #236
Commits for #236:
https://github.com/vogler/free-games-claimer/commit/d73a523fe7f76608af5db4e72b567f058814fea1
https://github.com/vogler/free-games-claimer/commit/a374d483451f1fa19c0f9f461e32cb37a4b46fd7
---
unrealengine.js | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/unrealengine.js b/unrealengine.js
index 5bbf988..cd2eb33 100644
--- a/unrealengine.js
+++ b/unrealengine.js
@@ -48,7 +48,7 @@ try {
await page.waitForResponse(r => r.request().method() == 'POST' && r.url().startsWith('https://graphql.unrealengine.com/ue/graphql'));
- while (await page.locator('.display-name').count() == 0) {
+ while (await page.locator('unrealengine-navigation').getAttribute('isloggedin') != 'true') {
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
@@ -59,8 +59,9 @@ try {
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.click('text=Sign in with Epic Games');
await page.fill('#email', email);
+ await page.click('button[type="submit"]');
await page.fill('#password', password);
await page.click('button[type="submit"]');
page.waitForSelector('#h_captcha_challenge_login_prod iframe').then(() => {
@@ -88,7 +89,7 @@ try {
if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
}
await page.waitForTimeout(1000);
- user = await page.locator('.display-name').first().innerHTML();
+ user = await page.locator('unrealengine-navigation').getAttribute('displayname'); // 'null' if !isloggedin
console.log(`Signed in as ${user}`);
db.data[user] ||= {};
From 3ddf1720bb10d1995bcf36a924805d5349ff8b63 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 9 Nov 2023 02:34:17 +0100
Subject: [PATCH 121/253] notify: use execFile with arg array instead of exec
to avoid shell-escape, fixes #239
Also proper fix for https://github.com/vogler/free-games-claimer/pull/167
https://www.npmjs.com/package/shell-escape
https://stackoverflow.com/questions/1779858/how-do-i-escape-a-string-for-a-shell-command-in-node
https://nodejs.org/api/child_process.html#child_processexecfilefile-args-options-callback
---
notify-test.js | 5 ++++-
util.js | 14 ++++++++++----
2 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/notify-test.js b/notify-test.js
index 9d593c0..b9c293f 100644
--- a/notify-test.js
+++ b/notify-test.js
@@ -1,15 +1,18 @@
/* eslint-disable no-constant-condition */
import { delay, html_game_list, notify } from './util.js';
+import { cfg } from './config.js';
const URL_CLAIM = 'https://gaming.amazon.com/home'; // dummy URL
+console.debug('NOTIFY:', cfg.notify);
+
if (true) {
const notify_games = [
// { title: 'Kerbal Space Program', status: 'claimed', url: URL_CLAIM },
// { title: "Shadow Tactics - Aiko's Choice", status: 'claimed', url: URL_CLAIM },
{ title: 'Epistory - Typing Chronicles', status: 'claimed', url: URL_CLAIM },
];
- notify(`epic-games:
${html_game_list(notify_games)}`);
+ await notify(`epic-games:
${html_game_list(notify_games)}`);
}
if (false) {
diff --git a/util.js b/util.js
index 16ae806..acd6c0b 100644
--- a/util.js
+++ b/util.js
@@ -85,13 +85,19 @@ export const prompt = o => enquirer.prompt({ name: 'name', type: 'input', messag
export const confirm = o => prompt({ type: 'confirm', message: 'Continue?', ...o });
// notifications via apprise CLI
-import { exec } from 'child_process';
+import { execFile } from 'child_process';
import { cfg } from './config.js';
export const notify = html => new Promise((resolve, reject) => {
- if (!cfg.notify) return resolve();
- const title = cfg.notify_title ? `-t ${cfg.notify_title}` : '';
- exec(`apprise ${cfg.notify} -i html '${title}' -b '${html}'`, (error, stdout, stderr) => {
+ if (!cfg.notify) {
+ if (cfg.debug) console.debug('notify: NOTIFY is not set!');
+ return resolve();
+ }
+ // const cmd = `apprise '${cfg.notify}' ${title} -i html -b '${html}'`; // this had problems if e.g. ' was used in arg; could have `npm i shell-escape`, but instead using safer execFile which takes args as array instead of exec which spawned a shell to execute the command
+ const args = [cfg.notify, '-i', 'html', '-b', html];
+ if (cfg.notify_title) args.push(...['-t', cfg.notify_title]);
+ if (cfg.debug) console.debug(`apprise ${args.map(a => `'${a}'`).join(' ')}`); // this also doesn't escape, but it's just for info
+ execFile('apprise', args, (error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`);
if (error.message.includes('command not found')) {
From 4231b7dd28892878ce2f2181984cff6e62dce127 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 9 Nov 2023 02:43:43 +0100
Subject: [PATCH 122/253] gog: don't wait for screenshot to not be loading,
closes #240
---
gog.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/gog.js b/gog.js
index c686851..74623e6 100644
--- a/gog.js
+++ b/gog.js
@@ -99,7 +99,7 @@ try {
console.log(`Current free game: ${title} - ${url}`);
db.data[user][title] ||= { title, time: datetime(), url };
if (cfg.dryrun) process.exit(1);
- await page.locator('#giveaway:not(.is-loading)').waitFor(); // otherwise screenshot is sometimes with loading indicator instead of game title
+ // await page.locator('#giveaway:not(.is-loading)').waitFor(); // otherwise screenshot is sometimes with loading indicator instead of game title; #TODO fix, skipped due to timeout, see #240
await banner.screenshot({ path: screenshot(`${filenamify(title)}.png`) }); // overwrites every time - only keep first?
// await banner.getByRole('button', { name: 'Add to library' }).click();
From 324f6bffe723c951abe7fa8ab830edce79929de2 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 13 Nov 2023 06:01:59 +0000
Subject: [PATCH 123/253] build(deps): bump actions/setup-node from 3 to 4
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3 to 4.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v3...v4)
---
updated-dependencies:
- dependency-name: actions/setup-node
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
.github/workflows/sonar.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml
index b532c17..eb4ad62 100644
--- a/.github/workflows/sonar.yml
+++ b/.github/workflows/sonar.yml
@@ -16,7 +16,7 @@ jobs:
# Disabling shallow clone is recommended for improving relevancy of reporting. Otherwise sonarcloud will show a warning.
fetch-depth: 0
-
- uses: actions/setup-node@v3
+ uses: actions/setup-node@v4
with:
cache: 'npm'
-
From 5919d37efaabad98c303e087c4874cffb58b3cb9 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Mon, 20 Nov 2023 11:41:15 +0100
Subject: [PATCH 124/253] ncu -u: playwright-firefox 1.39.0 -> 1.40.0, eslint
---
package-lock.json | 138 +++++++++++++++++++++++-----------------------
package.json | 6 +-
2 files changed, 73 insertions(+), 71 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 4736f13..2b19e37 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,12 +14,12 @@
"enquirer": "^2.4.1",
"lowdb": "^6.1.1",
"otplib": "^12.0.1",
- "playwright-firefox": "^1.39.0",
+ "playwright-firefox": "^1.40.0",
"puppeteer-extra-plugin-stealth": "^2.11.2"
},
"devDependencies": {
- "@stylistic/eslint-plugin-js": "^1.0.1",
- "eslint": "^8.53.0"
+ "@stylistic/eslint-plugin-js": "^1.4.1",
+ "eslint": "^8.54.0"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -47,9 +47,9 @@
}
},
"node_modules/@eslint-community/regexpp": {
- "version": "4.8.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.1.tgz",
- "integrity": "sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==",
+ "version": "4.10.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
+ "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
"dev": true,
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
@@ -79,9 +79,9 @@
}
},
"node_modules/@eslint/js": {
- "version": "8.53.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz",
- "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==",
+ "version": "8.54.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz",
+ "integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -198,18 +198,22 @@
}
},
"node_modules/@stylistic/eslint-plugin-js": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.0.1.tgz",
- "integrity": "sha512-SfJlEnmBowaWx9GyN/7vQ/7jQP2wVQe5CcaoVL6V5nmCWl9Q+VSeJPSBOjB7XOYSYL1HoEQsvA+8Hy7Zt2XrnA==",
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.4.1.tgz",
+ "integrity": "sha512-WXHPEVw5PB7OML7cLwHJDEcCyLiP7vzKeBbSwmpHLK0oh0JYkoJfTg2hEdFuQT5rQxFy3KzCy9R1mZ0wgLjKrA==",
"dev": true,
"dependencies": {
- "@eslint-community/eslint-utils": "^4.4.0",
"acorn": "^8.11.2",
"escape-string-regexp": "^4.0.0",
"eslint-visitor-keys": "^3.4.3",
"espree": "^9.6.1",
- "esutils": "^2.0.3",
"graphemer": "^1.4.0"
+ },
+ "engines": {
+ "node": "^16.0.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.40.0"
}
},
"node_modules/@types/debug": {
@@ -498,15 +502,15 @@
}
},
"node_modules/eslint": {
- "version": "8.53.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz",
- "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==",
+ "version": "8.54.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz",
+ "integrity": "sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
"@eslint/eslintrc": "^2.1.3",
- "@eslint/js": "8.53.0",
+ "@eslint/js": "8.54.0",
"@humanwhocodes/config-array": "^0.11.13",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
@@ -695,17 +699,17 @@
}
},
"node_modules/flat-cache": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz",
- "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
+ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
"dev": true,
"dependencies": {
- "flatted": "^3.2.7",
+ "flatted": "^3.2.9",
"keyv": "^4.5.3",
"rimraf": "^3.0.2"
},
"engines": {
- "node": ">=12.0.0"
+ "node": "^10.12.0 || >=12.0.0"
}
},
"node_modules/flatted": {
@@ -818,9 +822,9 @@
}
},
"node_modules/ignore": {
- "version": "5.2.4",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
- "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
+ "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==",
"dev": true,
"engines": {
"node": ">= 4"
@@ -974,9 +978,9 @@
}
},
"node_modules/keyv": {
- "version": "4.5.3",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz",
- "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==",
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
"dev": true,
"dependencies": {
"json-buffer": "3.0.1"
@@ -1207,9 +1211,9 @@
}
},
"node_modules/playwright-core": {
- "version": "1.39.0",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz",
- "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==",
+ "version": "1.40.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.0.tgz",
+ "integrity": "sha512-fvKewVJpGeca8t0ipM56jkVSU6Eo0RmFvQ/MaCQNDYm+sdvKkMBBWTE1FdeMqIdumRaXXjZChWHvIzCGM/tA/Q==",
"bin": {
"playwright-core": "cli.js"
},
@@ -1218,12 +1222,12 @@
}
},
"node_modules/playwright-firefox": {
- "version": "1.39.0",
- "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.39.0.tgz",
- "integrity": "sha512-DFJRVL8mfOPyfiK8on34kYdvFeV0a0aNGRNUTPuHD2sv2+pMITPSosxRCgPvnFO3oWzEwVBVv/+c5E9fICY7gg==",
+ "version": "1.40.0",
+ "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.40.0.tgz",
+ "integrity": "sha512-01KUdoo9Sk9lMGUhlc9tuvWun/mspBINuAvGFm0RFS7ZaZ27uWD9WJ0MnJ6cWcIkL0nLgjlrWmvHgQs1fIq7LQ==",
"hasInstallScript": true,
"dependencies": {
- "playwright-core": "1.39.0"
+ "playwright-core": "1.40.0"
},
"bin": {
"playwright": "cli.js"
@@ -1630,9 +1634,9 @@
}
},
"@eslint-community/regexpp": {
- "version": "4.8.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.1.tgz",
- "integrity": "sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==",
+ "version": "4.10.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
+ "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
"dev": true
},
"@eslint/eslintrc": {
@@ -1653,9 +1657,9 @@
}
},
"@eslint/js": {
- "version": "8.53.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz",
- "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==",
+ "version": "8.54.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz",
+ "integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==",
"dev": true
},
"@humanwhocodes/config-array": {
@@ -1750,17 +1754,15 @@
}
},
"@stylistic/eslint-plugin-js": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.0.1.tgz",
- "integrity": "sha512-SfJlEnmBowaWx9GyN/7vQ/7jQP2wVQe5CcaoVL6V5nmCWl9Q+VSeJPSBOjB7XOYSYL1HoEQsvA+8Hy7Zt2XrnA==",
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.4.1.tgz",
+ "integrity": "sha512-WXHPEVw5PB7OML7cLwHJDEcCyLiP7vzKeBbSwmpHLK0oh0JYkoJfTg2hEdFuQT5rQxFy3KzCy9R1mZ0wgLjKrA==",
"dev": true,
"requires": {
- "@eslint-community/eslint-utils": "^4.4.0",
"acorn": "^8.11.2",
"escape-string-regexp": "^4.0.0",
"eslint-visitor-keys": "^3.4.3",
"espree": "^9.6.1",
- "esutils": "^2.0.3",
"graphemer": "^1.4.0"
}
},
@@ -1967,15 +1969,15 @@
"dev": true
},
"eslint": {
- "version": "8.53.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz",
- "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==",
+ "version": "8.54.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz",
+ "integrity": "sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==",
"dev": true,
"requires": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
"@eslint/eslintrc": "^2.1.3",
- "@eslint/js": "8.53.0",
+ "@eslint/js": "8.54.0",
"@humanwhocodes/config-array": "^0.11.13",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
@@ -2116,12 +2118,12 @@
}
},
"flat-cache": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz",
- "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
+ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
"dev": true,
"requires": {
- "flatted": "^3.2.7",
+ "flatted": "^3.2.9",
"keyv": "^4.5.3",
"rimraf": "^3.0.2"
}
@@ -2209,9 +2211,9 @@
"dev": true
},
"ignore": {
- "version": "5.2.4",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
- "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
+ "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==",
"dev": true
},
"import-fresh": {
@@ -2330,9 +2332,9 @@
}
},
"keyv": {
- "version": "4.5.3",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz",
- "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==",
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
"dev": true,
"requires": {
"json-buffer": "3.0.1"
@@ -2505,16 +2507,16 @@
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
"playwright-core": {
- "version": "1.39.0",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz",
- "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw=="
+ "version": "1.40.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.0.tgz",
+ "integrity": "sha512-fvKewVJpGeca8t0ipM56jkVSU6Eo0RmFvQ/MaCQNDYm+sdvKkMBBWTE1FdeMqIdumRaXXjZChWHvIzCGM/tA/Q=="
},
"playwright-firefox": {
- "version": "1.39.0",
- "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.39.0.tgz",
- "integrity": "sha512-DFJRVL8mfOPyfiK8on34kYdvFeV0a0aNGRNUTPuHD2sv2+pMITPSosxRCgPvnFO3oWzEwVBVv/+c5E9fICY7gg==",
+ "version": "1.40.0",
+ "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.40.0.tgz",
+ "integrity": "sha512-01KUdoo9Sk9lMGUhlc9tuvWun/mspBINuAvGFm0RFS7ZaZ27uWD9WJ0MnJ6cWcIkL0nLgjlrWmvHgQs1fIq7LQ==",
"requires": {
- "playwright-core": "1.39.0"
+ "playwright-core": "1.40.0"
}
},
"prelude-ls": {
diff --git a/package.json b/package.json
index fe2b52b..9395767 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
"enquirer": "^2.4.1",
"lowdb": "^6.1.1",
"otplib": "^12.0.1",
- "playwright-firefox": "^1.39.0",
+ "playwright-firefox": "^1.40.0",
"puppeteer-extra-plugin-stealth": "^2.11.2"
},
"repository": {
@@ -25,7 +25,7 @@
"author": "Ralf Vogler",
"license": "AGPL-3.0-only",
"devDependencies": {
- "@stylistic/eslint-plugin-js": "^1.0.1",
- "eslint": "^8.53.0"
+ "@stylistic/eslint-plugin-js": "^1.4.1",
+ "eslint": "^8.54.0"
}
}
From 544eff8a23ddc016637508385b8df4144ff322a2 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 19 Dec 2023 11:25:21 +0100
Subject: [PATCH 125/253] ncu -u: playwright-firefox 1.40.0 -> 1.40.1, eslint
---
package-lock.json | 108 +++++++++++++++++++++++-----------------------
package.json | 6 +--
2 files changed, 56 insertions(+), 58 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 2b19e37..c17609e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,12 +14,12 @@
"enquirer": "^2.4.1",
"lowdb": "^6.1.1",
"otplib": "^12.0.1",
- "playwright-firefox": "^1.40.0",
+ "playwright-firefox": "^1.40.1",
"puppeteer-extra-plugin-stealth": "^2.11.2"
},
"devDependencies": {
- "@stylistic/eslint-plugin-js": "^1.4.1",
- "eslint": "^8.54.0"
+ "@stylistic/eslint-plugin-js": "^1.5.1",
+ "eslint": "^8.56.0"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -56,9 +56,9 @@
}
},
"node_modules/@eslint/eslintrc": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz",
- "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==",
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
"dev": true,
"dependencies": {
"ajv": "^6.12.4",
@@ -79,9 +79,9 @@
}
},
"node_modules/@eslint/js": {
- "version": "8.54.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz",
- "integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==",
+ "version": "8.56.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz",
+ "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -198,16 +198,15 @@
}
},
"node_modules/@stylistic/eslint-plugin-js": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.4.1.tgz",
- "integrity": "sha512-WXHPEVw5PB7OML7cLwHJDEcCyLiP7vzKeBbSwmpHLK0oh0JYkoJfTg2hEdFuQT5rQxFy3KzCy9R1mZ0wgLjKrA==",
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.5.1.tgz",
+ "integrity": "sha512-iZF0rF+uOhAmOJYOJx1Yvmm3CZ1uz9n0SRd9dpBYHA3QAvfABUORh9LADWwZCigjHJkp2QbCZelGFJGwGz7Siw==",
"dev": true,
"dependencies": {
"acorn": "^8.11.2",
"escape-string-regexp": "^4.0.0",
"eslint-visitor-keys": "^3.4.3",
- "espree": "^9.6.1",
- "graphemer": "^1.4.0"
+ "espree": "^9.6.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
@@ -502,15 +501,15 @@
}
},
"node_modules/eslint": {
- "version": "8.54.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz",
- "integrity": "sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==",
+ "version": "8.56.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz",
+ "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
- "@eslint/eslintrc": "^2.1.3",
- "@eslint/js": "8.54.0",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.56.0",
"@humanwhocodes/config-array": "^0.11.13",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
@@ -787,9 +786,9 @@
}
},
"node_modules/globals": {
- "version": "13.23.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz",
- "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==",
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
"dev": true,
"dependencies": {
"type-fest": "^0.20.2"
@@ -1211,9 +1210,9 @@
}
},
"node_modules/playwright-core": {
- "version": "1.40.0",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.0.tgz",
- "integrity": "sha512-fvKewVJpGeca8t0ipM56jkVSU6Eo0RmFvQ/MaCQNDYm+sdvKkMBBWTE1FdeMqIdumRaXXjZChWHvIzCGM/tA/Q==",
+ "version": "1.40.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.1.tgz",
+ "integrity": "sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ==",
"bin": {
"playwright-core": "cli.js"
},
@@ -1222,12 +1221,12 @@
}
},
"node_modules/playwright-firefox": {
- "version": "1.40.0",
- "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.40.0.tgz",
- "integrity": "sha512-01KUdoo9Sk9lMGUhlc9tuvWun/mspBINuAvGFm0RFS7ZaZ27uWD9WJ0MnJ6cWcIkL0nLgjlrWmvHgQs1fIq7LQ==",
+ "version": "1.40.1",
+ "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.40.1.tgz",
+ "integrity": "sha512-+C5eOWZv/CvALM0yBZFSYThvUzGKusNw6soDMhTEJwDUB5i9q/yZVFkj6I8CFXM4U6Pf1q0PXHMca70HoIwnCQ==",
"hasInstallScript": true,
"dependencies": {
- "playwright-core": "1.40.0"
+ "playwright-core": "1.40.1"
},
"bin": {
"playwright": "cli.js"
@@ -1640,9 +1639,9 @@
"dev": true
},
"@eslint/eslintrc": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz",
- "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==",
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
"dev": true,
"requires": {
"ajv": "^6.12.4",
@@ -1657,9 +1656,9 @@
}
},
"@eslint/js": {
- "version": "8.54.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz",
- "integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==",
+ "version": "8.56.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz",
+ "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
"dev": true
},
"@humanwhocodes/config-array": {
@@ -1754,16 +1753,15 @@
}
},
"@stylistic/eslint-plugin-js": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.4.1.tgz",
- "integrity": "sha512-WXHPEVw5PB7OML7cLwHJDEcCyLiP7vzKeBbSwmpHLK0oh0JYkoJfTg2hEdFuQT5rQxFy3KzCy9R1mZ0wgLjKrA==",
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.5.1.tgz",
+ "integrity": "sha512-iZF0rF+uOhAmOJYOJx1Yvmm3CZ1uz9n0SRd9dpBYHA3QAvfABUORh9LADWwZCigjHJkp2QbCZelGFJGwGz7Siw==",
"dev": true,
"requires": {
"acorn": "^8.11.2",
"escape-string-regexp": "^4.0.0",
"eslint-visitor-keys": "^3.4.3",
- "espree": "^9.6.1",
- "graphemer": "^1.4.0"
+ "espree": "^9.6.1"
}
},
"@types/debug": {
@@ -1969,15 +1967,15 @@
"dev": true
},
"eslint": {
- "version": "8.54.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz",
- "integrity": "sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==",
+ "version": "8.56.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz",
+ "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
"dev": true,
"requires": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
- "@eslint/eslintrc": "^2.1.3",
- "@eslint/js": "8.54.0",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.56.0",
"@humanwhocodes/config-array": "^0.11.13",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
@@ -2185,9 +2183,9 @@
}
},
"globals": {
- "version": "13.23.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz",
- "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==",
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
"dev": true,
"requires": {
"type-fest": "^0.20.2"
@@ -2507,16 +2505,16 @@
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
"playwright-core": {
- "version": "1.40.0",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.0.tgz",
- "integrity": "sha512-fvKewVJpGeca8t0ipM56jkVSU6Eo0RmFvQ/MaCQNDYm+sdvKkMBBWTE1FdeMqIdumRaXXjZChWHvIzCGM/tA/Q=="
+ "version": "1.40.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.1.tgz",
+ "integrity": "sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ=="
},
"playwright-firefox": {
- "version": "1.40.0",
- "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.40.0.tgz",
- "integrity": "sha512-01KUdoo9Sk9lMGUhlc9tuvWun/mspBINuAvGFm0RFS7ZaZ27uWD9WJ0MnJ6cWcIkL0nLgjlrWmvHgQs1fIq7LQ==",
+ "version": "1.40.1",
+ "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.40.1.tgz",
+ "integrity": "sha512-+C5eOWZv/CvALM0yBZFSYThvUzGKusNw6soDMhTEJwDUB5i9q/yZVFkj6I8CFXM4U6Pf1q0PXHMca70HoIwnCQ==",
"requires": {
- "playwright-core": "1.40.0"
+ "playwright-core": "1.40.1"
}
},
"prelude-ls": {
diff --git a/package.json b/package.json
index 9395767..c6890cf 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
"enquirer": "^2.4.1",
"lowdb": "^6.1.1",
"otplib": "^12.0.1",
- "playwright-firefox": "^1.40.0",
+ "playwright-firefox": "^1.40.1",
"puppeteer-extra-plugin-stealth": "^2.11.2"
},
"repository": {
@@ -25,7 +25,7 @@
"author": "Ralf Vogler",
"license": "AGPL-3.0-only",
"devDependencies": {
- "@stylistic/eslint-plugin-js": "^1.4.1",
- "eslint": "^8.54.0"
+ "@stylistic/eslint-plugin-js": "^1.5.1",
+ "eslint": "^8.56.0"
}
}
From 36fe60a2b109902bd29f5fa623bb6697b290cc99 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 19 Dec 2023 11:33:38 +0100
Subject: [PATCH 126/253] vscode update changed config
---
.vscode/settings.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.vscode/settings.json b/.vscode/settings.json
index c2cd6b0..6106b4f 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -3,7 +3,8 @@
"editor.formatOnSave": true,
"editor.formatOnSaveMode": "modifications",
"editor.codeActionsOnSave": {
- "source.fixAll.eslint": true
+ "source.fixAll.eslint": "explicit"
},
"eslint.experimental.useFlatConfig": true,
+ "eslint.codeActionsOnSave.rules": null,
}
From 9c89bf06a4cec739ae36c9fe2f0faf1142afc7eb Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 19 Dec 2023 11:34:00 +0100
Subject: [PATCH 127/253] npm run lint = npx eslint .
---
package.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/package.json b/package.json
index c6890cf..15b2165 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,8 @@
"main": "index.js",
"scripts": {
"docker:build": "docker build . -t ghcr.io/vogler/free-games-claimer",
- "docker": "cross-env-shell docker run --rm -it -p 5900:5900 -p 6080:6080 -v \\\"$INIT_CWD/data\\\":/fgc/data --name fgc ghcr.io/vogler/free-games-claimer"
+ "docker": "cross-env-shell docker run --rm -it -p 5900:5900 -p 6080:6080 -v \\\"$INIT_CWD/data\\\":/fgc/data --name fgc ghcr.io/vogler/free-games-claimer",
+ "lint": "npx eslint ."
},
"type": "module",
"dependencies": {
From 76597f4315db0ea9839319ff965bc4895cac9850 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 19 Dec 2023 11:52:35 +0100
Subject: [PATCH 128/253] eg: include link to game in captcha notification,
closes #259
TODO use purchaseURL from https://github.com/vogler/free-games-claimer/pull/130
---
epic-games.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/epic-games.js b/epic-games.js
index 06d66cc..90e0683 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -228,7 +228,7 @@ try {
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.');
- await notify('epic-games: got captcha challenge right before claim. Use VNC to solve it manually.');
+ await notify(`epic-games: got captcha challenge right before claim of ${title}. Use VNC to solve it manually.`); // TODO could even create purchase URL, see https://github.com/vogler/free-games-claimer/pull/130
// await page.waitForTimeout(2000);
// const p = path.resolve(cfg.dir.screenshots, 'epic-games', 'captcha', `${filenamify(datetime())}.png`);
// await captcha.screenshot({ path: p });
From 3c15252d8babebd994acbd9eb8518af35edfedc3 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 19 Dec 2023 12:06:15 +0100
Subject: [PATCH 129/253] pg: use chalk to color game codes blue, closes #250
---
package-lock.json | 53 ++++++++++++++++++++++++++++++++---------------
package.json | 1 +
prime-gaming.js | 5 +++--
3 files changed, 40 insertions(+), 19 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index c17609e..26603de 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,7 @@
"version": "1.4.0",
"license": "AGPL-3.0-only",
"dependencies": {
+ "chalk": "^5.3.0",
"cross-env": "^7.0.3",
"dotenv": "^16.3.1",
"enquirer": "^2.4.1",
@@ -340,16 +341,11 @@
}
},
"node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+ "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
"engines": {
- "node": ">=10"
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
@@ -583,6 +579,22 @@
"url": "https://opencollective.com/eslint"
}
},
+ "node_modules/eslint/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
"node_modules/espree": {
"version": "9.6.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
@@ -1859,14 +1871,9 @@
"dev": true
},
"chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+ "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w=="
},
"clone-deep": {
"version": "0.2.4",
@@ -2010,6 +2017,18 @@
"optionator": "^0.9.3",
"strip-ansi": "^6.0.1",
"text-table": "^0.2.0"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ }
}
},
"eslint-scope": {
diff --git a/package.json b/package.json
index 15b2165..1f3279e 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
},
"type": "module",
"dependencies": {
+ "chalk": "^5.3.0",
"cross-env": "^7.0.3",
"dotenv": "^16.3.1",
"enquirer": "^2.4.1",
diff --git a/prime-gaming.js b/prime-gaming.js
index e60caf7..0d9502b 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -1,5 +1,6 @@
import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdated playwright-extra
import { authenticator } from 'otplib';
+import chalk from 'chalk';
import { resolve, jsonDb, datetime, stealth, filenamify, prompt, confirm, notify, html_game_list, handleSIGINT } from './util.js';
import { cfg } from './config.js';
@@ -182,7 +183,7 @@ try {
};
if (store in redeem) { // did not work for linked origin: && !await page.locator('div:has-text("Successfully Claimed")').count()
const code = await Promise.any([page.inputValue('input[type="text"]'), page.textContent('[data-a-target="ClaimStateClaimCodeContent"]').then(s => s.replace('Your code: ', ''))]); // input: Legacy Games; text: gog.com
- console.log(' Code to redeem game:', code);
+ console.log(' Code to redeem game:', chalk.blue(code));
if (store == 'legacy games') { // may be different URL like https://legacygames.com/primeday/puzzleoftheyear/
redeem[store] = await (await page.$('li:has-text("Click here") a')).getAttribute('href'); // full text: Click here to enter your redemption code.
}
@@ -367,7 +368,7 @@ try {
dlc_unlinked[unlinked_store].push(title);
} else {
const code = await page.inputValue('input[type="text"]');
- console.log(' Code to redeem game:', code);
+ console.log(' Code to redeem game:', chalk.blue(code));
db.data[user][title].code = code;
db.data[user][title].status = 'claimed';
// notify_game.status = `${redeem_action} ${code} on ${store}`;
From 8bf36a2158f4b5410d5a4e3f9a87725d51309412 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 19 Dec 2023 12:27:55 +0100
Subject: [PATCH 130/253] specify engines.node >=15 for ||=, #264
---
package-lock.json | 3 +++
package.json | 3 +++
2 files changed, 6 insertions(+)
diff --git a/package-lock.json b/package-lock.json
index 26603de..930e7bf 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -21,6 +21,9 @@
"devDependencies": {
"@stylistic/eslint-plugin-js": "^1.5.1",
"eslint": "^8.56.0"
+ },
+ "engines": {
+ "node": ">=15"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
diff --git a/package.json b/package.json
index 1f3279e..3c1be2a 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,9 @@
"lint": "npx eslint ."
},
"type": "module",
+ "engines": {
+ "node": ">=15"
+ },
"dependencies": {
"chalk": "^5.3.0",
"cross-env": "^7.0.3",
From f4270e176fa12aca5f7f14c0e998f8cfc9045d9c Mon Sep 17 00:00:00 2001
From: Jannis Hell
Date: Sun, 12 Nov 2023 11:18:30 +0100
Subject: [PATCH 131/253] Update prime-gaming.js
The button was renamed to "Claim" instead of "Claim now" this causes / fixes #208
---
prime-gaming.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 0d9502b..95860b5 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -137,7 +137,7 @@ try {
if (cfg.debug) await page.pause();
if (cfg.dryrun) continue;
if (cfg.interactive && !await confirm()) continue;
- await Promise.any([page.click('button:has-text("Get game")'), page.click('button:has-text("Claim now")'), page.click('button:has-text("Complete Claim")'), page.waitForSelector('div:has-text("Link game account")'), page.waitForSelector('.thank-you-title:has-text("Success")')]); // waits for navigation
+ await Promise.any([page.click('button:has-text("Get game")'), page.click('button:has-text("Claim")'), page.click('button:has-text("Complete Claim")'), page.waitForSelector('div:has-text("Link game account")'), page.waitForSelector('.thank-you-title:has-text("Success")')]); // waits for navigation
// TODO would be simpler than the below, but will block for linked stores without code
// const redeem_text = await page.textContent('text=/ code on /'); // FAQ: How do I redeem my code?
@@ -348,8 +348,8 @@ try {
try {
await page.goto(url, { waitUntil: 'domcontentloaded' });
// most games have a button 'Get in-game content'
- // epic-games: Fall Guys: Claim now -> 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('button:has-text("Get in-game content")'), page.click('button:has-text("Claim your gift")'), page.click('button:has-text("Claim now")').then(() => page.click('button:has-text("Continue")'))]);
+ // 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('button:has-text("Get in-game content")'), page.click('button:has-text("Claim your gift")'), page.click('button:has-text("Claim")').then(() => page.click('button:has-text("Continue")'))]);
page.click('button:has-text("Continue")').catch(_ => { });
const linkAccountButton = page.locator('[data-a-target="LinkAccountButton"]');
let unlinked_store;
From bdc305aa83b23b25b3dd3d58b5b4520d30b00b01 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 21 Dec 2023 01:53:47 +0100
Subject: [PATCH 132/253] reorder package.json
---
package.json | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/package.json b/package.json
index 3c1be2a..6c954cd 100644
--- a/package.json
+++ b/package.json
@@ -3,6 +3,12 @@
"version": "1.4.0",
"description": "Automatically claims free games on the Epic Games Store, Amazon Prime Gaming and GOG.",
"homepage": "https://github.com/vogler/free-games-claimer",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/vogler/free-games-claimer.git"
+ },
+ "author": "Ralf Vogler",
+ "license": "AGPL-3.0-only",
"main": "index.js",
"scripts": {
"docker:build": "docker build . -t ghcr.io/vogler/free-games-claimer",
@@ -23,12 +29,6 @@
"playwright-firefox": "^1.40.1",
"puppeteer-extra-plugin-stealth": "^2.11.2"
},
- "repository": {
- "type": "git",
- "url": "https://github.com/vogler/free-games-claimer.git"
- },
- "author": "Ralf Vogler",
- "license": "AGPL-3.0-only",
"devDependencies": {
"@stylistic/eslint-plugin-js": "^1.5.1",
"eslint": "^8.56.0"
From 19e9bb9cfc614fbf8b632b96cda6aebea035f3bf Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 21 Dec 2023 01:55:54 +0100
Subject: [PATCH 133/253] eg: changed: move password fill and fix captcha
locators, #260
---
epic-games.js | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index 90e0683..069eee5 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -87,15 +87,15 @@ try {
// await page.click('text=Sign in with Epic Games');
await page.fill('#email', email);
await page.click('button[type="submit"]');
- await page.fill('#password', password);
- await page.click('button[type="submit"]');
- page.waitForSelector('#h_captcha_challenge_login_prod iframe').then(async () => {
+ page.waitForSelector('.h_captcha_challenge iframe').then(async () => {
console.error('Got a captcha during login (likely due to too many attempts)! You may solve it in the browser, get a new IP or try again in a few hours.');
await notify('epic-games: got captcha during login. Please check.');
}).catch(_ => { });
- page.waitForSelector('h6:has-text("Incorrect response.")').then(async () => {
+ page.waitForSelector('p:has-text("Incorrect response.")').then(async () => {
console.error('Incorrect repsonse for captcha!');
}).catch(_ => { });
+ await page.fill('#password', password);
+ await page.click('button[type="submit"]');
// handle MFA, but don't await it
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 ...');
From 30957d63d0289514b88b509f8d8fdd114b106d36 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 21 Dec 2023 16:16:48 +0100
Subject: [PATCH 134/253] DEBUG_NETWORK=1 to log network requests and responses
instead of DEBUG && RECORD
---
config.js | 1 +
epic-games.js | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/config.js b/config.js
index 0962159..bd41c7d 100644
--- a/config.js
+++ b/config.js
@@ -6,6 +6,7 @@ dotenv.config({ path: 'data/config.env' }); // loads env vars from file - will n
// Options - also see table in README.md
export const cfg = {
debug: process.env.DEBUG == '1' || process.env.PWDEBUG == '1', // runs non-headless and opens https://playwright.dev/docs/inspector
+ debug_network: process.env.DEBUG_NETWORK == '1', // log network requests and responses
record: process.env.RECORD == '1', // `recordHar` (network) + `recordVideo`
time: process.env.TIME == '1', // log duration of each step
dryrun: process.env.DRYRUN == '1', // don't claim anything
diff --git a/epic-games.js b/epic-games.js
index 069eee5..880e337 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -53,7 +53,7 @@ const page = context.pages().length ? context.pages()[0] : await context.newPage
// console.debug('userAgent:', await page.evaluate(() => navigator.userAgent));
// eslint-disable-next-line no-undef
if (cfg.debug) console.debug(await page.evaluate(() => window.screen));
-if (cfg.record && cfg.debug) {
+if (cfg.debug_network) {
// const filter = _ => true;
const filter = r => r.url().includes('store.epicgames.com');
page.on('request', request => filter(request) && console.log('>>', request.method(), request.url()));
From 9f97805517041e60dddd90274c02834165ae6a1f Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 21 Dec 2023 16:38:12 +0100
Subject: [PATCH 135/253] debug navigator.{userAgent, platform}
---
epic-games.js | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index 880e337..349b726 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -50,9 +50,8 @@ await stealth(context);
if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
const page = context.pages().length ? context.pages()[0] : await context.newPage(); // should always exist
-// console.debug('userAgent:', await page.evaluate(() => navigator.userAgent));
// eslint-disable-next-line no-undef
-if (cfg.debug) console.debug(await page.evaluate(() => window.screen));
+if (cfg.debug) console.debug(await page.evaluate(() => [window.screen, navigator.userAgent, navigator.platform]));
if (cfg.debug_network) {
// const filter = _ => true;
const filter = r => r.url().includes('store.epicgames.com');
From e06ad3a27ff4dc0c7aa13625d5e2f1e96526e344 Mon Sep 17 00:00:00 2001
From: Jannis Hell
Date: Fri, 22 Dec 2023 16:40:02 +0100
Subject: [PATCH 136/253] use tw-button css class over button html element
selector
---
prime-gaming.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 95860b5..9558a92 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -114,7 +114,7 @@ try {
console.log('Current free game:', title);
if (cfg.dryrun) continue;
if (cfg.interactive && !await confirm()) continue;
- await (await card.$('button:has-text("Claim")')).click();
+ await (await card.$('.tw-button:has-text("Claim")')).click();
db.data[user][title] ||= { title, time: datetime(), store: 'internal' };
notify_games.push({ title, status: 'claimed', url: URL_CLAIM });
// const img = await (await card.$('img.tw-image')).getAttribute('src');
@@ -137,7 +137,7 @@ try {
if (cfg.debug) await page.pause();
if (cfg.dryrun) continue;
if (cfg.interactive && !await confirm()) continue;
- await Promise.any([page.click('button:has-text("Get game")'), page.click('button:has-text("Claim")'), page.click('button:has-text("Complete Claim")'), page.waitForSelector('div:has-text("Link game account")'), page.waitForSelector('.thank-you-title:has-text("Success")')]); // waits for navigation
+ await Promise.any([page.click('.tw-button:has-text("Get game")'), page.click('.tw-button:has-text("Claim")'), page.click('.tw-button:has-text("Complete Claim")'), page.waitForSelector('div:has-text("Link game account")'), page.waitForSelector('.thank-you-title:has-text("Success")')]); // waits for navigation
// TODO would be simpler than the below, but will block for linked stores without code
// const redeem_text = await page.textContent('text=/ code on /'); // FAQ: How do I redeem my code?
From 093ed813c63e1df7d43c04b8eeb109a02d2c932a Mon Sep 17 00:00:00 2001
From: Jannis Hell
Date: Sat, 23 Dec 2023 11:55:27 +0100
Subject: [PATCH 137/253] Update prime-gaming.js
account for more than one account link button
---
prime-gaming.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 9558a92..25574b2 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -349,12 +349,12 @@ try {
await page.goto(url, { waitUntil: 'domcontentloaded' });
// 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('button:has-text("Get in-game content")'), page.click('button:has-text("Claim your gift")'), page.click('button:has-text("Claim")').then(() => page.click('button:has-text("Continue")'))]);
+ 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(_ => { });
const linkAccountButton = page.locator('[data-a-target="LinkAccountButton"]');
let unlinked_store;
if (await linkAccountButton.count()) {
- unlinked_store = await linkAccountButton.getAttribute('aria-label');
+ unlinked_store = await linkAccountButton.first().getAttribute('aria-label');
console.debug(' LinkAccountButton label:', unlinked_store);
const match = unlinked_store.match(/Link (.*) account/);
if (match && match.length == 2) unlinked_store = match[1];
From ace4ef8303b0cf312323a62c9138ad335788b75d Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sat, 23 Dec 2023 18:16:30 +0100
Subject: [PATCH 138/253] pg: DLC: ignore timeout if there is no code
https://github.com/vogler/free-games-claimer/issues/208#issuecomment-1868317276
---
prime-gaming.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 25574b2..067ef37 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -367,7 +367,7 @@ try {
dlc_unlinked[unlinked_store] ??= [];
dlc_unlinked[unlinked_store].push(title);
} else {
- const code = await page.inputValue('input[type="text"]');
+ 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';
From 5343535429e447009812e4e6ec969d8564383c97 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 24 Dec 2023 01:10:17 +0100
Subject: [PATCH 139/253] Create FUNDING.yml
---
.github/FUNDING.yml | 13 +++++++++++++
1 file changed, 13 insertions(+)
create mode 100644 .github/FUNDING.yml
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..23af7b2
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,13 @@
+# These are supported funding model platforms
+
+github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
+custom: https://paypal.me/voglerr # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
From 2a191006014571908942cc0c27d9b2e562660873 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 24 Dec 2023 15:39:38 +0100
Subject: [PATCH 140/253] Update FUNDING.yml
---
.github/FUNDING.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 23af7b2..0bf5ac4 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,7 +1,7 @@
# These are supported funding model platforms
-github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
-patreon: # Replace with a single Patreon username
+github: [vogler] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: 11239349 # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
From 68b66444ca093c23beb37f56caf183a496acae7f Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 24 Dec 2023 15:51:57 +0100
Subject: [PATCH 141/253] eg: set cookie to void 'please provide your date of
birth', closes #275
---
epic-games.js | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/epic-games.js b/epic-games.js
index 349b726..261057b 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -63,7 +63,10 @@ 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.
+ { name: 'HasAcceptedAgeGates', value: 'USK:9007199254740991,general:18,EPIC SUGGESTED RATING:18', domain: 'store.epicgames.com', path: '/' }, // gets rid of 'To continue, please provide your date of birth', https://github.com/vogler/free-games-claimer/issues/275, USK number doesn't seem to matter, cookie from 'Fallout 3: Game of the Year Edition'
+ ]);
await page.goto(URL_CLAIM, { waitUntil: 'domcontentloaded' }); // 'domcontentloaded' faster than default 'load' https://playwright.dev/docs/api/class-page#page-goto
@@ -146,6 +149,9 @@ try {
// click Continue if 'This game contains mature content recommended only for ages 18+'
if (await page.locator('button:has-text("Continue")').count() > 0) {
console.log(' This game contains mature content recommended only for ages 18+');
+ if (await page.locator('[data-testid="AgeSelect"]').count()) {
+ console.error(' Got "To continue, please provide your date of birth" - This shouldn\'t happen due to cookie set above. Please report to https://github.com/vogler/free-games-claimer/issues/275');
+ }
await page.click('button:has-text("Continue")', { delay: 111 });
await page.waitForTimeout(2000);
}
From e66d10574d5c64c309dcae54cb30b6ce69d1fbdf Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 24 Dec 2023 16:21:15 +0100
Subject: [PATCH 142/253] Update FUNDING.yml
---
.github/FUNDING.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 0bf5ac4..b8e0cc8 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,7 +1,7 @@
# These are supported funding model platforms
-github: [vogler] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
-patreon: 11239349 # Replace with a single Patreon username
+github: vogler # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: "111239349" # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
From a2af1ccd09339bed5f7d32b089e6e4ab4ff47901 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 24 Dec 2023 16:22:35 +0100
Subject: [PATCH 143/253] Update FUNDING.yml
---
.github/FUNDING.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index b8e0cc8..82bc994 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,7 +1,7 @@
# These are supported funding model platforms
github: vogler # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
-patreon: "111239349" # Replace with a single Patreon username
+patreon: https://patreon.com/user?u=111239349 # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
From 2dec0080d922ca14db9aa8ab890980092f02419c Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 24 Dec 2023 16:23:01 +0100
Subject: [PATCH 144/253] Update FUNDING.yml
---
.github/FUNDING.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 82bc994..4baac3b 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,7 +1,7 @@
# These are supported funding model platforms
github: vogler # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
-patreon: https://patreon.com/user?u=111239349 # Replace with a single Patreon username
+# patreon: 111239349 # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
From 377ee736cb49566e4631161952e136c6f88d4ac9 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 24 Dec 2023 16:29:05 +0100
Subject: [PATCH 145/253] Update FUNDING.yml
---
.github/FUNDING.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 4baac3b..95655c3 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,7 +1,7 @@
# These are supported funding model platforms
github: vogler # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
-# patreon: 111239349 # Replace with a single Patreon username
+patreon: fgc # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
From 4518914bfda0c8052942f909d2079938955ef21f Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 24 Dec 2023 17:04:37 +0100
Subject: [PATCH 146/253] Update FUNDING.yml
---
.github/FUNDING.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 95655c3..9a0d965 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -3,11 +3,11 @@
github: vogler # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: fgc # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
-ko_fi: # Replace with a single Ko-fi username
+ko_fi: vogler # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
-liberapay: # Replace with a single Liberapay username
+liberapay: vogler # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
-custom: https://paypal.me/voglerr # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+custom: ["https://www.buymeacoffee.com/vogler", "https://paypal.me/voglerr"] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
From 09a364c67d05d8f337123d83c562fee35b7076fa Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 26 Dec 2023 16:48:25 +0100
Subject: [PATCH 147/253] extract chrome-specific comments/args into
util.js/launchChromium
---
epic-games.js | 15 +++------------
util.js | 22 ++++++++++++++++++++++
2 files changed, 25 insertions(+), 12 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index 261057b..f91d7e5 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -16,13 +16,8 @@ const db = await jsonDb('epic-games.json', {});
if (cfg.time) console.time('startup');
-// 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
-
// https://playwright.dev/docs/auth#multi-factor-authentication
const context = await firefox.launchPersistentContext(cfg.dir.browser, {
- // chrome will not work in linux arm64, only chromium
- // channel: 'chrome', // https://playwright.dev/docs/browsers#google-chrome--microsoft-edge
headless: cfg.headless,
viewport: { width: cfg.width, height: cfg.height },
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?
@@ -32,14 +27,10 @@ const context = await firefox.launchPersistentContext(cfg.dir.browser, {
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
- args: [ // https://peter.sh/experiments/chromium-command-line-switches
- // don't want to see bubble 'Restore pages? Chrome didn't shut down correctly.'
- // '--restore-last-session', // does not apply for crash/killed
- '--hide-crash-restore-bubble',
- // `--disable-extensions-except=${ext}`,
- // `--load-extension=${ext}`,
+ // user settings for firefox have to be put in $BROWSER_DIR/user.js
+ args: [ // https://wiki.mozilla.org/Firefox/CommandLineOptions
+ // '-kiosk',
],
- // ignoreDefaultArgs: ['--enable-automation'], // remove default arg that shows the info bar with 'Chrome is being controlled by automated test software.'. Since Chromeium 106 this leads to show another info bar with 'You are using an unsupported command-line flag: --no-sandbox. Stability and security will suffer.'.
});
handleSIGINT(context);
diff --git a/util.js b/util.js
index acd6c0b..6ed1d10 100644
--- a/util.js
+++ b/util.js
@@ -27,6 +27,28 @@ export const handleSIGINT = (context = null) => process.on('SIGINT', async () =>
if (context) await context.close(); // in order to save recordings also on SIGINT, we need to disable Playwright's handleSIGINT and close the context ourselves
});
+export const launchChromium = async options => {
+ const { chromium } = await import('playwright-chromium'); // stealth plugin needs no outdated playwright-extra
+
+ // 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
+
+ const context = chromium.launchPersistentContext(cfg.dir.browser, {
+ // chrome will not work in linux arm64, only chromium
+ // channel: 'chrome', // https://playwright.dev/docs/browsers#google-chrome--microsoft-edge
+ args: [ // https://peter.sh/experiments/chromium-command-line-switches
+ // don't want to see bubble 'Restore pages? Chrome didn't shut down correctly.'
+ // '--restore-last-session', // does not apply for crash/killed
+ '--hide-crash-restore-bubble',
+ // `--disable-extensions-except=${ext}`,
+ // `--load-extension=${ext}`,
+ ],
+ // ignoreDefaultArgs: ['--enable-automation'], // remove default arg that shows the info bar with 'Chrome is being controlled by automated test software.'. Since Chromeium 106 this leads to show another info bar with 'You are using an unsupported command-line flag: --no-sandbox. Stability and security will suffer.'.
+ ...options,
+ });
+ return context;
+};
+
export const stealth = async context => {
// stealth with playwright: https://github.com/berstend/puppeteer-extra/issues/454#issuecomment-917437212
// https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra-plugin-stealth/evasions
From 5a4f07ce702bf158435bede953720612027d072a Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 26 Dec 2023 16:54:10 +0100
Subject: [PATCH 148/253] mv notify-test.js test/notify.js
---
notify-test.js => test/notify.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
rename notify-test.js => test/notify.js (93%)
diff --git a/notify-test.js b/test/notify.js
similarity index 93%
rename from notify-test.js
rename to test/notify.js
index b9c293f..3479076 100644
--- a/notify-test.js
+++ b/test/notify.js
@@ -1,6 +1,6 @@
/* eslint-disable no-constant-condition */
-import { delay, html_game_list, notify } from './util.js';
-import { cfg } from './config.js';
+import { delay, html_game_list, notify } from '../util.js';
+import { cfg } from '../config.js';
const URL_CLAIM = 'https://gaming.amazon.com/home'; // dummy URL
From 6b9420804bc877d86705efc9124634a2c2dd7529 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 26 Dec 2023 16:58:14 +0100
Subject: [PATCH 149/253] test to show enquirer's sigint issue
---
test/sigint-enquirer-raw.js | 40 ++++++++++++++++++++++++++++++++++
test/sigint-enquirer-simple.js | 15 +++++++++++++
2 files changed, 55 insertions(+)
create mode 100644 test/sigint-enquirer-raw.js
create mode 100644 test/sigint-enquirer-simple.js
diff --git a/test/sigint-enquirer-raw.js b/test/sigint-enquirer-raw.js
new file mode 100644
index 0000000..0459aa7
--- /dev/null
+++ b/test/sigint-enquirer-raw.js
@@ -0,0 +1,40 @@
+// https://github.com/enquirer/enquirer/issues/372
+import { prompt } from '../util.js';
+
+const handleSIGINT = () => process.on('SIGINT', () => { // e.g. when killed by Ctrl-C
+ console.log('\nInterrupted by SIGINT. Exit!');
+ process.exitCode = 130;
+});
+handleSIGINT();
+
+function onRawSIGINT(fn) {
+ const { stdin, stdout } = process;
+ stdin.setRawMode(true);
+ stdin.resume();
+ stdin.on('data', data => {
+ const key = data.toString('utf-8');
+ if (key === '\u0003') { // ctrl + c
+ fn();
+ } else {
+ stdout.write(key);
+ }
+ });
+}
+onRawSIGINT(() => {
+ console.log('raw'); process.exit(1);
+});
+
+console.log('hello');
+console.error('hello error');
+try {
+ let i = 'foo';
+ i = await prompt(); // SIGINT no longer handled if this is executed
+ // handleSIGINT();
+ console.log('value:', i);
+ setTimeout(() => console.log('timeout 3s'), 3000);
+} catch (e) {
+ process.exitCode ||= 1;
+ console.log('catch. exitCode:', process.exitCode);
+ console.error(e);
+}
+console.log('end. exitCode:', process.exitCode);
diff --git a/test/sigint-enquirer-simple.js b/test/sigint-enquirer-simple.js
new file mode 100644
index 0000000..13e7d02
--- /dev/null
+++ b/test/sigint-enquirer-simple.js
@@ -0,0 +1,15 @@
+// https://github.com/enquirer/enquirer/issues/372
+import Enquirer from 'enquirer';
+const enquirer = new Enquirer();
+
+let interrupted = false;
+process.on('SIGINT', () => {
+ if (interrupted) process.exit();
+ interrupted = true;
+ console.log('SIGINT');
+});
+await enquirer.prompt({
+ type: 'input',
+ name: 'username',
+ message: 'What is your username?',
+});
From 64676795d144bef79a10700cde20ced5a267a4bd Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 26 Dec 2023 17:09:14 +0100
Subject: [PATCH 150/253] mv {config,migrate,util,version}.js src/
---
README.md | 2 +-
epic-games.js | 4 ++--
gog.js | 4 ++--
prime-gaming.js | 2 +-
config.js => src/config.js | 0
migrate.js => src/migrate.js | 0
util.js => src/util.js | 2 +-
version.js => src/version.js | 0
unrealengine.js | 4 ++--
xbox.js | 4 ++--
10 files changed, 11 insertions(+), 11 deletions(-)
rename config.js => src/config.js (100%)
rename migrate.js => src/migrate.js (100%)
rename util.js => src/util.js (99%)
rename version.js => src/version.js (100%)
diff --git a/README.md b/README.md
index 5c9e90f..84ab623 100644
--- a/README.md
+++ b/README.md
@@ -93,7 +93,7 @@ Available options/variables and their default values:
| GOG_PASSWORD | | GOG password for login. Overrides PASSWORD. |
| GOG_NEWSLETTER | 0 | Do not unsubscribe from newsletter after claiming a game if 1. |
-See `config.js` for all options.
+See `src/config.js` for all options.
#### How to set options
You can add options directly in the command or put them in a file to load.
diff --git a/epic-games.js b/epic-games.js
index f91d7e5..ae1505d 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -2,8 +2,8 @@ import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdate
import { authenticator } from 'otplib';
import path from 'path';
import { existsSync, writeFileSync } from 'fs';
-import { resolve, jsonDb, datetime, stealth, filenamify, prompt, notify, html_game_list, handleSIGINT } from './util.js';
-import { cfg } from './config.js';
+import { resolve, jsonDb, datetime, stealth, filenamify, prompt, notify, html_game_list, handleSIGINT } from './src/util.js';
+import { cfg } from './src/config.js';
const screenshot = (...a) => resolve(cfg.dir.screenshots, 'epic-games', ...a);
diff --git a/gog.js b/gog.js
index 74623e6..048e04a 100644
--- a/gog.js
+++ b/gog.js
@@ -1,6 +1,6 @@
import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdated playwright-extra
-import { resolve, jsonDb, datetime, filenamify, prompt, notify, html_game_list, handleSIGINT } from './util.js';
-import { cfg } from './config.js';
+import { resolve, jsonDb, datetime, filenamify, prompt, notify, html_game_list, handleSIGINT } from './src/util.js';
+import { cfg } from './src/config.js';
const screenshot = (...a) => resolve(cfg.dir.screenshots, 'gog', ...a);
diff --git a/prime-gaming.js b/prime-gaming.js
index 067ef37..f437d91 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -2,7 +2,7 @@ import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdate
import { authenticator } from 'otplib';
import chalk from 'chalk';
import { resolve, jsonDb, datetime, stealth, filenamify, prompt, confirm, notify, html_game_list, handleSIGINT } from './util.js';
-import { cfg } from './config.js';
+import { cfg } from './src/config.js';
const screenshot = (...a) => resolve(cfg.dir.screenshots, 'prime-gaming', ...a);
diff --git a/config.js b/src/config.js
similarity index 100%
rename from config.js
rename to src/config.js
diff --git a/migrate.js b/src/migrate.js
similarity index 100%
rename from migrate.js
rename to src/migrate.js
diff --git a/util.js b/src/util.js
similarity index 99%
rename from util.js
rename to src/util.js
index 6ed1d10..f82102b 100644
--- a/util.js
+++ b/src/util.js
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// explicit object instead of Object.fromEntries since the built-in type would loose the keys, better type: https://dev.to/svehla/typescript-object-fromentries-389c
-export const dataDir = s => path.resolve(__dirname, 'data', s);
+export const dataDir = s => path.resolve(__dirname, '..', 'data', s);
// modified path.resolve to return null if first argument is '0', used to disable screenshots
export const resolve = (...a) => a.length && a[0] == '0' ? null : path.resolve(...a);
diff --git a/version.js b/src/version.js
similarity index 100%
rename from version.js
rename to src/version.js
diff --git a/unrealengine.js b/unrealengine.js
index cd2eb33..bfd3417 100644
--- a/unrealengine.js
+++ b/unrealengine.js
@@ -5,8 +5,8 @@ import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdate
import { authenticator } from 'otplib';
import path from 'path';
import { writeFileSync } from 'fs';
-import { resolve, jsonDb, datetime, stealth, filenamify, prompt, notify, html_game_list, handleSIGINT } from './util.js';
-import { cfg } from './config.js';
+import { resolve, jsonDb, datetime, stealth, filenamify, prompt, notify, html_game_list, handleSIGINT } from './src/util.js';
+import { cfg } from './src/config.js';
const screenshot = (...a) => resolve(cfg.dir.screenshots, 'unrealengine', ...a);
diff --git a/xbox.js b/xbox.js
index 54cccf2..008388c 100644
--- a/xbox.js
+++ b/xbox.js
@@ -7,8 +7,8 @@ import {
jsonDb,
notify,
prompt,
-} from './util.js';
-import { cfg } from './config.js';
+} from './src/util.js';
+import { cfg } from './src/config.js';
// ### SETUP
const URL_CLAIM = 'https://www.xbox.com/en-US/live/gold'; // #gameswithgold";
From 105f6f414acd8d5113c69d1ef81f585a981ecf60 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 26 Dec 2023 17:49:51 +0100
Subject: [PATCH 151/253] eg: only ask for password after email submit ->
notice captcha before
---
epic-games.js | 23 +++++++++++++----------
1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index ae1505d..1a2b602 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -74,9 +74,18 @@ try {
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 notifyBrowserLogin = async () => {
+ console.log('Waiting for you to login in the browser.');
+ await notify('epic-games: no longer signed in and not enough options set for automatic login.');
+ if (cfg.headless) {
+ console.log('Run `SHOW=1 node epic-games` to login in the opened browser.');
+ await context.close(); // finishes potential recording
+ process.exit(1);
+ }
+ };
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) {
+ if (!email) await notifyBrowserLogin();
+ else {
// await page.click('text=Sign in with Epic Games');
await page.fill('#email', email);
await page.click('button[type="submit"]');
@@ -87,6 +96,8 @@ try {
page.waitForSelector('p:has-text("Incorrect response.")').then(async () => {
console.error('Incorrect repsonse for captcha!');
}).catch(_ => { });
+ const password = email && (cfg.eg_password || await prompt({ type: 'password', message: 'Enter password' }));
+ if (!password) await notifyBrowserLogin();
await page.fill('#password', password);
await page.click('button[type="submit"]');
// handle MFA, but don't await it
@@ -97,14 +108,6 @@ try {
await page.locator('input[name="code-input-0"]').pressSequentially(otp.toString());
await page.click('button[type="submit"]');
}).catch(_ => { });
- } else {
- console.log('Waiting for you to login in the browser.');
- await notify('epic-games: no longer signed in and not enough options set for automatic login.');
- if (cfg.headless) {
- console.log('Run `SHOW=1 node epic-games` to login in the opened browser.');
- await context.close(); // finishes potential recording
- process.exit(1);
- }
}
await page.waitForURL(URL_CLAIM);
if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
From baeaaa64f8bb9b8d0ba8c699dcd04ae5265e3d84 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 26 Dec 2023 17:52:18 +0100
Subject: [PATCH 152/253] docker: firefox: privacy.resistFingerprinting fixes
#261
https://github.com/vogler/free-games-claimer/issues/261#issuecomment-1868385830
Docker container will ask/fill email, challenge captcha, ask/fill password, challenge captcha again.
---
docker-entrypoint.sh | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
index 0eea025..77b5588 100755
--- a/docker-entrypoint.sh
+++ b/docker-entrypoint.sh
@@ -12,6 +12,13 @@ echo "Build: $NOW"
# https://bugs.chromium.org/p/chromium/issues/detail?id=367048
rm -f /fgc/data/browser/SingletonLock
+# Firefox preferences are stored in $BROWSER_DIR/pref.js and can be overridden by a file user.js
+# Since this file has to be in the volume (data/browser), we can't do this in Dockerfile.
+mkdir -p /fgc/data/browser
+# fix for 'Incorrect response' after solving a captcha correctly - https://github.com/vogler/free-games-claimer/issues/261#issuecomment-1868385830
+echo 'user_pref("privacy.resistFingerprinting", true);' >> /fgc/data/browser/user.js
+# TODO disable session restore message?
+
# Remove X server display lock, fix for `docker compose up` which reuses container which made it fail after initial run, https://github.com/vogler/free-games-claimer/issues/31
# echo $DISPLAY
# ls -l /tmp/.X11-unix/
From b68021b8e824ff4f0b66727a8be41c1eb141e239 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 26 Dec 2023 18:01:32 +0100
Subject: [PATCH 153/253] docker: firefox: overwrite user.js instead of
appending on every run, #261
---
docker-entrypoint.sh | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
index 77b5588..679acc6 100755
--- a/docker-entrypoint.sh
+++ b/docker-entrypoint.sh
@@ -16,7 +16,13 @@ rm -f /fgc/data/browser/SingletonLock
# Since this file has to be in the volume (data/browser), we can't do this in Dockerfile.
mkdir -p /fgc/data/browser
# fix for 'Incorrect response' after solving a captcha correctly - https://github.com/vogler/free-games-claimer/issues/261#issuecomment-1868385830
-echo 'user_pref("privacy.resistFingerprinting", true);' >> /fgc/data/browser/user.js
+# echo 'user_pref("privacy.resistFingerprinting", true);' > /fgc/data/browser/user.js
+cat << EOT > /fgc/data/browser/user.js
+user_pref("privacy.resistFingerprinting", true);
+// user_pref("privacy.resistFingerprinting.letterboxing", true);
+// user_pref("browser.contentblocking.category", "strict");
+// user_pref("webgl.disabled", true);
+EOT
# TODO disable session restore message?
# Remove X server display lock, fix for `docker compose up` which reuses container which made it fail after initial run, https://github.com/vogler/free-games-claimer/issues/31
From a9e20928eec5b6ec8aee77cc2377be5c7f4feb82 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 26 Dec 2023 18:56:36 +0100
Subject: [PATCH 154/253] pg: fix util.js -> src/util.js
https://github.com/vogler/free-games-claimer/commit/64676795d144bef79a10700cde20ced5a267a4bd#r135743209
---
prime-gaming.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index f437d91..7b7f18b 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -1,7 +1,7 @@
import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdated playwright-extra
import { authenticator } from 'otplib';
import chalk from 'chalk';
-import { resolve, jsonDb, datetime, stealth, filenamify, prompt, confirm, notify, html_game_list, handleSIGINT } from './util.js';
+import { resolve, jsonDb, datetime, stealth, filenamify, prompt, confirm, notify, html_game_list, handleSIGINT } from './src/util.js';
import { cfg } from './src/config.js';
const screenshot = (...a) => resolve(cfg.dir.screenshots, 'prime-gaming', ...a);
From fb8f38706aca029eef140e66724d1e3d6ec09621 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 27 Dec 2023 10:22:54 +0100
Subject: [PATCH 155/253] mention pipx, #276
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 84ab623..dfb5c92 100644
--- a/README.md
+++ b/README.md
@@ -37,7 +37,7 @@ Data (including json files with claimed games, codes to redeem, screenshots) is
1. [Install Node.js](https://nodejs.org/en/download)
2. Clone/download this repository and `cd` into it in a terminal
3. Run `npm install`
-4. Run `pip install apprise` to install [apprise](https://github.com/caronc/apprise) if you want notifications
+4. Run `pip install apprise` (or use [pipx](https://github.com/pypa/pipx) if you have [problems](https://stackoverflow.com/questions/75608323/how-do-i-solve-error-externally-managed-environment-every-time-i-use-pip-3)) to install [apprise](https://github.com/caronc/apprise) if you want notifications
5. To get updates: `git pull; npm install`
6. Run `node epic-games`, `node prime-gaming`, `node gog`...
From 56cfab6e21c3b8e86f9066f2019e1cfd69e8ea2a Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 28 Dec 2023 11:28:23 +0100
Subject: [PATCH 156/253] Update README.md
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index dfb5c92..265bfad 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ _Works on Windows/macOS/Linux._
Raspberry Pi (3, 4, Zero 2): [requires 64-bit OS](https://github.com/vogler/free-games-claimer/issues/3) like Raspberry Pi OS or Ubuntu (Raspbian won't work since it's 32-bit).
## How to run
-Easy option: [install Docker](https://docs.docker.com/get-docker/) (or [podman](https://podman-desktop.io/)) and run this command in a terminal (Windows: `cmd`, `.bat` file):
+Easy option: [install Docker](https://docs.docker.com/get-docker/) (or [podman](https://podman-desktop.io/)) and run this command in a terminal:
```
docker run --rm -it -p 6080:6080 -v fgc:/fgc/data --pull=always ghcr.io/vogler/free-games-claimer
```
@@ -156,7 +156,7 @@ If you want it to run regularly, you have to schedule the runs yourself:
- Linux/macOS: `crontab -e` ([example](https://github.com/vogler/free-games-claimer/discussions/56))
- macOS: [launchd](https://stackoverflow.com/questions/132955/how-do-i-set-a-task-to-run-every-so-often)
-- Windows: [task scheduler](https://active-directory-wp.com/docs/Usage/How_to_add_a_cron_job_on_Windows/Scheduled_tasks_and_cron_jobs_on_Windows/index.html), [other options](https://stackoverflow.com/questions/132971/what-is-the-windows-version-of-cron)
+- Windows: [task scheduler](https://active-directory-wp.com/docs/Usage/How_to_add_a_cron_job_on_Windows/Scheduled_tasks_and_cron_jobs_on_Windows/index.html), [other options](https://stackoverflow.com/questions/132971/what-is-the-windows-version-of-cron), or just put the command in a `.bat` file in Autostart if you restart often...
- any OS: use a process manager like [pm2](https://pm2.keymetrics.io/docs/usage/restart-strategies/)
- Docker Compose `command: bash -c "node epic-games; node prime-gaming; node gog; echo sleeping; sleep 1d"` additionally add `restart: unless-stopped` to it.
From 7ca79bd4139d61eabc05a50326356923c1350f48 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 28 Dec 2023 17:31:18 +0100
Subject: [PATCH 157/253] eg: debug window.screen
---
epic-games.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/epic-games.js b/epic-games.js
index 1a2b602..2644c40 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -41,8 +41,10 @@ await stealth(context);
if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
const page = context.pages().length ? context.pages()[0] : await context.newPage(); // should always exist
+
+// some debug info about the page (screen dimensions, user agent, platform)
// eslint-disable-next-line no-undef
-if (cfg.debug) console.debug(await page.evaluate(() => [window.screen, navigator.userAgent, navigator.platform]));
+if (cfg.debug) console.debug(await page.evaluate(() => [(({ width, height, availWidth, availHeight }) => ({ width, height, availWidth, availHeight }))(window.screen), navigator.userAgent, navigator.platform, navigator.vendor])); // deconstruct screen needed since `window.screen` prints {}, `window.screen.toString()` '[object Screen]', and can't use some pick function without defining it on `page`
if (cfg.debug_network) {
// const filter = _ => true;
const filter = r => r.url().includes('store.epicgames.com');
From c3657f05ac6440563b5bf82cd3db37941d8bf1b5 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 28 Dec 2023 17:56:09 +0100
Subject: [PATCH 158/253] workaround for cropped viewport (Playwright
regression), fixes #277
---
epic-games.js | 1 +
gog.js | 1 +
prime-gaming.js | 1 +
unrealengine.js | 1 +
xbox.js | 1 +
5 files changed, 5 insertions(+)
diff --git a/epic-games.js b/epic-games.js
index 2644c40..55d9ad7 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -41,6 +41,7 @@ await stealth(context);
if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
const page = context.pages().length ? context.pages()[0] : await context.newPage(); // should always exist
+await page.setViewportSize({ width: cfg.width, height: cfg.height }); // TODO workaround for https://github.com/vogler/free-games-claimer/issues/277 until Playwright fixes it
// some debug info about the page (screen dimensions, user agent, platform)
// eslint-disable-next-line no-undef
diff --git a/gog.js b/gog.js
index 048e04a..651afa6 100644
--- a/gog.js
+++ b/gog.js
@@ -25,6 +25,7 @@ handleSIGINT(context);
if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
const page = context.pages().length ? context.pages()[0] : await context.newPage(); // should always exist
+await page.setViewportSize({ width: cfg.width, height: cfg.height }); // TODO workaround for https://github.com/vogler/free-games-claimer/issues/277 until Playwright fixes it
// console.debug('userAgent:', await page.evaluate(() => navigator.userAgent));
const notify_games = [];
diff --git a/prime-gaming.js b/prime-gaming.js
index 7b7f18b..3868274 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -31,6 +31,7 @@ await stealth(context);
if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
const page = context.pages().length ? context.pages()[0] : await context.newPage(); // should always exist
+await page.setViewportSize({ width: cfg.width, height: cfg.height }); // TODO workaround for https://github.com/vogler/free-games-claimer/issues/277 until Playwright fixes it
// console.debug('userAgent:', await page.evaluate(() => navigator.userAgent));
const notify_games = [];
diff --git a/unrealengine.js b/unrealengine.js
index bfd3417..e67633e 100644
--- a/unrealengine.js
+++ b/unrealengine.js
@@ -36,6 +36,7 @@ await stealth(context);
if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
const page = context.pages().length ? context.pages()[0] : await context.newPage(); // should always exist
+await page.setViewportSize({ width: cfg.width, height: cfg.height }); // TODO workaround for https://github.com/vogler/free-games-claimer/issues/277 until Playwright fixes it
// console.debug('userAgent:', await page.evaluate(() => navigator.userAgent));
const notify_games = [];
diff --git a/xbox.js b/xbox.js
index 008388c..b2406cc 100644
--- a/xbox.js
+++ b/xbox.js
@@ -32,6 +32,7 @@ if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
const page = context.pages().length
? context.pages()[0]
: await context.newPage(); // should always exist
+await page.setViewportSize({ width: cfg.width, height: cfg.height }); // TODO workaround for https://github.com/vogler/free-games-claimer/issues/277 until Playwright fixes it
const notify_games = [];
let user;
From 0ec24ef0620d3053645c11b8e41e2c8c75c34668 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sat, 30 Dec 2023 13:37:04 +0100
Subject: [PATCH 159/253] fix import paths for test/
---
test/notify.js | 4 ++--
test/sigint-enquirer-raw.js | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/test/notify.js b/test/notify.js
index 3479076..6d89086 100644
--- a/test/notify.js
+++ b/test/notify.js
@@ -1,6 +1,6 @@
/* eslint-disable no-constant-condition */
-import { delay, html_game_list, notify } from '../util.js';
-import { cfg } from '../config.js';
+import { delay, html_game_list, notify } from '../src/util.js';
+import { cfg } from '../src/config.js';
const URL_CLAIM = 'https://gaming.amazon.com/home'; // dummy URL
diff --git a/test/sigint-enquirer-raw.js b/test/sigint-enquirer-raw.js
index 0459aa7..0a95892 100644
--- a/test/sigint-enquirer-raw.js
+++ b/test/sigint-enquirer-raw.js
@@ -1,5 +1,5 @@
// https://github.com/enquirer/enquirer/issues/372
-import { prompt } from '../util.js';
+import { prompt } from '../src/util.js';
const handleSIGINT = () => process.on('SIGINT', () => { // e.g. when killed by Ctrl-C
console.log('\nInterrupted by SIGINT. Exit!');
From 28bcceb285ef7e4ccc0e4d0c1ba5a074fd29904b Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 31 Dec 2023 16:58:56 +0100
Subject: [PATCH 160/253] eg: enter date of birth if age confirmation pops up,
#275
---
epic-games.js | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/epic-games.js b/epic-games.js
index 55d9ad7..68a2dde 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -148,6 +148,12 @@ try {
console.log(' This game contains mature content recommended only for ages 18+');
if (await page.locator('[data-testid="AgeSelect"]').count()) {
console.error(' Got "To continue, please provide your date of birth" - This shouldn\'t happen due to cookie set above. Please report to https://github.com/vogler/free-games-claimer/issues/275');
+ await page.locator('#month_toggle').click();
+ await page.locator('#month_menu li:has-text("01")').click();
+ await page.locator('#day_toggle').click();
+ await page.locator('#day_menu li:has-text("01")').click();
+ await page.locator('#year_toggle').click();
+ await page.locator('#year_menu li:has-text("1987")').click();
}
await page.click('button:has-text("Continue")', { delay: 111 });
await page.waitForTimeout(2000);
From fc810af80e72d08ac5f871ea7fb512303109f1e4 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 11 Jan 2024 15:58:56 +0100
Subject: [PATCH 161/253] Update README.md: add Repobeats insights
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index 265bfad..754df22 100644
--- a/README.md
+++ b/README.md
@@ -213,6 +213,8 @@ Added notifications via [apprise](https://github.com/caronc/apprise).
[](https://star-history.com/#vogler/free-games-claimer&Date)
+
+
---
Logo with smaller aspect ratio (for Telegram bot etc.): 👾 - [emojipedia](https://emojipedia.org/alien-monster/)
From e4e4ce703df9f018da9b848e3b60193504469f72 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 13 Feb 2024 23:45:10 +0100
Subject: [PATCH 162/253] pg: also get and store url for internal games
---
prime-gaming.js | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 3868274..4bb0d1f 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -112,12 +112,14 @@ try {
for (const card of internal) {
await card.scrollIntoViewIfNeeded();
const title = await (await card.$('.item-card-details__body__primary')).innerText();
+ const slug = await (await card.$('a')).getAttribute('href');
+ const url = 'https://gaming.amazon.com' + slug.split('?')[0];
console.log('Current free game:', title);
if (cfg.dryrun) continue;
if (cfg.interactive && !await confirm()) continue;
await (await card.$('.tw-button:has-text("Claim")')).click();
- db.data[user][title] ||= { title, time: datetime(), store: 'internal' };
- notify_games.push({ title, status: 'claimed', url: URL_CLAIM });
+ db.data[user][title] ||= { title, time: datetime(), url, store: 'internal' };
+ notify_games.push({ title, status: 'claimed', url });
// const img = await (await card.$('img.tw-image')).getAttribute('src');
// console.log('Image:', img);
await card.screenshot({ path: screenshot('internal', `${filenamify(title)}.png`) });
From 2ea0c611e4da2729ea16fafadf576017c566cca6 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 13 Feb 2024 23:48:48 +0100
Subject: [PATCH 163/253] pg: fix claiming external games, detect store early
in description, delete old code
---
prime-gaming.js | 26 ++++----------------------
1 file changed, 4 insertions(+), 22 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 4bb0d1f..b555f67 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -138,30 +138,12 @@ try {
console.log('Current free game:', title); // , url);
await page.goto(url, { waitUntil: 'domcontentloaded' });
if (cfg.debug) await page.pause();
+ const item_text = await page.innerText('[data-a-target="DescriptionItemDetails"]');
+ const store = item_text.toLowerCase().replace(/.* on /, '').slice(0, -1);
+ console.log(' External store:', store);
if (cfg.dryrun) continue;
if (cfg.interactive && !await confirm()) continue;
- await Promise.any([page.click('.tw-button:has-text("Get game")'), page.click('.tw-button:has-text("Claim")'), page.click('.tw-button:has-text("Complete Claim")'), page.waitForSelector('div:has-text("Link game account")'), page.waitForSelector('.thank-you-title:has-text("Success")')]); // waits for navigation
-
- // TODO would be simpler than the below, but will block for linked stores without code
- // const redeem_text = await page.textContent('text=/ code on /'); // FAQ: How do I redeem my code?
- // console.log(' ', redeem_text);
- // // Before July 29, 2023, redeem your offer code on GOG.com.
- // // Before July 1, 2023, redeem your product code on Legacy Games.
- // let store = redeem_text.toLowerCase().replace(/.* on /, '').slice(0, -1);
-
- let store = '';
- const store_text = await page.$('[data-a-target="hero-header-subtitle"]'); // worked fine for every store, but now no longer works for gog.com
- if (store_text) { // legacy games, ?
- const store_texts = await store_text.innerText();
- // Full game for PC [and MAC] on: Legacy Games, Origin, EPIC GAMES, Battle.net; alt: 3 Full PC Games on Legacy Games
- store = store_texts.toLowerCase().replace(/.* on /, '');
- } else { // gog.com, ?
- // $('[data-a-target="DescriptionItemDetails"]').innerText is e.g. 'Prey for PC on GOG.com.' but does not work for Legacy Games
- const item_text = await page.innerText('[data-a-target="DescriptionItemDetails"]');
- store = item_text.toLowerCase().replace(/.* on /, '').slice(0, -1);
- }
- console.log(' External store:', store);
-
+ await Promise.any([page.click('[data-a-target="buy-box"] .tw-button:has-text("Get game")'), page.click('[data-a-target="buy-box"] .tw-button:has-text("Claim")'), page.click('.tw-button:has-text("Complete Claim")'), page.waitForSelector('div:has-text("Link game account")'), page.waitForSelector('.thank-you-title:has-text("Success")')]); // waits for navigation
db.data[user][title] ||= { title, time: datetime(), url, store };
const notify_game = { title, url };
notify_games.push(notify_game); // status is updated below
From 285c7a44fd997c11706f8c667be1c83da23087b1 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 29 Feb 2024 16:35:02 +0100
Subject: [PATCH 164/253] README.md: comment alternative starchart.cc
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 754df22..cfdddec 100644
--- a/README.md
+++ b/README.md
@@ -212,6 +212,7 @@ Added notifications via [apprise](https://github.com/caronc/apprise).
[](https://star-history.com/#vogler/free-games-claimer&Date)
+

From 61fdf566c9c2748ce06e35cd7262d314151c836d Mon Sep 17 00:00:00 2001
From: Rex
Date: Fri, 1 Mar 2024 20:57:00 +0800
Subject: [PATCH 165/253] Resolve error with no default data
---
xbox.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/xbox.js b/xbox.js
index b2406cc..67054ef 100644
--- a/xbox.js
+++ b/xbox.js
@@ -15,7 +15,7 @@ const URL_CLAIM = 'https://www.xbox.com/en-US/live/gold'; // #gameswithgold";
console.log(datetime(), 'started checking xbox');
-const db = await jsonDb('xbox.json');
+const db = await jsonDb('xbox.json', {});
db.data ||= {};
handleSIGINT();
From 6f06fccd461cf1cbb79dd2440c88222d4f52bfa8 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 7 Mar 2024 13:47:58 +0100
Subject: [PATCH 166/253] eslint: prefer-const
---
eslint.config.js | 1 +
src/util.js | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/eslint.config.js b/eslint.config.js
index bc6447f..48b1cbc 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -24,6 +24,7 @@ export default [
// https://eslint.style/packages/js
rules: {
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
+ 'prefer-const': 'error',
'@stylistic/js/array-bracket-newline': ['error', 'consistent'],
'@stylistic/js/array-bracket-spacing': 'error',
'@stylistic/js/array-element-newline': ['error', 'consistent'],
diff --git a/src/util.js b/src/util.js
index f82102b..bda3f78 100644
--- a/src/util.js
+++ b/src/util.js
@@ -81,7 +81,7 @@ export const stealth = async context => {
const evasion = await import(`puppeteer-extra-plugin-stealth/evasions/${e}/index.js`);
evasion.default().onPageCreated(stealth);
}
- for (let evasion of stealth.callbacks) {
+ for (const evasion of stealth.callbacks) {
await context.addInitScript(evasion.cb, evasion.a);
}
};
From da6964c19afbb24848c997b1d8b1f9995d360791 Mon Sep 17 00:00:00 2001
From: Targunitoth
Date: Thu, 25 Apr 2024 13:51:40 +0200
Subject: [PATCH 167/253] Escape the apprise -b parameter
---
src/util.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/util.js b/src/util.js
index bda3f78..69cbf7a 100644
--- a/src/util.js
+++ b/src/util.js
@@ -116,7 +116,7 @@ export const notify = html => new Promise((resolve, reject) => {
return resolve();
}
// const cmd = `apprise '${cfg.notify}' ${title} -i html -b '${html}'`; // this had problems if e.g. ' was used in arg; could have `npm i shell-escape`, but instead using safer execFile which takes args as array instead of exec which spawned a shell to execute the command
- const args = [cfg.notify, '-i', 'html', '-b', html];
+ const args = [cfg.notify, '-i', 'html', '-b', `'${html}'`];
if (cfg.notify_title) args.push(...['-t', cfg.notify_title]);
if (cfg.debug) console.debug(`apprise ${args.map(a => `'${a}'`).join(' ')}`); // this also doesn't escape, but it's just for info
execFile('apprise', args, (error, stdout, stderr) => {
From 3338d08a1d29584d7a209656b17266cd19b06c7a Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 2 May 2024 16:58:31 +0200
Subject: [PATCH 168/253] eg: fix title for Bundles, TODO got stuck after
happened for 'LISA: The Definitive Edition' - https://store.epicgames.com/en-US/bundles/lisa-the-definitive-edition
---
epic-games.js | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index 68a2dde..b703de5 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -159,7 +159,13 @@ try {
await page.waitForTimeout(2000);
}
- const title = await page.locator('h1').first().innerText();
+ let title;
+ if (await page.locator('span:text-is("About Bundle")').count()) {
+ // console.log(' This is a bundle containing: TODO');
+ title = (await page.locator('span:has-text("Buy"):left-of([data-testid="purchase-cta-button"])').first().innerText()).replace('Buy ', '');
+ } else {
+ title = await page.locator('h1').first().innerText();
+ }
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);
@@ -191,8 +197,8 @@ try {
// Accept End User License Agreement (only needed once)
page.locator('input#agree').waitFor().then(async () => {
- console.log('Accept End User License Agreement (only needed once)');
- await page.locator('input#agree').check();
+ console.log(' Accept End User License Agreement (only needed once)');
+ await page.locator('input#agree').check(); // TODO Bundle: got stuck here
await page.locator('button:has-text("Accept")').click();
}).catch(_ => { });
@@ -243,7 +249,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' }); // TODO Bundle: got stuck here
db.data[user][game_id].status = 'claimed';
db.data[user][game_id].time = datetime(); // claimed time overwrites failed/dryrun time
console.log(' Claimed successfully!');
From 075106da11ce4bb250a11a47ba232072e9546f1a Mon Sep 17 00:00:00 2001
From: Tymec
Date: Sat, 18 May 2024 19:45:59 +0200
Subject: [PATCH 169/253] Update config.js
---
src/config.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/config.js b/src/config.js
index bd41c7d..4c32d59 100644
--- a/src/config.js
+++ b/src/config.js
@@ -49,4 +49,6 @@ export const cfg = {
// experimmental - likely to change
pg_redeem: process.env.PG_REDEEM == '1', // prime-gaming: redeem keys on external stores
pg_claimdlc: process.env.PG_CLAIMDLC == '1', // prime-gaming: claim in-game content
+ // external stores
+ lg_email: process.env.LG_EMAIL || process.env.PG_EMAIL || process.env.EMAIL, // legacy-games: email to use for redeeming
};
From 8c535f48fa8f2de63fe32cf4a000b962c3a60e6b Mon Sep 17 00:00:00 2001
From: Tymec
Date: Sat, 18 May 2024 19:47:18 +0200
Subject: [PATCH 170/253] Update prime-gaming.js
---
prime-gaming.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index b555f67..2bd705f 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -243,8 +243,8 @@ try {
}
} else if (store == 'legacy games') {
await page2.fill('[name=coupon_code]', code);
- await page2.fill('[name=email]', cfg.pg_email); // TODO option for sep. email?
- await page2.fill('[name=email_validate]', cfg.pg_email);
+ await page2.fill('[name=email]', cfg.lg_email);
+ await page2.fill('[name=email_validate]', cfg.lg_email);
await page2.uncheck('[name=newsletter_sub]');
await page2.click('[type="submit"]');
try {
From c4ae3505b38f25361bf1a16ab2089fffc63e4d9f Mon Sep 17 00:00:00 2001
From: Tymec
Date: Sat, 18 May 2024 19:50:13 +0200
Subject: [PATCH 171/253] Update README.md
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index cfdddec..1491257 100644
--- a/README.md
+++ b/README.md
@@ -92,6 +92,7 @@ Available options/variables and their default values:
| GOG_EMAIL | | GOG email for login. Overrides EMAIL. |
| GOG_PASSWORD | | GOG password for login. Overrides PASSWORD. |
| GOG_NEWSLETTER | 0 | Do not unsubscribe from newsletter after claiming a game if 1. |
+| LG_EMAIL | | Legacy Games: email to use for redeeming (if not set, defaults to PG_EMAIL) |
See `src/config.js` for all options.
From e49fa930e0b426a497ca32e0d30cbfe707ece459 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 23 May 2024 15:54:23 +0200
Subject: [PATCH 172/253] eg: claim base game before add-on
---
epic-games.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/epic-games.js b/epic-games.js
index b703de5..3ead561 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -185,6 +185,8 @@ try {
const baseUrl = 'https://store.epicgames.com' + await page.locator('a:has-text("Overview")').getAttribute('href');
console.log(' Base game:', baseUrl);
// await page.click('a:has-text("Overview")');
+ urls.push(baseUrl); // add base game to the list of games to claim
+ urls.push(url); // add add-on itself again
} 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
From 9ef9798626c7f6108b0eb76cb651f8027f7a6931 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 23 May 2024 15:55:07 +0200
Subject: [PATCH 173/253] eg: detect 'Failed to challenge captcha, please try
again later.'
---
epic-games.js | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/epic-games.js b/epic-games.js
index 3ead561..54ca597 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -251,6 +251,10 @@ 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
+ iframe.locator('.payment__errors:has-text("Failed to challenge captcha, please try again later.")').waitFor().then(async () => {
+ console.error(' Failed to challenge captcha, please try again later.');
+ await notify('epic-games: failed to challenge captcha. Please check.');
+ });
await page.locator('text=Thanks for your order!').waitFor({ state: 'attached' }); // TODO Bundle: got stuck here
db.data[user][game_id].status = 'claimed';
db.data[user][game_id].time = datetime(); // claimed time overwrites failed/dryrun time
From f7c23569c7a40fa6f56d5a32d855e0b60c530a0e Mon Sep 17 00:00:00 2001
From: Vladimir Budylnikov
Date: Sun, 2 Jun 2024 13:33:51 +0400
Subject: [PATCH 174/253] Update README.md
I've made i howto for windows users
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index cfdddec..a6e3825 100644
--- a/README.md
+++ b/README.md
@@ -156,7 +156,7 @@ If you want it to run regularly, you have to schedule the runs yourself:
- Linux/macOS: `crontab -e` ([example](https://github.com/vogler/free-games-claimer/discussions/56))
- macOS: [launchd](https://stackoverflow.com/questions/132955/how-do-i-set-a-task-to-run-every-so-often)
-- Windows: [task scheduler](https://active-directory-wp.com/docs/Usage/How_to_add_a_cron_job_on_Windows/Scheduled_tasks_and_cron_jobs_on_Windows/index.html), [other options](https://stackoverflow.com/questions/132971/what-is-the-windows-version-of-cron), or just put the command in a `.bat` file in Autostart if you restart often...
+- Windows: [task scheduler](https://active-directory-wp.com/docs/Usage/How_to_add_a_cron_job_on_Windows/Scheduled_tasks_and_cron_jobs_on_Windows/index.html) ([example](https://github.com/vogler/free-games-claimer/wiki/%5BHowTo%5D-Schedule-runs-on-Windows)), [other options](https://stackoverflow.com/questions/132971/what-is-the-windows-version-of-cron), or just put the command in a `.bat` file in Autostart if you restart often...
- any OS: use a process manager like [pm2](https://pm2.keymetrics.io/docs/usage/restart-strategies/)
- Docker Compose `command: bash -c "node epic-games; node prime-gaming; node gog; echo sleeping; sleep 1d"` additionally add `restart: unless-stopped` to it.
From cf59da5d9e74d49d684762625ae2446341115336 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 23 Jun 2024 18:02:02 +0200
Subject: [PATCH 175/253] gog: new locators, fixes #326, #334
---
gog.js | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/gog.js b/gog.js
index 651afa6..de5d02f 100644
--- a/gog.js
+++ b/gog.js
@@ -93,10 +93,9 @@ try {
if (!await banner.count()) {
console.log('Currently no free giveaway!');
} else {
- const text = await page.locator('.giveaway-banner__title').innerText();
- const title = text.match(/Claim (.*)/)[1];
- const slug = await banner.getAttribute('href');
- const url = `https://gog.com${slug}`;
+ const text = await page.locator('.giveaway__content-header').innerText();
+ const title = text.match(/Claim (.*) and don't miss the/)[1];
+ const url = await banner.locator('a').first().getAttribute('href');
console.log(`Current free game: ${title} - ${url}`);
db.data[user][title] ||= { title, time: datetime(), url };
if (cfg.dryrun) process.exit(1);
From f0f142733e0fd7e365a239fbc8d737a70a739cbc Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 23 Jun 2024 19:14:13 +0200
Subject: [PATCH 176/253] ncu -u: lowdb 6 -> 7 dropped support for Node 16
---
package-lock.json | 647 ++++++++++++++++++++++++++--------------------
package.json | 12 +-
src/util.js | 4 +-
3 files changed, 376 insertions(+), 287 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 930e7bf..3d1d878 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,16 +11,16 @@
"dependencies": {
"chalk": "^5.3.0",
"cross-env": "^7.0.3",
- "dotenv": "^16.3.1",
+ "dotenv": "^16.4.5",
"enquirer": "^2.4.1",
- "lowdb": "^6.1.1",
+ "lowdb": "^7.0.1",
"otplib": "^12.0.1",
- "playwright-firefox": "^1.40.1",
+ "playwright-firefox": "^1.44.1",
"puppeteer-extra-plugin-stealth": "^2.11.2"
},
"devDependencies": {
- "@stylistic/eslint-plugin-js": "^1.5.1",
- "eslint": "^8.56.0"
+ "@stylistic/eslint-plugin-js": "^2.2.2",
+ "eslint": "^9.5.0"
},
"engines": {
"node": ">=15"
@@ -59,16 +59,32 @@
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
- "node_modules/@eslint/eslintrc": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
- "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "node_modules/@eslint/config-array": {
+ "version": "0.16.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.16.0.tgz",
+ "integrity": "sha512-/jmuSd74i4Czf1XXn7wGRWZCuyaUZ330NH1Bek0Pplatt4Sy1S5haN21SCLLdbeKslQ+S0wEJ+++v5YibSi+Lg==",
"dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.4",
+ "debug": "^4.3.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz",
+ "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
- "espree": "^9.6.0",
- "globals": "^13.19.0",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.0",
@@ -76,33 +92,30 @@
"strip-json-comments": "^3.1.1"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@eslint/js": {
- "version": "8.56.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz",
- "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.5.0.tgz",
+ "integrity": "sha512-A7+AOT2ICkodvtsWnxZP4Xxk3NbZ3VMHd8oihydLRGrJgqqdEz1qSeEgXYyT/Cu8h1TWWsQRejIx48mtjZ5y1w==",
"dev": true,
+ "license": "MIT",
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
- "node_modules/@humanwhocodes/config-array": {
- "version": "0.11.13",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
- "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==",
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz",
+ "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==",
"dev": true,
- "dependencies": {
- "@humanwhocodes/object-schema": "^2.0.1",
- "debug": "^4.1.1",
- "minimatch": "^3.0.5"
- },
+ "license": "Apache-2.0",
"engines": {
- "node": ">=10.10.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@humanwhocodes/module-importer": {
@@ -118,11 +131,19 @@
"url": "https://github.com/sponsors/nzakas"
}
},
- "node_modules/@humanwhocodes/object-schema": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz",
- "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
- "dev": true
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz",
+ "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
@@ -202,23 +223,37 @@
}
},
"node_modules/@stylistic/eslint-plugin-js": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.5.1.tgz",
- "integrity": "sha512-iZF0rF+uOhAmOJYOJx1Yvmm3CZ1uz9n0SRd9dpBYHA3QAvfABUORh9LADWwZCigjHJkp2QbCZelGFJGwGz7Siw==",
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.2.2.tgz",
+ "integrity": "sha512-Vj2Q1YHVvJw+ThtOvmk5Yx7wZanVrIBRUTT89horLDb4xdP9GA1um9XOYQC6j67VeUC2gjZQnz5/RVJMzaOhtw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "acorn": "^8.11.2",
- "escape-string-regexp": "^4.0.0",
- "eslint-visitor-keys": "^3.4.3",
- "espree": "^9.6.1"
+ "@types/eslint": "^8.56.10",
+ "acorn": "^8.11.3",
+ "eslint-visitor-keys": "^4.0.0",
+ "espree": "^10.0.1"
},
"engines": {
- "node": "^16.0.0 || >=18.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"peerDependencies": {
"eslint": ">=8.40.0"
}
},
+ "node_modules/@stylistic/eslint-plugin-js/node_modules/eslint-visitor-keys": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
+ "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
"node_modules/@types/debug": {
"version": "4.1.8",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz",
@@ -227,22 +262,42 @@
"@types/ms": "*"
}
},
+ "node_modules/@types/eslint": {
+ "version": "8.56.10",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz",
+ "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*",
+ "@types/json-schema": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/ms": {
"version": "0.7.31",
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA=="
},
- "node_modules/@ungap/structured-clone": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
- "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
- "dev": true
- },
"node_modules/acorn": {
- "version": "8.11.2",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
- "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==",
+ "version": "8.11.3",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
+ "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
"dev": true,
+ "license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
@@ -255,6 +310,7 @@
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
"dev": true,
+ "license": "MIT",
"peerDependencies": {
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
@@ -264,6 +320,7 @@
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -310,7 +367,8 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true
+ "dev": true,
+ "license": "Python-2.0"
},
"node_modules/arr-union": {
"version": "3.1.0",
@@ -339,6 +397,7 @@
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=6"
}
@@ -452,27 +511,16 @@
"node": ">=0.10.0"
}
},
- "node_modules/doctrine": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
- "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
- "dev": true,
- "dependencies": {
- "esutils": "^2.0.2"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
"node_modules/dotenv": {
- "version": "16.3.1",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
- "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
+ "version": "16.4.5",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
+ "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
+ "license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
- "url": "https://github.com/motdotla/dotenv?sponsor=1"
+ "url": "https://dotenvx.com"
}
},
"node_modules/enquirer": {
@@ -500,41 +548,38 @@
}
},
"node_modules/eslint": {
- "version": "8.56.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz",
- "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.5.0.tgz",
+ "integrity": "sha512-+NAOZFrW/jFTS3dASCGBxX1pkFD0/fsO+hfAkJ4TyYKwgsXZbqzrw+seCYFCcPCYXvnD67tAnglU7GQTz6kcVw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
- "@eslint/eslintrc": "^2.1.4",
- "@eslint/js": "8.56.0",
- "@humanwhocodes/config-array": "^0.11.13",
+ "@eslint/config-array": "^0.16.0",
+ "@eslint/eslintrc": "^3.1.0",
+ "@eslint/js": "9.5.0",
"@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.3.0",
"@nodelib/fs.walk": "^1.2.8",
- "@ungap/structured-clone": "^1.2.0",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"debug": "^4.3.2",
- "doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^7.2.2",
- "eslint-visitor-keys": "^3.4.3",
- "espree": "^9.6.1",
- "esquery": "^1.4.2",
+ "eslint-scope": "^8.0.1",
+ "eslint-visitor-keys": "^4.0.0",
+ "espree": "^10.0.1",
+ "esquery": "^1.5.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
- "file-entry-cache": "^6.0.1",
+ "file-entry-cache": "^8.0.0",
"find-up": "^5.0.0",
"glob-parent": "^6.0.2",
- "globals": "^13.19.0",
- "graphemer": "^1.4.0",
"ignore": "^5.2.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"is-path-inside": "^3.0.3",
- "js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
"lodash.merge": "^4.6.2",
@@ -548,23 +593,24 @@
"eslint": "bin/eslint.js"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
- "url": "https://opencollective.com/eslint"
+ "url": "https://eslint.org/donate"
}
},
"node_modules/eslint-scope": {
- "version": "7.2.2",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
- "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz",
+ "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==",
"dev": true,
+ "license": "BSD-2-Clause",
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
@@ -598,18 +644,45 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
- "node_modules/espree": {
- "version": "9.6.1",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
- "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "node_modules/eslint/node_modules/eslint-visitor-keys": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
+ "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
"dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz",
+ "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==",
+ "dev": true,
+ "license": "BSD-2-Clause",
"dependencies": {
- "acorn": "^8.9.0",
+ "acorn": "^8.11.3",
"acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^3.4.1"
+ "eslint-visitor-keys": "^4.0.0"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree/node_modules/eslint-visitor-keys": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
+ "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
@@ -632,6 +705,7 @@
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
"dev": true,
+ "license": "BSD-2-Clause",
"dependencies": {
"estraverse": "^5.2.0"
},
@@ -653,6 +727,7 @@
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true,
+ "license": "BSD-2-Clause",
"engines": {
"node": ">=0.10.0"
}
@@ -661,13 +736,15 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/fast-levenshtein": {
"version": "2.0.6",
@@ -685,15 +762,16 @@
}
},
"node_modules/file-entry-cache": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
- "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "flat-cache": "^3.0.4"
+ "flat-cache": "^4.0.0"
},
"engines": {
- "node": "^10.12.0 || >=12.0.0"
+ "node": ">=16.0.0"
}
},
"node_modules/find-up": {
@@ -713,24 +791,25 @@
}
},
"node_modules/flat-cache": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
- "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"flatted": "^3.2.9",
- "keyv": "^4.5.3",
- "rimraf": "^3.0.2"
+ "keyv": "^4.5.4"
},
"engines": {
- "node": "^10.12.0 || >=12.0.0"
+ "node": ">=16"
}
},
"node_modules/flatted": {
- "version": "3.2.9",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz",
- "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
- "dev": true
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
+ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
+ "dev": true,
+ "license": "ISC"
},
"node_modules/for-in": {
"version": "1.0.2",
@@ -801,15 +880,13 @@
}
},
"node_modules/globals": {
- "version": "13.24.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
- "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"dev": true,
- "dependencies": {
- "type-fest": "^0.20.2"
- },
+ "license": "MIT",
"engines": {
- "node": ">=8"
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -820,12 +897,6 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
},
- "node_modules/graphemer": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
- "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
- "dev": true
- },
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -836,10 +907,11 @@
}
},
"node_modules/ignore": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
- "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==",
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
+ "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">= 4"
}
@@ -849,6 +921,7 @@
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
@@ -955,6 +1028,7 @@
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
@@ -966,13 +1040,15 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
@@ -996,6 +1072,7 @@
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"json-buffer": "3.0.1"
}
@@ -1054,14 +1131,15 @@
"dev": true
},
"node_modules/lowdb": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-6.1.1.tgz",
- "integrity": "sha512-HO13FCxI8SCwfj2JRXOKgXggxnmfSc+l0aJsZ5I34X3pwzG/DPBSKyKu3Zkgg/pNmx854SVgE2la0oUeh6wzNw==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-7.0.1.tgz",
+ "integrity": "sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==",
+ "license": "MIT",
"dependencies": {
- "steno": "^3.1.1"
+ "steno": "^4.0.2"
},
"engines": {
- "node": ">=16"
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/typicode"
@@ -1192,6 +1270,7 @@
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"callsites": "^3.0.0"
},
@@ -1225,9 +1304,10 @@
}
},
"node_modules/playwright-core": {
- "version": "1.40.1",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.1.tgz",
- "integrity": "sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ==",
+ "version": "1.44.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz",
+ "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==",
+ "license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
@@ -1236,12 +1316,13 @@
}
},
"node_modules/playwright-firefox": {
- "version": "1.40.1",
- "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.40.1.tgz",
- "integrity": "sha512-+C5eOWZv/CvALM0yBZFSYThvUzGKusNw6soDMhTEJwDUB5i9q/yZVFkj6I8CFXM4U6Pf1q0PXHMca70HoIwnCQ==",
+ "version": "1.44.1",
+ "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.44.1.tgz",
+ "integrity": "sha512-ywwHQGTLM7P5r3SzVTSyRQQUK8xsCj6MrIqY9cn8SNz+GkKL4atZb1KuYDulxrfKFzZWXLJ8M+VGc0/vNWLMfA==",
"hasInstallScript": true,
+ "license": "Apache-2.0",
"dependencies": {
- "playwright-core": "1.40.1"
+ "playwright-core": "1.44.1"
},
"bin": {
"playwright": "cli.js"
@@ -1264,6 +1345,7 @@
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=6"
}
@@ -1395,6 +1477,7 @@
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=4"
}
@@ -1499,11 +1582,12 @@
}
},
"node_modules/steno": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/steno/-/steno-3.1.1.tgz",
- "integrity": "sha512-B7c6EVH7oEiaMRW36SjUnktkDwp/qd4pQiduylyiqvcZEZDeX0IIFZRBZdwO/RaVo60M0wkDwC0e8yeKaR4VGg==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/steno/-/steno-4.0.2.tgz",
+ "integrity": "sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==",
+ "license": "MIT",
"engines": {
- "node": ">=16"
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/typicode"
@@ -1525,6 +1609,7 @@
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
},
@@ -1570,18 +1655,6 @@
"node": ">= 0.8.0"
}
},
- "node_modules/type-fest": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
@@ -1595,6 +1668,7 @@
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
+ "license": "BSD-2-Clause",
"dependencies": {
"punycode": "^2.1.0"
}
@@ -1653,16 +1727,27 @@
"integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
"dev": true
},
+ "@eslint/config-array": {
+ "version": "0.16.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.16.0.tgz",
+ "integrity": "sha512-/jmuSd74i4Czf1XXn7wGRWZCuyaUZ330NH1Bek0Pplatt4Sy1S5haN21SCLLdbeKslQ+S0wEJ+++v5YibSi+Lg==",
+ "dev": true,
+ "requires": {
+ "@eslint/object-schema": "^2.1.4",
+ "debug": "^4.3.1",
+ "minimatch": "^3.0.5"
+ }
+ },
"@eslint/eslintrc": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
- "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz",
+ "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==",
"dev": true,
"requires": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
- "espree": "^9.6.0",
- "globals": "^13.19.0",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.0",
@@ -1671,21 +1756,16 @@
}
},
"@eslint/js": {
- "version": "8.56.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz",
- "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.5.0.tgz",
+ "integrity": "sha512-A7+AOT2ICkodvtsWnxZP4Xxk3NbZ3VMHd8oihydLRGrJgqqdEz1qSeEgXYyT/Cu8h1TWWsQRejIx48mtjZ5y1w==",
"dev": true
},
- "@humanwhocodes/config-array": {
- "version": "0.11.13",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
- "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==",
- "dev": true,
- "requires": {
- "@humanwhocodes/object-schema": "^2.0.1",
- "debug": "^4.1.1",
- "minimatch": "^3.0.5"
- }
+ "@eslint/object-schema": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz",
+ "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==",
+ "dev": true
},
"@humanwhocodes/module-importer": {
"version": "1.0.1",
@@ -1693,10 +1773,10 @@
"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
"dev": true
},
- "@humanwhocodes/object-schema": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz",
- "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
+ "@humanwhocodes/retry": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz",
+ "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==",
"dev": true
},
"@nodelib/fs.scandir": {
@@ -1768,15 +1848,23 @@
}
},
"@stylistic/eslint-plugin-js": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.5.1.tgz",
- "integrity": "sha512-iZF0rF+uOhAmOJYOJx1Yvmm3CZ1uz9n0SRd9dpBYHA3QAvfABUORh9LADWwZCigjHJkp2QbCZelGFJGwGz7Siw==",
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.2.2.tgz",
+ "integrity": "sha512-Vj2Q1YHVvJw+ThtOvmk5Yx7wZanVrIBRUTT89horLDb4xdP9GA1um9XOYQC6j67VeUC2gjZQnz5/RVJMzaOhtw==",
"dev": true,
"requires": {
- "acorn": "^8.11.2",
- "escape-string-regexp": "^4.0.0",
- "eslint-visitor-keys": "^3.4.3",
- "espree": "^9.6.1"
+ "@types/eslint": "^8.56.10",
+ "acorn": "^8.11.3",
+ "eslint-visitor-keys": "^4.0.0",
+ "espree": "^10.0.1"
+ },
+ "dependencies": {
+ "eslint-visitor-keys": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
+ "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
+ "dev": true
+ }
}
},
"@types/debug": {
@@ -1787,21 +1875,37 @@
"@types/ms": "*"
}
},
+ "@types/eslint": {
+ "version": "8.56.10",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz",
+ "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==",
+ "dev": true,
+ "requires": {
+ "@types/estree": "*",
+ "@types/json-schema": "*"
+ }
+ },
+ "@types/estree": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+ "dev": true
+ },
+ "@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true
+ },
"@types/ms": {
"version": "0.7.31",
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA=="
},
- "@ungap/structured-clone": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
- "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
- "dev": true
- },
"acorn": {
- "version": "8.11.2",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
- "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==",
+ "version": "8.11.3",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
+ "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
"dev": true
},
"acorn-jsx": {
@@ -1947,19 +2051,10 @@
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
},
- "doctrine": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
- "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2"
- }
- },
"dotenv": {
- "version": "16.3.1",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
- "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ=="
+ "version": "16.4.5",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
+ "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg=="
},
"enquirer": {
"version": "2.4.1",
@@ -1977,41 +2072,37 @@
"dev": true
},
"eslint": {
- "version": "8.56.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz",
- "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.5.0.tgz",
+ "integrity": "sha512-+NAOZFrW/jFTS3dASCGBxX1pkFD0/fsO+hfAkJ4TyYKwgsXZbqzrw+seCYFCcPCYXvnD67tAnglU7GQTz6kcVw==",
"dev": true,
"requires": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
- "@eslint/eslintrc": "^2.1.4",
- "@eslint/js": "8.56.0",
- "@humanwhocodes/config-array": "^0.11.13",
+ "@eslint/config-array": "^0.16.0",
+ "@eslint/eslintrc": "^3.1.0",
+ "@eslint/js": "9.5.0",
"@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.3.0",
"@nodelib/fs.walk": "^1.2.8",
- "@ungap/structured-clone": "^1.2.0",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"debug": "^4.3.2",
- "doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^7.2.2",
- "eslint-visitor-keys": "^3.4.3",
- "espree": "^9.6.1",
- "esquery": "^1.4.2",
+ "eslint-scope": "^8.0.1",
+ "eslint-visitor-keys": "^4.0.0",
+ "espree": "^10.0.1",
+ "esquery": "^1.5.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
- "file-entry-cache": "^6.0.1",
+ "file-entry-cache": "^8.0.0",
"find-up": "^5.0.0",
"glob-parent": "^6.0.2",
- "globals": "^13.19.0",
- "graphemer": "^1.4.0",
"ignore": "^5.2.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"is-path-inside": "^3.0.3",
- "js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
"lodash.merge": "^4.6.2",
@@ -2031,13 +2122,19 @@
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
+ },
+ "eslint-visitor-keys": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
+ "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
+ "dev": true
}
}
},
"eslint-scope": {
- "version": "7.2.2",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
- "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz",
+ "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==",
"dev": true,
"requires": {
"esrecurse": "^4.3.0",
@@ -2051,14 +2148,22 @@
"dev": true
},
"espree": {
- "version": "9.6.1",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
- "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz",
+ "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==",
"dev": true,
"requires": {
- "acorn": "^8.9.0",
+ "acorn": "^8.11.3",
"acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^3.4.1"
+ "eslint-visitor-keys": "^4.0.0"
+ },
+ "dependencies": {
+ "eslint-visitor-keys": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
+ "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
+ "dev": true
+ }
}
},
"esquery": {
@@ -2119,12 +2224,12 @@
}
},
"file-entry-cache": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
- "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
"dev": true,
"requires": {
- "flat-cache": "^3.0.4"
+ "flat-cache": "^4.0.0"
}
},
"find-up": {
@@ -2138,20 +2243,19 @@
}
},
"flat-cache": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
- "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
"dev": true,
"requires": {
"flatted": "^3.2.9",
- "keyv": "^4.5.3",
- "rimraf": "^3.0.2"
+ "keyv": "^4.5.4"
}
},
"flatted": {
- "version": "3.2.9",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz",
- "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
+ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
"dev": true
},
"for-in": {
@@ -2205,25 +2309,16 @@
}
},
"globals": {
- "version": "13.24.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
- "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
- "dev": true,
- "requires": {
- "type-fest": "^0.20.2"
- }
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true
},
"graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
},
- "graphemer": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
- "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
- "dev": true
- },
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -2231,9 +2326,9 @@
"dev": true
},
"ignore": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
- "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==",
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
+ "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
"dev": true
},
"import-fresh": {
@@ -2399,11 +2494,11 @@
"dev": true
},
"lowdb": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-6.1.1.tgz",
- "integrity": "sha512-HO13FCxI8SCwfj2JRXOKgXggxnmfSc+l0aJsZ5I34X3pwzG/DPBSKyKu3Zkgg/pNmx854SVgE2la0oUeh6wzNw==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-7.0.1.tgz",
+ "integrity": "sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==",
"requires": {
- "steno": "^3.1.1"
+ "steno": "^4.0.2"
}
},
"merge-deep": {
@@ -2527,16 +2622,16 @@
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
"playwright-core": {
- "version": "1.40.1",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.1.tgz",
- "integrity": "sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ=="
+ "version": "1.44.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz",
+ "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA=="
},
"playwright-firefox": {
- "version": "1.40.1",
- "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.40.1.tgz",
- "integrity": "sha512-+C5eOWZv/CvALM0yBZFSYThvUzGKusNw6soDMhTEJwDUB5i9q/yZVFkj6I8CFXM4U6Pf1q0PXHMca70HoIwnCQ==",
+ "version": "1.44.1",
+ "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.44.1.tgz",
+ "integrity": "sha512-ywwHQGTLM7P5r3SzVTSyRQQUK8xsCj6MrIqY9cn8SNz+GkKL4atZb1KuYDulxrfKFzZWXLJ8M+VGc0/vNWLMfA==",
"requires": {
- "playwright-core": "1.40.1"
+ "playwright-core": "1.44.1"
}
},
"prelude-ls": {
@@ -2668,9 +2763,9 @@
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
},
"steno": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/steno/-/steno-3.1.1.tgz",
- "integrity": "sha512-B7c6EVH7oEiaMRW36SjUnktkDwp/qd4pQiduylyiqvcZEZDeX0IIFZRBZdwO/RaVo60M0wkDwC0e8yeKaR4VGg=="
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/steno/-/steno-4.0.2.tgz",
+ "integrity": "sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A=="
},
"strip-ansi": {
"version": "6.0.1",
@@ -2715,12 +2810,6 @@
"prelude-ls": "^1.2.1"
}
},
- "type-fest": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
- "dev": true
- },
"universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
diff --git a/package.json b/package.json
index 6c954cd..b2eee9f 100644
--- a/package.json
+++ b/package.json
@@ -17,20 +17,20 @@
},
"type": "module",
"engines": {
- "node": ">=15"
+ "node": ">=17"
},
"dependencies": {
"chalk": "^5.3.0",
"cross-env": "^7.0.3",
- "dotenv": "^16.3.1",
+ "dotenv": "^16.4.5",
"enquirer": "^2.4.1",
- "lowdb": "^6.1.1",
+ "lowdb": "^7.0.1",
"otplib": "^12.0.1",
- "playwright-firefox": "^1.40.1",
+ "playwright-firefox": "^1.44.1",
"puppeteer-extra-plugin-stealth": "^2.11.2"
},
"devDependencies": {
- "@stylistic/eslint-plugin-js": "^1.5.1",
- "eslint": "^8.56.0"
+ "@stylistic/eslint-plugin-js": "^2.2.2",
+ "eslint": "^9.5.0"
}
}
diff --git a/src/util.js b/src/util.js
index bda3f78..20c2f97 100644
--- a/src/util.js
+++ b/src/util.js
@@ -11,8 +11,8 @@ export const dataDir = s => path.resolve(__dirname, '..', 'data', s);
export const resolve = (...a) => a.length && a[0] == '0' ? null : path.resolve(...a);
// json database
-import { JSONPreset } from 'lowdb/node';
-export const jsonDb = (file, defaultData) => JSONPreset(dataDir(file), defaultData);
+import { JSONFilePreset } from 'lowdb/node';
+export const jsonDb = (file, defaultData) => JSONFilePreset(dataDir(file), defaultData);
export const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
// date and time as UTC (no timezone offset) in nicely readable and sortable format, e.g., 2022-10-06 12:05:27.313
From 30b1835bdbf850eae389a0010945ca607cf61f6a Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 23 Jun 2024 19:16:01 +0200
Subject: [PATCH 177/253] pg: reverse games: oldest/bottom to newest/top
---
prime-gaming.js | 3 +++
1 file changed, 3 insertions(+)
diff --git a/prime-gaming.js b/prime-gaming.js
index b555f67..ca3fcee 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -107,6 +107,9 @@ try {
// can't use .all() since the list of elements via locator will change after click while we iterate over it
const internal = await games.locator('.item-card__action:has([data-a-target="FGWPOffer"])').elementHandles();
const external = await games.locator('.item-card__action:has([data-a-target="ExternalOfferClaim"])').all();
+ // bottom to top: oldest to newest games
+ internal.reverse();
+ external.reverse();
console.log('Number of free unclaimed games (Prime Gaming):', internal.length);
// claim games in internal store
for (const card of internal) {
From f920aa26d0d89404a97a715c14f8f7b9400beda5 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 23 Jun 2024 19:18:30 +0200
Subject: [PATCH 178/253] pg: checkTimeLeft via PG_TIMELEFT=1
---
prime-gaming.js | 19 +++++++++++++++++++
src/config.js | 3 ++-
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index ca3fcee..d8caaeb 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -110,6 +110,23 @@ try {
// bottom to top: oldest to newest games
internal.reverse();
external.reverse();
+ const checkTimeLeft = async url => {
+ // console.log(' Checking time left for game:', url);
+ const check = async p => {
+ console.log(' ', await p.locator('.availability-date').innerText());
+ const dueDateOrg = await p.locator('.availability-date .tw-bold').innerText();
+ const dueDate = datetime(new Date(Date.parse(dueDateOrg + ' 17:00')));
+ console.log(' Due date:', dueDate);
+ };
+ if (page.url() == url) {
+ await check(page);
+ } else {
+ const p = await context.newPage();
+ await p.goto(url, { waitUntil: 'domcontentloaded' });
+ await check(p);
+ p.close();
+ }
+ };
console.log('Number of free unclaimed games (Prime Gaming):', internal.length);
// claim games in internal store
for (const card of internal) {
@@ -118,6 +135,7 @@ try {
const slug = await (await card.$('a')).getAttribute('href');
const url = 'https://gaming.amazon.com' + slug.split('?')[0];
console.log('Current free game:', title);
+ if (cfg.pg_timeLeft) await checkTimeLeft(url);
if (cfg.dryrun) continue;
if (cfg.interactive && !await confirm()) continue;
await (await card.$('.tw-button:has-text("Claim")')).click();
@@ -144,6 +162,7 @@ try {
const item_text = await page.innerText('[data-a-target="DescriptionItemDetails"]');
const store = item_text.toLowerCase().replace(/.* on /, '').slice(0, -1);
console.log(' External store:', store);
+ if (cfg.pg_timeLeft) await checkTimeLeft(url);
if (cfg.dryrun) continue;
if (cfg.interactive && !await confirm()) continue;
await Promise.any([page.click('[data-a-target="buy-box"] .tw-button:has-text("Get game")'), page.click('[data-a-target="buy-box"] .tw-button:has-text("Claim")'), page.click('.tw-button:has-text("Complete Claim")'), page.waitForSelector('div:has-text("Link game account")'), page.waitForSelector('.thank-you-title:has-text("Success")')]); // waits for navigation
diff --git a/src/config.js b/src/config.js
index bd41c7d..56e42d2 100644
--- a/src/config.js
+++ b/src/config.js
@@ -46,7 +46,8 @@ export const cfg = {
xbox_email: process.env.XBOX_EMAIL || process.env.EMAIL,
xbox_password: process.env.XBOX_PASSWORD || process.env.PASSWORD,
xbox_otpkey: process.env.XBOX_OTPKEY,
- // experimmental - likely to change
+ // experimmental
pg_redeem: process.env.PG_REDEEM == '1', // prime-gaming: redeem keys on external stores
pg_claimdlc: process.env.PG_CLAIMDLC == '1', // prime-gaming: claim in-game content
+ pg_timeLeft: process.env.PG_TIMELEFT == '1', // prime-gaming: list time left to claim
};
From 43bef9b23c93104345fa2e9b52c1db6ef231bbba Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 23 Jun 2024 20:44:03 +0200
Subject: [PATCH 179/253] pg: redeem microsoft store and xbox, WIP, #315, #5
---
prime-gaming.js | 43 +++++++++++++++++++++++++++++++------------
1 file changed, 31 insertions(+), 12 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index d8caaeb..1f20a47 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -155,6 +155,7 @@ try {
// await (await card.$('text=Claim')).click(); // goes to URL of game, no need to wait
external_info.push({ title, url });
}
+ // external_info = [ { title: 'Fallout 76 (XBOX)', url: 'https://gaming.amazon.com/fallout-76-xbox-fgwp/dp/amzn1.pg.item.9fe17d7b-b6c2-4f58-b494-cc4e79528d0b?ingress=amzn&ref_=SM_Fallout76XBOX_S01_FGWP_CRWN' } ];
for (const { title, url } of external_info) {
console.log('Current free game:', title); // , url);
await page.goto(url, { waitUntil: 'domcontentloaded' });
@@ -185,7 +186,8 @@ try {
const redeem = {
// 'origin': 'https://www.origin.com/redeem', // TODO still needed or now only via account linking?
'gog.com': 'https://www.gog.com/redeem',
- 'microsoft games': 'https://redeem.microsoft.com',
+ 'microsoft store': 'https://account.microsoft.com/billing/redeem',
+ xbox: 'https://account.microsoft.com/billing/redeem',
'legacy games': 'https://www.legacygames.com/primedeal',
};
if (store in redeem) { // did not work for linked origin: && !await page.locator('div:has-text("Successfully Claimed")').count()
@@ -240,27 +242,44 @@ try {
console.log(' Unknown Response 2 - please report in https://github.com/vogler/free-games-claimer/issues/5');
}
}
- } else if (store == 'microsoft games') {
- console.error(` Redeem on ${store} not yet implemented!`);
+ } else if (store == 'microsoft store' || store == 'xbox') {
+ console.error(` Redeem on ${store} is experimental!`);
+ // await page2.pause();
if (page2.url().startsWith('https://login.')) {
- console.error(' Not logged in! Use the browser to login manually.');
+ console.error(' Not logged in! Use the browser to login manually. Waiting for 60s.');
+ await page2.waitForTimeout(60 * 1000);
redeem_action = 'redeem (login)';
} else {
- const r = page2.waitForResponse(r => r.url().startsWith('https://purchase.mp.microsoft.com/'));
- await page2.fill('[name=tokenString]', code);
+ const iframe = page2.frameLocator('#redeem-iframe');
+ const input = iframe.locator('[name=tokenString]');
+ await input.waitFor();
+ await input.fill(code);
+ const r = page2.waitForResponse(r => r.url().startsWith('https://cart.production.store-web.dynamics.com/v1.0/Redeem/PrepareRedeem'));
// console.log(await page2.locator('.redeem_code_error').innerText());
const rt = await (await r).text();
- console.debug(` Response: ${rt}`);
// {"code":"NotFound","data":[],"details":[],"innererror":{"code":"TokenNotFound",...
- const reason = JSON.parse(rt).code;
- if (reason == 'NotFound') {
+ const j = JSON.parse(rt);
+ const reason = j?.events?.cart.length && j.events.cart[0]?.data?.reason;
+ if (reason == 'TokenNotFound') {
redeem_action = 'redeem (not found)';
console.error(' Code was not found!');
+ } else if (j?.productInfos?.length && j.productInfos[0]?.redeemable) {
+ await iframe.locator('button:has-text("Next")').click();
+ await iframe.locator('button:has-text("Confirm")').click();
+ const r = page2.waitForResponse(r => r.url().startsWith('https://cart.production.store-web.dynamics.com/v1.0/Redeem/RedeemToken'));
+ const j = JSON.parse(await (await r).text());
+ 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?
+ 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');
+ }
} else { // TODO find out other responses
- await page2.click('#nextButton');
- redeem_action = 'redeemed?';
+ redeem_action = 'unknown';
+ console.debug(` Response: ${rt}`);
console.log(' Redeemed successfully? Please report your Response from above (if it is new) in https://github.com/vogler/free-games-claimer/issues/5');
- db.data[user][title].status = 'claimed and redeemed?';
}
}
} else if (store == 'legacy games') {
From 601e893714731767ca5ce7ef6ca46c1e3330ed60 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 23 Jun 2024 20:50:00 +0200
Subject: [PATCH 180/253] README: link example how to set env vars on Windows,
closes #314
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index a6e3825..a2da668 100644
--- a/README.md
+++ b/README.md
@@ -103,7 +103,7 @@ You can pass variables using `-e VAR=VAL`, for example `docker run -e EMAIL=foo@
If you are using [docker compose](https://docs.docker.com/compose/environment-variables/) (or Portainer etc.), you can put options in the `environment:` section.
##### Without Docker
-On Linux/macOS you can prefix the variables you want to set, for example `EMAIL=foo@bar.baz SHOW=1 node epic-games` will show the browser and skip asking you for your login email.
+On Linux/macOS you can prefix the variables you want to set, for example `EMAIL=foo@bar.baz SHOW=1 node epic-games` will show the browser and skip asking you for your login email. On Windows you have to use `set`, [example](https://github.com/vogler/free-games-claimer/issues/314).
You can also put options in `data/config.env` which will be loaded by [dotenv](https://github.com/motdotla/dotenv).
### Notifications
From 076738e3012ff9f90b75e9c6161f63314c96d6a1 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 25 Jun 2024 13:09:10 +0200
Subject: [PATCH 181/253] gog: fail if WIDTH<1280 due to hidden username,
closes #335
---
gog.js | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/gog.js b/gog.js
index de5d02f..40d8fad 100644
--- a/gog.js
+++ b/gog.js
@@ -10,6 +10,11 @@ console.log(datetime(), 'started checking gog');
const db = await jsonDb('gog.json', {});
+if (cfg.width < 1280) { // otherwise 'Sign in' and #menuUsername are hidden (but attached to DOM), see https://github.com/vogler/free-games-claimer/issues/335
+ console.error(`Window width is set to ${cfg.width} but needs to be at least 1280 for GOG!`);
+ process.exit(1);
+}
+
// https://playwright.dev/docs/auth#multi-factor-authentication
const context = await firefox.launchPersistentContext(cfg.dir.browser, {
headless: cfg.headless,
From c0eb6dbb0bb6c64e268ae70b9e37586c92a59366 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 25 Jun 2024 13:24:55 +0200
Subject: [PATCH 182/253] ncu -u: playwright 1.44.1 -> 1.45.0
---
package-lock.json | 36 ++++++++++++++++++------------------
package.json | 2 +-
2 files changed, 19 insertions(+), 19 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 3d1d878..e6f66b2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,7 +15,7 @@
"enquirer": "^2.4.1",
"lowdb": "^7.0.1",
"otplib": "^12.0.1",
- "playwright-firefox": "^1.44.1",
+ "playwright-firefox": "^1.45.0",
"puppeteer-extra-plugin-stealth": "^2.11.2"
},
"devDependencies": {
@@ -23,7 +23,7 @@
"eslint": "^9.5.0"
},
"engines": {
- "node": ">=15"
+ "node": ">=17"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -1304,31 +1304,31 @@
}
},
"node_modules/playwright-core": {
- "version": "1.44.1",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz",
- "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==",
+ "version": "1.45.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.0.tgz",
+ "integrity": "sha512-lZmHlFQ0VYSpAs43dRq1/nJ9G/6SiTI7VPqidld9TDefL9tX87bTKExWZZUF5PeRyqtXqd8fQi2qmfIedkwsNQ==",
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
- "node": ">=16"
+ "node": ">=18"
}
},
"node_modules/playwright-firefox": {
- "version": "1.44.1",
- "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.44.1.tgz",
- "integrity": "sha512-ywwHQGTLM7P5r3SzVTSyRQQUK8xsCj6MrIqY9cn8SNz+GkKL4atZb1KuYDulxrfKFzZWXLJ8M+VGc0/vNWLMfA==",
+ "version": "1.45.0",
+ "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.45.0.tgz",
+ "integrity": "sha512-JmGESfFR8xTjAYQzECYO00yBbSSnu4dBImsrmJVeOXTvT+i9p1dpVUaxKz6lTFMI/xzYROqB4E4Km8NBiOgslw==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
- "playwright-core": "1.44.1"
+ "playwright-core": "1.45.0"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
- "node": ">=16"
+ "node": ">=18"
}
},
"node_modules/prelude-ls": {
@@ -2622,16 +2622,16 @@
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
"playwright-core": {
- "version": "1.44.1",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz",
- "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA=="
+ "version": "1.45.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.0.tgz",
+ "integrity": "sha512-lZmHlFQ0VYSpAs43dRq1/nJ9G/6SiTI7VPqidld9TDefL9tX87bTKExWZZUF5PeRyqtXqd8fQi2qmfIedkwsNQ=="
},
"playwright-firefox": {
- "version": "1.44.1",
- "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.44.1.tgz",
- "integrity": "sha512-ywwHQGTLM7P5r3SzVTSyRQQUK8xsCj6MrIqY9cn8SNz+GkKL4atZb1KuYDulxrfKFzZWXLJ8M+VGc0/vNWLMfA==",
+ "version": "1.45.0",
+ "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.45.0.tgz",
+ "integrity": "sha512-JmGESfFR8xTjAYQzECYO00yBbSSnu4dBImsrmJVeOXTvT+i9p1dpVUaxKz6lTFMI/xzYROqB4E4Km8NBiOgslw==",
"requires": {
- "playwright-core": "1.44.1"
+ "playwright-core": "1.45.0"
}
},
"prelude-ls": {
diff --git a/package.json b/package.json
index b2eee9f..2c64f46 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,7 @@
"enquirer": "^2.4.1",
"lowdb": "^7.0.1",
"otplib": "^12.0.1",
- "playwright-firefox": "^1.44.1",
+ "playwright-firefox": "^1.45.0",
"puppeteer-extra-plugin-stealth": "^2.11.2"
},
"devDependencies": {
From 55226933c0eb01a0f0b258732e1bb3fc67849200 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 25 Jun 2024 15:30:02 +0200
Subject: [PATCH 183/253] filenamify datetime recordHar for Windows : -> ., fix
#336
---
epic-games.js | 2 +-
gog.js | 2 +-
prime-gaming.js | 2 +-
unrealengine.js | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index 54ca597..5487445 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -25,7 +25,7 @@ const context = await firefox.launchPersistentContext(cfg.dir.browser, {
// 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
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
+ recordHar: cfg.record ? { path: `data/record/eg-${filenamify(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
// user settings for firefox have to be put in $BROWSER_DIR/user.js
args: [ // https://wiki.mozilla.org/Firefox/CommandLineOptions
diff --git a/gog.js b/gog.js
index 40d8fad..0688626 100644
--- a/gog.js
+++ b/gog.js
@@ -21,7 +21,7 @@ const context = await firefox.launchPersistentContext(cfg.dir.browser, {
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
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
+ recordHar: cfg.record ? { path: `data/record/gog-${filenamify(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
});
diff --git a/prime-gaming.js b/prime-gaming.js
index 6e8cb03..91d3fe2 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -19,7 +19,7 @@ const context = await firefox.launchPersistentContext(cfg.dir.browser, {
viewport: { width: cfg.width, height: cfg.height },
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/pg-${datetime()}.har` } : undefined, // will record a HAR file with network requests and responses; can be imported in Chrome devtools
+ recordHar: cfg.record ? { path: `data/record/pg-${filenamify(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
});
diff --git a/unrealengine.js b/unrealengine.js
index e67633e..2bb8ee9 100644
--- a/unrealengine.js
+++ b/unrealengine.js
@@ -25,7 +25,7 @@ const context = await firefox.launchPersistentContext(cfg.dir.browser, {
// userAgent for firefox: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:106.0) Gecko/20100101 Firefox/106.0
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/ue-${datetime()}.har` } : undefined, // will record a HAR file with network requests and responses; can be imported in Chrome devtools
+ recordHar: cfg.record ? { path: `data/record/ue-${filenamify(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
});
From 9cf5d2f7f21157566dad636b75febb831d14b333 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 25 Jun 2024 15:50:02 +0200
Subject: [PATCH 184/253] pg: include code in redeem_url for gog, closes #330
---
prime-gaming.js | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 91d3fe2..5e3e283 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -196,7 +196,9 @@ try {
if (store == 'legacy games') { // may be different URL like https://legacygames.com/primeday/puzzleoftheyear/
redeem[store] = await (await page.$('li:has-text("Click here") a')).getAttribute('href'); // full text: Click here to enter your redemption code.
}
- console.log(' URL to redeem game:', redeem[store]);
+ let redeem_url = redeem[store];
+ if (store == 'gog.com') redeem_url += '/' + code; // to log and notify, but can't use for goto below (captcha)
+ console.log(' URL to redeem game:', redeem_url);
db.data[user][title].code = code;
let redeem_action = 'redeem';
if (cfg.pg_redeem) { // try to redeem keys on external stores
@@ -305,7 +307,7 @@ try {
if (cfg.debug) await page2.pause();
await page2.close();
}
- notify_game.status = `${redeem_action} ${code} on ${store}`;
+ notify_game.status = `${redeem_action} ${code} on ${store}`;
} else {
notify_game.status = `claimed on ${store}`;
db.data[user][title].status = 'claimed';
From c4be7ece21702fb73bc7ed61bafd7abc6133a911 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 25 Jun 2024 18:45:21 +0200
Subject: [PATCH 185/253] xbox: fixup #307
---
xbox.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/xbox.js b/xbox.js
index 67054ef..fd78c66 100644
--- a/xbox.js
+++ b/xbox.js
@@ -16,7 +16,6 @@ const URL_CLAIM = 'https://www.xbox.com/en-US/live/gold'; // #gameswithgold";
console.log(datetime(), 'started checking xbox');
const db = await jsonDb('xbox.json', {});
-db.data ||= {};
handleSIGINT();
From 9af36e902c0634d8fc19fdb8a75267b0887ccf47 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 26 Jun 2024 17:10:04 +0200
Subject: [PATCH 186/253] eg: fix login: password now together with email
again, detect login error, closes #338, closes #337
---
epic-games.js | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index 5487445..70807b8 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -20,7 +20,7 @@ if (cfg.time) console.time('startup');
const context = await firefox.launchPersistentContext(cfg.dir.browser, {
headless: cfg.headless,
viewport: { width: cfg.width, height: cfg.height },
- 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: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0', // 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
@@ -90,8 +90,6 @@ try {
if (!email) await notifyBrowserLogin();
else {
// await page.click('text=Sign in with Epic Games');
- await page.fill('#email', email);
- await page.click('button[type="submit"]');
page.waitForSelector('.h_captcha_challenge iframe').then(async () => {
console.error('Got a captcha during login (likely due to too many attempts)! You may solve it in the browser, get a new IP or try again in a few hours.');
await notify('epic-games: got captcha during login. Please check.');
@@ -99,10 +97,20 @@ try {
page.waitForSelector('p:has-text("Incorrect response.")').then(async () => {
console.error('Incorrect repsonse for captcha!');
}).catch(_ => { });
+ await page.fill('#email', email);
+ // await page.click('button[type="submit"]'); login was split in two steps for some time, now email and password are on the same form again
const password = email && (cfg.eg_password || await prompt({ type: 'password', message: 'Enter password' }));
if (!password) await notifyBrowserLogin();
- await page.fill('#password', password);
- await page.click('button[type="submit"]');
+ else {
+ await page.fill('#password', password);
+ await page.click('button[type="submit"]');
+ }
+ const error = page.locator('#form-error-message');
+ error.waitFor().then(async () => {
+ console.error('Login error:', await error.innerText());
+ await context.close(); // finishes potential recording
+ process.exit(1);
+ }).catch(_ => { });
// handle MFA, but don't await it
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 ...');
From d85cd8d20ccb27834bfaafd98f8be95fecb7a3d5 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 27 Jun 2024 10:54:46 +0200
Subject: [PATCH 187/253] rm xbox.js, no more games with gold, closes #286,
closes #339
TODO integrate login code for microsoft games in prime-gaming
---
src/config.js | 4 -
xbox.js | 250 --------------------------------------------------
2 files changed, 254 deletions(-)
delete mode 100644 xbox.js
diff --git a/src/config.js b/src/config.js
index 9624471..03414a0 100644
--- a/src/config.js
+++ b/src/config.js
@@ -42,10 +42,6 @@ export const cfg = {
gog_password: process.env.GOG_PASSWORD || process.env.PASSWORD,
gog_newsletter: process.env.GOG_NEWSLETTER == '1', // do not unsubscribe from newsletter after claiming a game
// OTP only via GOG_EMAIL, can't add app...
- // auth xbox
- xbox_email: process.env.XBOX_EMAIL || process.env.EMAIL,
- xbox_password: process.env.XBOX_PASSWORD || process.env.PASSWORD,
- xbox_otpkey: process.env.XBOX_OTPKEY,
// experimmental
pg_redeem: process.env.PG_REDEEM == '1', // prime-gaming: redeem keys on external stores
lg_email: process.env.LG_EMAIL || process.env.PG_EMAIL || process.env.EMAIL, // prime-gaming: external: legacy-games: email to use for redeeming
diff --git a/xbox.js b/xbox.js
deleted file mode 100644
index fd78c66..0000000
--- a/xbox.js
+++ /dev/null
@@ -1,250 +0,0 @@
-import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdated playwright-extra
-import { authenticator } from 'otplib';
-import {
- datetime,
- handleSIGINT,
- html_game_list,
- jsonDb,
- notify,
- prompt,
-} from './src/util.js';
-import { cfg } from './src/config.js';
-
-// ### SETUP
-const URL_CLAIM = 'https://www.xbox.com/en-US/live/gold'; // #gameswithgold";
-
-console.log(datetime(), 'started checking xbox');
-
-const db = await jsonDb('xbox.json', {});
-
-handleSIGINT();
-
-// https://playwright.dev/docs/auth#multi-factor-authentication
-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
-});
-
-if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
-
-const page = context.pages().length
- ? context.pages()[0]
- : await context.newPage(); // should always exist
-await page.setViewportSize({ width: cfg.width, height: cfg.height }); // TODO workaround for https://github.com/vogler/free-games-claimer/issues/277 until Playwright fixes it
-
-const notify_games = [];
-let user;
-
-main();
-
-async function main() {
- try {
- await performLogin();
- await getAndSaveUser();
- await redeemFreeGames();
- } catch (error) {
- console.error(error);
- process.exitCode ||= 1;
- if (error.message && process.exitCode != 130) notify(`xbox 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(`xbox (${user}):
${html_game_list(notify_games)}`);
- }
- await context.close();
- }
-}
-
-async function performLogin() {
- await page.goto(URL_CLAIM, { waitUntil: 'domcontentloaded' }); // default 'load' takes forever
-
- const signInLocator = page
- .getByRole('link', {
- name: 'Sign in to your account',
- })
- .first();
- const usernameLocator = page
- .getByRole('button', {
- name: 'Account manager for',
- })
- .first();
-
- await Promise.any([signInLocator.waitFor(), usernameLocator.waitFor()]);
-
- if (await usernameLocator.isVisible()) {
- return; // logged in using saved cookie
- } else if (await signInLocator.isVisible()) {
- console.error('Not signed in anymore.');
- await signInLocator.click();
- await signInToXbox();
- } else {
- console.error('lost! where am i?');
- }
-}
-
-async function signInToXbox() {
- page.waitForLoadState('domcontentloaded');
- 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!`);
-
- // ### FETCH EMAIL/PASS
- if (cfg.xbox_email && cfg.xbox_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.xbox_email || await prompt({ message: 'Enter email' });
- const password =
- email &&
- (cfg.xbox_password ||
- await prompt({
- type: 'password',
- message: 'Enter password',
- }));
- // ### FILL IN EMAIL/PASS
- if (email && password) {
- const usernameLocator = page
- .getByPlaceholder('Email, phone, or Skype')
- .first();
- const passwordLocator = page.getByPlaceholder('Password').first();
-
- await Promise.any([
- usernameLocator.waitFor(),
- passwordLocator.waitFor(),
- ]);
-
- // username may already be saved from before, if so, skip to filling in password
- if (await page.getByPlaceholder('Email, phone, or Skype').isVisible()) {
- await usernameLocator.fill(email);
- await page.getByRole('button', { name: 'Next' }).click();
- }
-
- await passwordLocator.fill(password);
- await page.getByRole('button', { name: 'Sign in' }).click();
-
- // handle MFA, but don't await it
- page.locator('input[name="otc"]')
- .waitFor()
- .then(async () => {
- console.log('Two-Step Verification - Enter security code');
- console.log(
- await page
- .locator('div[data-bind="text: description"]')
- .innerText(),
- );
- const otp =
- cfg.xbox_otpkey &&
- authenticator.generate(cfg.xbox_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.type('input[name="otc"]', otp.toString());
- await page
- .getByLabel('Don\'t ask me again on this device')
- .check(); // Trust this Browser
- await page.getByRole('button', { name: 'Verify' }).click();
- })
- .catch(_ => {});
-
- // Trust this browser, but don't await it
- page.getByLabel('Don\'t show this again')
- .waitFor()
- .then(async () => {
- await page.getByLabel('Don\'t show this again').check();
- await page.getByRole('button', { name: 'Yes' }).click();
- })
- .catch(_ => {});
- } else {
- console.log('Waiting for you to login in the browser.');
- await notify(
- 'xbox: no longer signed in and not enough options set for automatic login.',
- );
- if (cfg.headless) {
- console.log(
- 'Run `SHOW=1 node xbox` to login in the opened browser.',
- );
- await context.close();
- process.exit(1);
- }
- }
-
- // ### VERIFY SIGNED IN
- await page.waitForURL(`${URL_CLAIM}**`);
-
- if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
-}
-
-async function getAndSaveUser() {
- user = await page.locator('#mectrl_currentAccount_primary').innerHTML();
- console.log(`Signed in as '${user}'`);
- db.data[user] ||= {};
-}
-
-async function redeemFreeGames() {
- const monthlyGamesLocator = await page.locator('.f-size-large').all();
-
- const monthlyGamesPageLinks = await Promise.all(
- monthlyGamesLocator.map(
- async el => await el.locator('a').getAttribute('href'),
- ),
- );
- console.log('Free games:', monthlyGamesPageLinks);
-
- for (const url of monthlyGamesPageLinks) {
- await page.goto(url);
-
- const title = await page.locator('h1').first().innerText();
- 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);
- const notify_game = { title, url, status: 'failed' };
- notify_games.push(notify_game); // status is updated below
-
- // SELECTORS
- const getBtnLocator = page.getByText('GET', { exact: true }).first();
- const installToLocator = page
- .getByText('INSTALL TO', { exact: true })
- .first();
-
- await Promise.any([
- getBtnLocator.waitFor(),
- installToLocator.waitFor(),
- ]);
-
- if (await installToLocator.isVisible()) {
- console.log(' Already in library! Nothing to claim.');
- notify_game.status = 'existed';
- db.data[user][game_id].status ||= 'existed'; // does not overwrite claimed or failed
- } else if (await getBtnLocator.isVisible()) {
- console.log(' Not in library yet! Click GET.');
- await getBtnLocator.click();
-
- // wait for popup
- await page
- .locator('iframe[name="purchase-sdk-hosted-iframe"]')
- .waitFor();
- const popupLocator = page.frameLocator(
- '[name=purchase-sdk-hosted-iframe]',
- );
-
- const finalGetBtnLocator = popupLocator.getByText('GET');
- await finalGetBtnLocator.waitFor();
- await finalGetBtnLocator.click();
-
- await page.getByText('Thank you for your purchase.').waitFor();
- notify_game.status = 'claimed';
- db.data[user][game_id].status = 'claimed';
- db.data[user][game_id].time = datetime(); // claimed time overwrites failed/dryrun time
- console.log(' Claimed successfully!');
- }
-
- // notify_game.status = db.data[user][game_id].status; // claimed or failed
-
- // const p = path.resolve(cfg.dir.screenshots, playstation-plus', `${game_id}.png`);
- // if (!existsSync(p)) await page.screenshot({ path: p, fullPage: false }); // fullPage is quite long...
- }
-}
From e154b74e4d3a927f230d39fd63b374f92f37f7ef Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 27 Jun 2024 14:09:37 +0200
Subject: [PATCH 188/253] eg: TODO handle base game for add-ons via function
---
epic-games.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/epic-games.js b/epic-games.js
index 70807b8..9aeb6ab 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -193,6 +193,7 @@ try {
const baseUrl = 'https://store.epicgames.com' + await page.locator('a:has-text("Overview")').getAttribute('href');
console.log(' Base game:', baseUrl);
// await page.click('a:has-text("Overview")');
+ // TODO handle this via function call for base game above since this will never terminate if DRYRUN=1
urls.push(baseUrl); // add base game to the list of games to claim
urls.push(url); // add add-on itself again
} else { // GET
From 00275d825baa6f7ddc04f74b091b4639e7948212 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 27 Jun 2024 15:40:45 +0200
Subject: [PATCH 189/253] eg: forgot to catch timeout for captcha detection
---
epic-games.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/epic-games.js b/epic-games.js
index 9aeb6ab..04a091e 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -263,7 +263,7 @@ try {
iframe.locator('.payment__errors:has-text("Failed to challenge captcha, please try again later.")').waitFor().then(async () => {
console.error(' Failed to challenge captcha, please try again later.');
await notify('epic-games: failed to challenge captcha. Please check.');
- });
+ }).catch(_ => { });
await page.locator('text=Thanks for your order!').waitFor({ state: 'attached' }); // TODO Bundle: got stuck here
db.data[user][game_id].status = 'claimed';
db.data[user][game_id].time = datetime(); // claimed time overwrites failed/dryrun time
From aee72327eaf06600ae43c80799188b58d9074ddf Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 27 Jun 2024 15:41:58 +0200
Subject: [PATCH 190/253] eg: disable webgl since it leaks running virtualized,
#183
---
epic-games.js | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/epic-games.js b/epic-games.js
index 04a091e..d0b9a03 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -1,7 +1,7 @@
import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdated playwright-extra
import { authenticator } from 'otplib';
import path from 'path';
-import { existsSync, writeFileSync } from 'fs';
+import { existsSync, writeFileSync, appendFileSync } from 'fs';
import { resolve, jsonDb, datetime, stealth, filenamify, prompt, notify, html_game_list, handleSIGINT } from './src/util.js';
import { cfg } from './src/config.js';
@@ -16,6 +16,14 @@ const db = await jsonDb('epic-games.json', {});
if (cfg.time) console.time('startup');
+const browserPrefs = path.join(cfg.dir.browser, 'prefs.js');
+if (existsSync(browserPrefs)) {
+ console.log('Adding webgl.disabled to', browserPrefs);
+ appendFileSync(browserPrefs, 'user_pref("webgl.disabled", true);'); // apparently Firefox removes duplicates (and sorts), so no problem appending every time
+} else {
+ console.log(browserPrefs, 'does not exist yet, will patch it on next run. Restart the script if you get a captcha.');
+}
+
// https://playwright.dev/docs/auth#multi-factor-authentication
const context = await firefox.launchPersistentContext(cfg.dir.browser, {
headless: cfg.headless,
From 17f1ee41c975e1dc9d8a7e5abef254a17bf07349 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 27 Jun 2024 23:32:15 +0200
Subject: [PATCH 191/253] eg: don't exit on login error since it may be
'Incorrect response' for captcha
https://github.com/vogler/free-games-claimer/issues/183#issuecomment-2195691372
---
epic-games.js | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index d0b9a03..9d492a0 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -103,7 +103,7 @@ try {
await notify('epic-games: got captcha during login. Please check.');
}).catch(_ => { });
page.waitForSelector('p:has-text("Incorrect response.")').then(async () => {
- console.error('Incorrect repsonse for captcha!');
+ console.error('Incorrect response for captcha!');
}).catch(_ => { });
await page.fill('#email', email);
// await page.click('button[type="submit"]'); login was split in two steps for some time, now email and password are on the same form again
@@ -116,8 +116,7 @@ try {
const error = page.locator('#form-error-message');
error.waitFor().then(async () => {
console.error('Login error:', await error.innerText());
- await context.close(); // finishes potential recording
- process.exit(1);
+ console.log('Please login in the browser!');
}).catch(_ => { });
// handle MFA, but don't await it
page.waitForURL('**/id/login/mfa**').then(async () => {
From b8f7068a873d47ccc3c657c2c82aa1bc718cc455 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 10 Jul 2024 13:27:48 +0200
Subject: [PATCH 192/253] pg: scrollUntilStable to wait in loop to load all
games since one scroll may not be enough
---
prime-gaming.js | 34 ++++++++++++++++++----------------
1 file changed, 18 insertions(+), 16 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 5e3e283..eeface0 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -97,12 +97,25 @@ try {
process.exit(1);
}
+ const waitUntilStable = async (f, act) => {
+ let v;
+ while (true) {
+ const v2 = await f();
+ if (v == v2) break;
+ v = v2;
+ await act();
+ }
+ };
+ const scrollUntilStable = async f => waitUntilStable(f, async () => {
+ await page.keyboard.press('End'); // scroll to bottom to show all games
+ await page.waitForLoadState('networkidle'); // wait for all games to be loaded
+ await page.waitForTimeout(2000); // TODO networkidle wasn't enough to load all already collected games
+ });
+
await page.click('button[data-type="Game"]');
- await page.keyboard.press('End'); // scroll to bottom to show all games
- await page.waitForLoadState('networkidle'); // wait for all games to be loaded
- await page.waitForTimeout(2000); // TODO networkidle wasn't enough to load all already collected games
const games = page.locator('div[data-a-target="offer-list-FGWP_FULL"]');
await games.waitFor();
+ await scrollUntilStable(() => games.locator('.item-card__action').count());
console.log('Number of already claimed games (total):', await games.locator('p:has-text("Collected")').count());
// can't use .all() since the list of elements via locator will change after click while we iterate over it
const internal = await games.locator('.item-card__action:has([data-a-target="FGWPOffer"])').elementHandles();
@@ -324,8 +337,7 @@ try {
if (notify_games.length) { // make screenshot of all games if something was claimed
const p = screenshot(`${filenamify(datetime())}.png`);
// await page.screenshot({ path: p, fullPage: true }); // fullPage does not make a difference since scroll not on body but on some element
- await page.keyboard.press('End'); // scroll to bottom to show all games
- await page.waitForTimeout(1000); // wait for fade in animation
+ await scrollUntilStable(() => games.locator('.item-card__action').count());
const viewportSize = page.viewportSize(); // current viewport size
await page.setViewportSize({ ...viewportSize, height: 3000 }); // increase height, otherwise element screenshot is cut off at the top and bottom
await games.screenshot({ path: p }); // screenshot of all claimed games
@@ -339,17 +351,7 @@ try {
await loot.waitFor();
process.stdout.write('Loading all DLCs on page...');
- let n1 = 0;
- let n2 = 0;
- do {
- n1 = n2;
- n2 = await loot.locator('[data-a-target="item-card"]').count();
- // console.log(n2);
- process.stdout.write(` ${n2}`);
- await page.keyboard.press('End'); // scroll to bottom to show all dlcs
- await page.waitForLoadState('networkidle'); // did not wait for dlcs to be loaded
- await page.waitForTimeout(1000);
- } while (n2 > n1);
+ scrollUntilStable(() => loot.locator('[data-a-target="item-card"]').count())
console.log('\nNumber of already claimed DLC:', await loot.locator('p:has-text("Collected")').count());
From a39d737999c509b9065ee07dd7a806e6579ff5e6 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Fri, 19 Jul 2024 14:35:40 +0200
Subject: [PATCH 193/253] aliexpress: collect daily coins via desktop website
All load in webview in android app.
Other games have no desktop version and mobile version uses canvas
and only refers to download app with both stealth and fingerprint-injector.
Also stuck on loading screen when using firefox instead of chrome.
---
aliexpress.js | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/config.js | 3 ++
2 files changed, 108 insertions(+)
create mode 100644 aliexpress.js
diff --git a/aliexpress.js b/aliexpress.js
new file mode 100644
index 0000000..c44c673
--- /dev/null
+++ b/aliexpress.js
@@ -0,0 +1,105 @@
+import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdated playwright-extra
+import { datetime, filenamify, prompt, handleSIGINT, stealth } from './src/util.js';
+import { cfg } from './src/config.js';
+
+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
+ 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-${filenamify(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
+});
+handleSIGINT(context);
+await stealth(context);
+
+context.setDefaultTimeout(cfg.debug ? 0 : cfg.timeout);
+
+const page = context.pages().length ? context.pages()[0] : await context.newPage(); // should always exist
+
+const auth = async (url) => {
+ console.log('auth', url);
+ await page.goto(url, { waitUntil: 'domcontentloaded' });
+ // redirects to https://login.aliexpress.com/?return_url=https%3A%2F%2Fwww.aliexpress.com%2Fp%2Fcoin-pc-index%2Findex.html
+ await Promise.any([page.waitForURL(/.*login.aliexpress.com.*/).then(async () => {
+ // manual login
+ console.error('Not logged in! Will wait for 120s for you to login...');
+ // await page.waitForTimeout(120*1000);
+ // or try automated
+ page.locator('span:has-text("Switch account")').click().catch(_ => {}); // sometimes no longer logged in, but previous user/email is pre-selected -> in this case we want to go back to the classic login
+ const login = page.locator('.login-container');
+ const email = cfg.ae_email || await prompt({ message: 'Enter email' });
+ const emailInput = login.locator('input[label="Email or phone number"]');
+ await emailInput.fill(email);
+ await emailInput.blur(); // otherwise Continue button stays disabled
+ const continueButton = login.locator('button:has-text("Continue")');
+ await continueButton.click({ force: true }); // normal click waits for button to no longer be covered by their suggestion menu, so we have to force click somewhere for the menu to close and then click
+ await continueButton.click();
+ const password = email && (cfg.ae_password || await prompt({ type: 'password', message: 'Enter password' }));
+ await login.locator('input[label="Password"]').fill(password);
+ await login.locator('button:has-text("Sign in")').click();
+ const error = login.locator('.error-text');
+ error.waitFor().then(async _ => console.error('Login error:', await error.innerText()));
+ await page.waitForURL(url);
+ // await page.addLocatorHandler(page.getByRole('button', { name: 'Accept cookies' }), btn => btn.click());
+ page.getByRole('button', { name: 'Accept cookies' }).click().then(_ => console.log('Accepted cookies')).catch(_ => { });
+ }), page.locator('#nav-user-account').waitFor()]).catch(_ => {});
+
+ // await page.locator('#nav-user-account').hover();
+ // console.log('Logged in as:', await page.locator('.welcome-name').innerText());
+};
+
+// copied URLs from AliExpress app on tablet which has menu for the used webview
+const urls = {
+ // works with desktop view, but stuck at 100% loading in mobile view:
+ coins: 'https://www.aliexpress.com/p/coin-pc-index/index.html',
+ // only work with mobile view:
+ grow: 'https://m.aliexpress.com/p/ae_fruit/index.html', // firefox: stuck at 60% loading, chrome: loads, but canvas
+ gogo: 'https://m.aliexpress.com/p/gogo-match-cc/index.html', // closes firefox?!
+ // only show notification to install the app
+ euro: 'https://m.aliexpress.com/p/european-cup/index.html', // doesn't load
+ merge: 'https://m.aliexpress.com/p/merge-market/index.html',
+};
+
+const coins = async () => {
+ // await auth(urls.coins);
+ await Promise.any([page.locator('.checkin-button').click(), page.locator('.addcoin').waitFor()]);
+ console.log('Coins:', await page.locator('.mycoin-content-right-money').innerText());
+ console.log('Streak:', await page.locator('.title-box').innerText());
+ console.log('Tomorrow:', await page.locator('.addcoin').innerText());
+};
+
+const grow = async () => {
+ await page.pause();
+};
+
+const gogo = async () => {
+ await page.pause();
+};
+
+const euro = async () => {
+ await page.pause();
+};
+
+const merge = async () => {
+ await page.pause();
+};
+
+try {
+ // await coins();
+ await [
+ coins,
+ // grow,
+ // gogo,
+ // euro,
+ // merge,
+ ].reduce((a, f) => a.then(async _ => { await auth(urls[f.name]); await f(); console.log() }), Promise.resolve());
+
+ // await page.pause();
+} catch (error) {
+ process.exitCode ||= 1;
+ console.error('--- Exception:');
+ console.error(error); // .toString()?
+}
+if (page.video()) console.log('Recorded video:', await page.video().path());
+await context.close();
diff --git a/src/config.js b/src/config.js
index 03414a0..4a384b8 100644
--- a/src/config.js
+++ b/src/config.js
@@ -41,6 +41,9 @@ export const cfg = {
gog_email: process.env.GOG_EMAIL || process.env.EMAIL,
gog_password: process.env.GOG_PASSWORD || process.env.PASSWORD,
gog_newsletter: process.env.GOG_NEWSLETTER == '1', // do not unsubscribe from newsletter after claiming a game
+ // auth AliExpress
+ ae_email: process.env.AE_EMAIL || process.env.EMAIL,
+ ae_password: process.env.AE_PASSWORD || process.env.PASSWORD,
// OTP only via GOG_EMAIL, can't add app...
// experimmental
pg_redeem: process.env.PG_REDEEM == '1', // prime-gaming: redeem keys on external stores
From d9e91d22c9f01114f905b8edf6e7322bb56639be Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 1 Aug 2024 15:46:41 +0200
Subject: [PATCH 194/253] eg: fix changed button locators
---
epic-games.js | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index 9d492a0..ff8f3e0 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -156,7 +156,8 @@ try {
for (const url of urls) {
if (cfg.time) console.time('claim 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
+ const purcahseBtn = page.locator('aside button').first();
+ const btnText = (await purcahseBtn.innerText()).toLowerCase(); // barrier to block until page is loaded
// click Continue if 'This game contains mature content recommended only for ages 18+'
if (await page.locator('button:has-text("Continue")').count() > 0) {
@@ -187,12 +188,12 @@ try {
const notify_game = { title, url, status: 'failed' };
notify_games.push(notify_game); // status is updated below
- if (btnText.toLowerCase() == 'in library') {
+ if (btnText == 'in library') {
console.log(' Already in library! Nothing to claim.');
notify_game.status = 'existed';
db.data[user][game_id].status ||= 'existed'; // does not overwrite claimed or failed
if (db.data[user][game_id].status.startsWith('failed')) db.data[user][game_id].status = 'manual'; // was failed but now it's claimed
- } else if (btnText.toLowerCase() == 'requires base game') {
+ } else if (btnText == 'requires base game') {
console.log(' Requires base game! Nothing to claim.');
notify_game.status = 'requires base game';
db.data[user][game_id].status ||= 'failed:requires-base-game';
@@ -205,7 +206,7 @@ try {
urls.push(url); // add add-on itself again
} 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
+ await purcahseBtn.click({ delay: 11 }); // got stuck here without delay (or mouse move), see #75, 1ms was also enough
// 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?
@@ -252,7 +253,7 @@ try {
await iframe.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 = iframe.locator('button:has-text("I Accept")');
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?
From 8605b7037707addbd98643bb3e454fedb1951917 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Fri, 2 Aug 2024 12:57:37 +0200
Subject: [PATCH 195/253] pg: fix internal/external locators, closes #355,
waitUntilStable 2s -> 5s
---
prime-gaming.js | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index eeface0..b902712 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -101,6 +101,7 @@ try {
let v;
while (true) {
const v2 = await f();
+ console.log('waitUntilStable', v2);
if (v == v2) break;
v = v2;
await act();
@@ -109,7 +110,7 @@ try {
const scrollUntilStable = async f => waitUntilStable(f, async () => {
await page.keyboard.press('End'); // scroll to bottom to show all games
await page.waitForLoadState('networkidle'); // wait for all games to be loaded
- await page.waitForTimeout(2000); // TODO networkidle wasn't enough to load all already collected games
+ await page.waitForTimeout(5000); // TODO networkidle wasn't enough to load all already collected games
});
await page.click('button[data-type="Game"]');
@@ -118,8 +119,8 @@ try {
await scrollUntilStable(() => games.locator('.item-card__action').count());
console.log('Number of already claimed games (total):', await games.locator('p:has-text("Collected")').count());
// can't use .all() since the list of elements via locator will change after click while we iterate over it
- const internal = await games.locator('.item-card__action:has([data-a-target="FGWPOffer"])').elementHandles();
- const external = await games.locator('.item-card__action:has([data-a-target="ExternalOfferClaim"])').all();
+ const internal = await games.locator('.item-card__action:has(button[data-a-target="FGWPOffer"])').elementHandles();
+ const external = await games.locator('.item-card__action:has(a[data-a-target="FGWPOffer"])').all();
// bottom to top: oldest to newest games
internal.reverse();
external.reverse();
From eac11e8949c398c870b63d0e3d0d56f6c74e0692 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Fri, 2 Aug 2024 13:06:28 +0200
Subject: [PATCH 196/253] ci/docker: force tag main as latest since many people
pull it
---
.github/workflows/docker.yml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index a54117c..34c0285 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -34,6 +34,9 @@ jobs:
else
echo "IMAGE_TAG=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV
fi
+ # TODO the above didn't tag main as latest... probably not available in env.BRANCH right away
+ echo "${{ env.BRANCH }}"
+ echo "IMAGE_TAG=latest" >> $GITHUB_ENV
-
name: Set up QEMU
uses: docker/setup-qemu-action@v3
From 23a611a2f11569ed20c189d0d51d45ffa93de403 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Fri, 2 Aug 2024 13:16:54 +0200
Subject: [PATCH 197/253] ci/docker: fix: tag main as latest
---
.github/workflows/docker.yml | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 34c0285..5e5adba 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -27,16 +27,14 @@ jobs:
-
name: Set environment variables
run: |
- echo "BRANCH=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV
+ BRANCH="${GITHUB_REF#refs/heads/}"
+ echo "BRANCH=$BRANCH" >> $GITHUB_ENV
echo "NOW=$(date -R)" >> $GITHUB_ENV # date -Iseconds; date +'%Y-%m-%dT%H:%M:%S'
- if [[ "${{ env.BRANCH }}" == "main" ]]; then
+ if [[ "$BRANCH" == "main" ]]; then
echo "IMAGE_TAG=latest" >> $GITHUB_ENV
else
- echo "IMAGE_TAG=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV
+ echo "IMAGE_TAG=$BRANCH" >> $GITHUB_ENV
fi
- # TODO the above didn't tag main as latest... probably not available in env.BRANCH right away
- echo "${{ env.BRANCH }}"
- echo "IMAGE_TAG=latest" >> $GITHUB_ENV
-
name: Set up QEMU
uses: docker/setup-qemu-action@v3
From c87deb393eec26b78d7a30bded8859c183ebb278 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Fri, 2 Aug 2024 14:02:33 +0200
Subject: [PATCH 198/253] pg: DLC: forgot to await scrollUntilStable, closes
#356
---
prime-gaming.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index b902712..88ae542 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -352,7 +352,7 @@ try {
await loot.waitFor();
process.stdout.write('Loading all DLCs on page...');
- 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());
From eafb2316f71d0cc548c4263be74c1818114ffc5b Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 28 Aug 2024 02:31:47 +0200
Subject: [PATCH 199/253] pg: login now has Continue button after email
---
prime-gaming.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/prime-gaming.js b/prime-gaming.js
index 88ae542..7d0d0d2 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -53,6 +53,7 @@ try {
const password = email && (cfg.pg_password || await prompt({ type: 'password', message: 'Enter password' }));
if (email && password) {
await page.fill('[name=email]', email);
+ await page.click('input[type="submit"]');
await page.fill('[name=password]', password);
await page.check('[name=rememberMe]');
await page.click('input[type="submit"]');
From c8e06404906d44395a00fdbdfecd208f2c052a38 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 29 Aug 2024 12:46:28 +0200
Subject: [PATCH 200/253] eg: #371: dump HTML in case of EULA popup to find new
locator
https://github.com/vogler/free-games-claimer/issues/371
---
epic-games.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/epic-games.js b/epic-games.js
index ff8f3e0..fbf5732 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -215,8 +215,10 @@ try {
page.click('button:has-text("Yes, buy now")').catch(_ => { });
// Accept End User License Agreement (only needed once)
- page.locator('input#agree').waitFor().then(async () => {
+ page.locator(':has-text("end user license agreement")').waitFor().then(async () => {
console.log(' Accept End User License Agreement (only needed once)');
+ console.log(page.innerHTML);
+ console.log('Please report the HTML above here: https://github.com/vogler/free-games-claimer/issues/371');
await page.locator('input#agree').check(); // TODO Bundle: got stuck here
await page.locator('button:has-text("Accept")').click();
}).catch(_ => { });
From 1f4af79e0eff56dc42bd3a8611c530a0ec606aaf Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 29 Aug 2024 13:23:45 +0200
Subject: [PATCH 201/253] pg: fix loading all games by 2*PageDown + checking
height instead of End + checking count..., #357
---
prime-gaming.js | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 7d0d0d2..adfdbd4 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -108,16 +108,22 @@ try {
await act();
}
};
- const scrollUntilStable = async f => waitUntilStable(f, async () => {
- await page.keyboard.press('End'); // scroll to bottom to show all games
+ const scrollUntilStable = async f => await waitUntilStable(f, async () => {
+ // await page.keyboard.press('End'); // scroll to bottom to show all games
+ // loading all games became flaky; see https://github.com/vogler/free-games-claimer/issues/357
+ await page.keyboard.press('PageDown'); // scrolling to straight to the bottom started to skip loading some games
await page.waitForLoadState('networkidle'); // wait for all games to be loaded
- await page.waitForTimeout(5000); // TODO networkidle wasn't enough to load all already collected games
+ await page.waitForTimeout(3000); // TODO networkidle wasn't enough to load all already collected games
+ // do it again since once wasn't enough...
+ await page.keyboard.press('PageDown');
+ await page.waitForTimeout(3000);
});
await page.click('button[data-type="Game"]');
const games = page.locator('div[data-a-target="offer-list-FGWP_FULL"]');
await games.waitFor();
- await scrollUntilStable(() => games.locator('.item-card__action').count());
+ // await scrollUntilStable(() => games.locator('.item-card__action').count()); // number of games
+ await scrollUntilStable(() => page.evaluate(() => document.querySelector('.tw-full-width').scrollHeight)); // height may change during loading while number of games is still the same?
console.log('Number of already claimed games (total):', await games.locator('p:has-text("Collected")').count());
// can't use .all() since the list of elements via locator will change after click while we iterate over it
const internal = await games.locator('.item-card__action:has(button[data-a-target="FGWPOffer"])').elementHandles();
From 7279ba06e8b50b3cbafbbb51d440b8aaff6bddd7 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 29 Aug 2024 19:48:28 +0200
Subject: [PATCH 202/253] eg: change back to purchase-cta-button from 'aside
button', fixes #374
---
epic-games.js | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index fbf5732..f15d393 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -156,8 +156,8 @@ try {
for (const url of urls) {
if (cfg.time) console.time('claim game');
await page.goto(url); // , { waitUntil: 'domcontentloaded' });
- const purcahseBtn = page.locator('aside button').first();
- const btnText = (await purcahseBtn.innerText()).toLowerCase(); // barrier to block until page is loaded
+ const purchaseBtn = page.locator('button[data-testid="purchase-cta-button"]').first();
+ const btnText = (await purchaseBtn.innerText()).toLowerCase(); // barrier to block until page is loaded
// click Continue if 'This game contains mature content recommended only for ages 18+'
if (await page.locator('button:has-text("Continue")').count() > 0) {
@@ -179,6 +179,7 @@ try {
if (await page.locator('span:text-is("About Bundle")').count()) {
// console.log(' This is a bundle containing: TODO');
title = (await page.locator('span:has-text("Buy"):left-of([data-testid="purchase-cta-button"])').first().innerText()).replace('Buy ', '');
+ // h1 first didn't exist for bundles but now it does... However h1 would e.g. be 'Fallout® Classic Collection' instead of 'Fallout Classic Collection'
} else {
title = await page.locator('h1').first().innerText();
}
@@ -206,7 +207,7 @@ try {
urls.push(url); // add add-on itself again
} else { // GET
console.log(' Not in library yet! Click GET.');
- await purcahseBtn.click({ delay: 11 }); // got stuck here without delay (or mouse move), see #75, 1ms was also enough
+ await purchaseBtn.click({ delay: 11 }); // got stuck here without delay (or mouse move), see #75, 1ms was also enough
// 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?
From 292aadae3c5f4b7ec084dec28129e26312d8a48a Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 29 Aug 2024 20:06:59 +0200
Subject: [PATCH 203/253] eg: list games included in a bundle
---
epic-games.js | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index f15d393..155e9ee 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -176,16 +176,22 @@ try {
}
let title;
+ let bundle_includes;
if (await page.locator('span:text-is("About Bundle")').count()) {
- // console.log(' This is a bundle containing: TODO');
title = (await page.locator('span:has-text("Buy"):left-of([data-testid="purchase-cta-button"])').first().innerText()).replace('Buy ', '');
// h1 first didn't exist for bundles but now it does... However h1 would e.g. be 'Fallout® Classic Collection' instead of 'Fallout Classic Collection'
+ try {
+ bundle_includes = await Promise.all((await page.locator('.product-card-top-row h5').all()).map(b => b.innerText()));
+ } catch (e) {
+ console.error('Failed to get "Bundle Includes":', e);
+ }
} else {
title = await page.locator('h1').first().innerText();
}
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);
+ if (bundle_includes) console.log(' This bundle includes:', bundle_includes);
const notify_game = { title, url, status: 'failed' };
notify_games.push(notify_game); // status is updated below
@@ -220,7 +226,7 @@ try {
console.log(' Accept End User License Agreement (only needed once)');
console.log(page.innerHTML);
console.log('Please report the HTML above here: https://github.com/vogler/free-games-claimer/issues/371');
- await page.locator('input#agree').check(); // TODO Bundle: got stuck here
+ await page.locator('input#agree').check(); // TODO Bundle: got stuck here; likely unrelated to bundle and locator just changed: https://github.com/vogler/free-games-claimer/issues/371
await page.locator('button:has-text("Accept")').click();
}).catch(_ => { });
From fef5b97e3b70d3387233e612d8ed6709437d9cd4 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 29 Aug 2024 20:23:43 +0200
Subject: [PATCH 204/253] eg: wait for purchaseBtn, without it didn't detect
bundle as already claimed
---
epic-games.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index 155e9ee..d8784f8 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -157,6 +157,7 @@ try {
if (cfg.time) console.time('claim game');
await page.goto(url); // , { waitUntil: 'domcontentloaded' });
const purchaseBtn = page.locator('button[data-testid="purchase-cta-button"]').first();
+ await purchaseBtn.waitFor();
const btnText = (await purchaseBtn.innerText()).toLowerCase(); // barrier to block until page is loaded
// click Continue if 'This game contains mature content recommended only for ages 18+'
@@ -212,7 +213,7 @@ try {
urls.push(baseUrl); // add base game to the list of games to claim
urls.push(url); // add add-on itself again
} else { // GET
- console.log(' Not in library yet! Click GET.');
+ console.log(' Not in library yet! Click', btnText);
await purchaseBtn.click({ delay: 11 }); // got stuck here without delay (or mouse move), see #75, 1ms was also enough
// click Continue if 'Device not supported. This product is not compatible with your current device.' - avoided by Windows userAgent?
@@ -281,7 +282,7 @@ try {
console.error(' Failed to challenge captcha, please try again later.');
await notify('epic-games: failed to challenge captcha. Please check.');
}).catch(_ => { });
- await page.locator('text=Thanks for your order!').waitFor({ state: 'attached' }); // TODO Bundle: got stuck here
+ await page.locator('text=Thanks for your order!').waitFor({ state: 'attached' }); // TODO Bundle: got stuck here, but normal game now as well
db.data[user][game_id].status = 'claimed';
db.data[user][game_id].time = datetime(); // claimed time overwrites failed/dryrun time
console.log(' Claimed successfully!');
From c8cf7362fa2ff92386eee4dc847784d95d7f5195 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Sun, 1 Sep 2024 01:20:45 +0200
Subject: [PATCH 205/253] eg: wait for purchaseBtn to have some text, fixes
#375
---
epic-games.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/epic-games.js b/epic-games.js
index d8784f8..f853380 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -156,7 +156,7 @@ try {
for (const url of urls) {
if (cfg.time) console.time('claim game');
await page.goto(url); // , { waitUntil: 'domcontentloaded' });
- const purchaseBtn = page.locator('button[data-testid="purchase-cta-button"]').first();
+ const purchaseBtn = page.locator('button[data-testid="purchase-cta-button"] >> :has-text("e"), :has-text("i")').first(); // when loading, the button text is empty -> need to wait for some text {'get', 'in library', 'requires base game'} -> just wait for e or i to not be too specific; :text-matches("\w+") somehow didn't work - https://github.com/vogler/free-games-claimer/issues/375
await purchaseBtn.waitFor();
const btnText = (await purchaseBtn.innerText()).toLowerCase(); // barrier to block until page is loaded
From c5a7a10ca35de6cc54cd16c424ef81af8609a30d Mon Sep 17 00:00:00 2001
From: Samuel Rounce
Date: Sat, 28 Sep 2024 18:34:39 +0100
Subject: [PATCH 206/253] fix: docker-entrypoint.sh uses safe shebang
Shebangs should use /usr/bin/env to locate the interpreter. Setting a path directly to the interpreter itself tends to be brittle and prone to breaking.
---
docker-entrypoint.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
index 679acc6..5837164 100755
--- a/docker-entrypoint.sh
+++ b/docker-entrypoint.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
set -eo pipefail # exit on error, error on any fail in pipe (not just last cmd); add -x to print each cmd; see gist bash_strict_mode.md
From d5072a62c090ab637af98da524f0a37268f25638 Mon Sep 17 00:00:00 2001
From: AgentTechnoman <46273498+AgentTechnoman@users.noreply.github.com>
Date: Thu, 19 Dec 2024 16:46:18 -0700
Subject: [PATCH 207/253] Fix gog error per @jordyamc
https://github.com/vogler/free-games-claimer/issues/398#issuecomment-2487274414
---
gog.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/gog.js b/gog.js
index 0688626..c4d5078 100644
--- a/gog.js
+++ b/gog.js
@@ -99,7 +99,8 @@ try {
console.log('Currently no free giveaway!');
} else {
const text = await page.locator('.giveaway__content-header').innerText();
- const title = text.match(/Claim (.*) and don't miss the/)[1];
+ const match_all = text.match(/Claim (.*) and don't miss the|Success! (.*) was added to/);
+ const title = match_all[1] ? match_all[1] : match_all[2];
const url = await banner.locator('a').first().getAttribute('href');
console.log(`Current free game: ${title} - ${url}`);
db.data[user][title] ||= { title, time: datetime(), url };
From b3ab8f7830ba6a2f6785812b086134798c55b4da Mon Sep 17 00:00:00 2001
From: NeoMod
Date: Wed, 1 Jan 2025 15:31:04 +0100
Subject: [PATCH 208/253] Fixed missing game link in notification for
epic-games on captcha halt
This fixes the "game link" missing from notification when checking Epic Games Store and encountering a captcha, as per #259 should have been but instead wasn't. Issue was identified in #402 comment by "vttc08" (https://github.com/vogler/free-games-claimer/issues/402#issuecomment-2510818082)
I also added the missing notification for "Game Already in Library" for Epic Games Store, since I felt it is useful because it provides an easy way to know if the script is working or not during scheduled usage.
This also provided the opportunity to check if the game url link was handled properly outside "Discord": I have added a commented option where one could activate the "Already in Library" notification that will also provide the game url link for easy verification.
---
epic-games.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/epic-games.js b/epic-games.js
index f853380..5ac6839 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -198,6 +198,8 @@ try {
if (btnText == 'in library') {
console.log(' Already in library! Nothing to claim.');
+ //await notify(`Game Already in Library! Follow is a test link:.\n Game link ${url}`); // Decomment this line if you want to also test if a link is beeing sent correctly in a notification. The "already in library" notification was missing, I find it usefull.
+ await notify(`Game Already in Library!`); // decomment the previous line and comment this one if you also want to test for proper link-handling via notification.
notify_game.status = 'existed';
db.data[user][game_id].status ||= 'existed'; // does not overwrite claimed or failed
if (db.data[user][game_id].status.startsWith('failed')) db.data[user][game_id].status = 'manual'; // was failed but now it's claimed
@@ -271,7 +273,7 @@ try {
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.');
- await notify(`epic-games: got captcha challenge right before claim of ${title}. Use VNC to solve it manually.`); // TODO could even create purchase URL, see https://github.com/vogler/free-games-claimer/pull/130
+ await notify(`epic-games: got captcha challenge.\n Game link ${url}`);// FIXED: Game link was not sent, probably error with html formatting? Anyway, the link is automatically parsed. TODO could even create purchase URL, see https://github.com/vogler/free-games-claimer/pull/130
// await page.waitForTimeout(2000);
// const p = path.resolve(cfg.dir.screenshots, 'epic-games', 'captcha', `${filenamify(datetime())}.png`);
// await captcha.screenshot({ path: p });
From 9e87ce58ac2899e58d771c6894e6d3f2a166d75d Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 5 Mar 2025 13:17:13 +0000
Subject: [PATCH 209/253] Add renovate.json
---
renovate.json | 6 ++++++
1 file changed, 6 insertions(+)
create mode 100644 renovate.json
diff --git a/renovate.json b/renovate.json
new file mode 100644
index 0000000..5db72dd
--- /dev/null
+++ b/renovate.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
+ "extends": [
+ "config:recommended"
+ ]
+}
From 4a018c20e930b1945872804af67265253d50790f Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 5 Mar 2025 13:22:40 +0000
Subject: [PATCH 210/253] fix(deps): update dependency dotenv to v16.4.7
---
package-lock.json | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index e6f66b2..30fd170 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -512,10 +512,9 @@
}
},
"node_modules/dotenv": {
- "version": "16.4.5",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
- "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
- "license": "BSD-2-Clause",
+ "version": "16.4.7",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
+ "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
"engines": {
"node": ">=12"
},
@@ -2052,9 +2051,9 @@
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
},
"dotenv": {
- "version": "16.4.5",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
- "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg=="
+ "version": "16.4.7",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
+ "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ=="
},
"enquirer": {
"version": "2.4.1",
From fed03428f3efff42ddd1fcc4a0adc2b25a405c6b Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 5 Mar 2025 15:07:37 +0100
Subject: [PATCH 211/253] try super-linter
https://github.com/marketplace/actions/super-linter#get-started
---
.github/workflows/lint.yml | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
create mode 100644 .github/workflows/lint.yml
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 0000000..792d1d4
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,33 @@
+---
+name: Lint
+
+on: # yamllint disable-line rule:truthy
+ push: null
+ pull_request: null
+
+permissions: {}
+
+jobs:
+ build:
+ name: Lint
+ runs-on: ubuntu-latest
+
+ permissions:
+ contents: read
+ packages: read
+ # To report GitHub Actions status checks
+ statuses: write
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ # super-linter needs the full git history to get the
+ # list of files that changed across commits
+ fetch-depth: 0
+
+ - name: Super-linter
+ uses: super-linter/super-linter@v7.3.0 # x-release-please-version
+ env:
+ # To report GitHub Actions status checks
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
From d76c03ed6aff7d2fe4eac4d44903e982aef9dcfc Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 5 Mar 2025 14:08:28 +0000
Subject: [PATCH 212/253] chore(deps): update dependency eslint to v9.21.0
---
package-lock.json | 534 ++++++++++++++++++++--------------------------
1 file changed, 226 insertions(+), 308 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index e6f66b2..1205edc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -51,35 +51,45 @@
}
},
"node_modules/@eslint-community/regexpp": {
- "version": "4.10.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
- "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
+ "version": "4.12.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+ "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
"dev": true,
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
"node_modules/@eslint/config-array": {
- "version": "0.16.0",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.16.0.tgz",
- "integrity": "sha512-/jmuSd74i4Czf1XXn7wGRWZCuyaUZ330NH1Bek0Pplatt4Sy1S5haN21SCLLdbeKslQ+S0wEJ+++v5YibSi+Lg==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz",
+ "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==",
"dev": true,
- "license": "Apache-2.0",
"dependencies": {
- "@eslint/object-schema": "^2.1.4",
+ "@eslint/object-schema": "^2.1.6",
"debug": "^4.3.1",
- "minimatch": "^3.0.5"
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz",
+ "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/eslintrc": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz",
- "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==",
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz",
+ "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==",
"dev": true,
- "license": "MIT",
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
@@ -99,25 +109,58 @@
}
},
"node_modules/@eslint/js": {
- "version": "9.5.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.5.0.tgz",
- "integrity": "sha512-A7+AOT2ICkodvtsWnxZP4Xxk3NbZ3VMHd8oihydLRGrJgqqdEz1qSeEgXYyT/Cu8h1TWWsQRejIx48mtjZ5y1w==",
+ "version": "9.21.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.21.0.tgz",
+ "integrity": "sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==",
"dev": true,
- "license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/object-schema": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz",
- "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==",
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
+ "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
"dev": true,
- "license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz",
+ "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==",
+ "dev": true,
+ "dependencies": {
+ "@eslint/core": "^0.12.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.6",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
+ "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
+ "dev": true,
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
"node_modules/@humanwhocodes/module-importer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -145,41 +188,6 @@
"url": "https://github.com/sponsors/nzakas"
}
},
- "node_modules/@nodelib/fs.scandir": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
- "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
- "dev": true,
- "dependencies": {
- "@nodelib/fs.stat": "2.0.5",
- "run-parallel": "^1.1.9"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.stat": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
- "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
- "dev": true,
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.walk": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
- "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
- "dev": true,
- "dependencies": {
- "@nodelib/fs.scandir": "2.1.5",
- "fastq": "^1.6.0"
- },
- "engines": {
- "node": ">= 8"
- }
- },
"node_modules/@otplib/core": {
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/@otplib/core/-/core-12.0.1.tgz",
@@ -274,11 +282,10 @@
}
},
"node_modules/@types/estree": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
- "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
- "dev": true,
- "license": "MIT"
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
+ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
+ "dev": true
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
@@ -293,11 +300,10 @@
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA=="
},
"node_modules/acorn": {
- "version": "8.11.3",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
- "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+ "version": "8.14.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
+ "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
"dev": true,
- "license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
@@ -469,9 +475,9 @@
}
},
"node_modules/cross-spawn": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
- "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -548,28 +554,31 @@
}
},
"node_modules/eslint": {
- "version": "9.5.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.5.0.tgz",
- "integrity": "sha512-+NAOZFrW/jFTS3dASCGBxX1pkFD0/fsO+hfAkJ4TyYKwgsXZbqzrw+seCYFCcPCYXvnD67tAnglU7GQTz6kcVw==",
+ "version": "9.21.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz",
+ "integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==",
"dev": true,
- "license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.6.1",
- "@eslint/config-array": "^0.16.0",
- "@eslint/eslintrc": "^3.1.0",
- "@eslint/js": "9.5.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.19.2",
+ "@eslint/core": "^0.12.0",
+ "@eslint/eslintrc": "^3.3.0",
+ "@eslint/js": "9.21.0",
+ "@eslint/plugin-kit": "^0.2.7",
+ "@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
- "@humanwhocodes/retry": "^0.3.0",
- "@nodelib/fs.walk": "^1.2.8",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "@types/json-schema": "^7.0.15",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
- "cross-spawn": "^7.0.2",
+ "cross-spawn": "^7.0.6",
"debug": "^4.3.2",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^8.0.1",
- "eslint-visitor-keys": "^4.0.0",
- "espree": "^10.0.1",
+ "eslint-scope": "^8.2.0",
+ "eslint-visitor-keys": "^4.2.0",
+ "espree": "^10.3.0",
"esquery": "^1.5.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
@@ -579,15 +588,11 @@
"ignore": "^5.2.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
- "is-path-inside": "^3.0.3",
"json-stable-stringify-without-jsonify": "^1.0.1",
- "levn": "^0.4.1",
"lodash.merge": "^4.6.2",
"minimatch": "^3.1.2",
"natural-compare": "^1.4.0",
- "optionator": "^0.9.3",
- "strip-ansi": "^6.0.1",
- "text-table": "^0.2.0"
+ "optionator": "^0.9.3"
},
"bin": {
"eslint": "bin/eslint.js"
@@ -597,14 +602,21 @@
},
"funding": {
"url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
}
},
"node_modules/eslint-scope": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz",
- "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==",
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz",
+ "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==",
"dev": true,
- "license": "BSD-2-Clause",
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
@@ -628,6 +640,19 @@
"url": "https://opencollective.com/eslint"
}
},
+ "node_modules/eslint/node_modules/@humanwhocodes/retry": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz",
+ "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
"node_modules/eslint/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -645,11 +670,10 @@
}
},
"node_modules/eslint/node_modules/eslint-visitor-keys": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
- "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
"dev": true,
- "license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
@@ -658,15 +682,14 @@
}
},
"node_modules/espree": {
- "version": "10.0.1",
- "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz",
- "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==",
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
+ "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
"dev": true,
- "license": "BSD-2-Clause",
"dependencies": {
- "acorn": "^8.11.3",
+ "acorn": "^8.14.0",
"acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^4.0.0"
+ "eslint-visitor-keys": "^4.2.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -676,11 +699,10 @@
}
},
"node_modules/espree/node_modules/eslint-visitor-keys": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
- "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
"dev": true,
- "license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
@@ -752,15 +774,6 @@
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true
},
- "node_modules/fastq": {
- "version": "1.15.0",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
- "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
- "dev": true,
- "dependencies": {
- "reusify": "^1.0.4"
- }
- },
"node_modules/file-entry-cache": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
@@ -990,15 +1003,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/is-path-inside": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
- "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/is-plain-object": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
@@ -1452,26 +1456,6 @@
}
}
},
- "node_modules/queue-microtask": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
- "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ]
- },
"node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@@ -1482,16 +1466,6 @@
"node": ">=4"
}
},
- "node_modules/reusify": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
- "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
- "dev": true,
- "engines": {
- "iojs": ">=1.0.0",
- "node": ">=0.10.0"
- }
- },
"node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@@ -1506,29 +1480,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/run-parallel": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
- "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "dependencies": {
- "queue-microtask": "^1.2.2"
- }
- },
"node_modules/shallow-clone": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz",
@@ -1629,12 +1580,6 @@
"node": ">=8"
}
},
- "node_modules/text-table": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
- "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
- "dev": true
- },
"node_modules/thirty-two": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz",
@@ -1722,26 +1667,35 @@
}
},
"@eslint-community/regexpp": {
- "version": "4.10.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
- "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
+ "version": "4.12.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+ "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
"dev": true
},
"@eslint/config-array": {
- "version": "0.16.0",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.16.0.tgz",
- "integrity": "sha512-/jmuSd74i4Czf1XXn7wGRWZCuyaUZ330NH1Bek0Pplatt4Sy1S5haN21SCLLdbeKslQ+S0wEJ+++v5YibSi+Lg==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz",
+ "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==",
"dev": true,
"requires": {
- "@eslint/object-schema": "^2.1.4",
+ "@eslint/object-schema": "^2.1.6",
"debug": "^4.3.1",
- "minimatch": "^3.0.5"
+ "minimatch": "^3.1.2"
+ }
+ },
+ "@eslint/core": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz",
+ "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.15"
}
},
"@eslint/eslintrc": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz",
- "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==",
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz",
+ "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==",
"dev": true,
"requires": {
"ajv": "^6.12.4",
@@ -1756,17 +1710,43 @@
}
},
"@eslint/js": {
- "version": "9.5.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.5.0.tgz",
- "integrity": "sha512-A7+AOT2ICkodvtsWnxZP4Xxk3NbZ3VMHd8oihydLRGrJgqqdEz1qSeEgXYyT/Cu8h1TWWsQRejIx48mtjZ5y1w==",
+ "version": "9.21.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.21.0.tgz",
+ "integrity": "sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==",
"dev": true
},
"@eslint/object-schema": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz",
- "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==",
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
+ "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
"dev": true
},
+ "@eslint/plugin-kit": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz",
+ "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==",
+ "dev": true,
+ "requires": {
+ "@eslint/core": "^0.12.0",
+ "levn": "^0.4.1"
+ }
+ },
+ "@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true
+ },
+ "@humanfs/node": {
+ "version": "0.16.6",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
+ "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
+ "dev": true,
+ "requires": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.3.0"
+ }
+ },
"@humanwhocodes/module-importer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -1779,32 +1759,6 @@
"integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==",
"dev": true
},
- "@nodelib/fs.scandir": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
- "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
- "dev": true,
- "requires": {
- "@nodelib/fs.stat": "2.0.5",
- "run-parallel": "^1.1.9"
- }
- },
- "@nodelib/fs.stat": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
- "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
- "dev": true
- },
- "@nodelib/fs.walk": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
- "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
- "dev": true,
- "requires": {
- "@nodelib/fs.scandir": "2.1.5",
- "fastq": "^1.6.0"
- }
- },
"@otplib/core": {
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/@otplib/core/-/core-12.0.1.tgz",
@@ -1886,9 +1840,9 @@
}
},
"@types/estree": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
- "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
+ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
"dev": true
},
"@types/json-schema": {
@@ -1903,9 +1857,9 @@
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA=="
},
"acorn": {
- "version": "8.11.3",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
- "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+ "version": "8.14.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
+ "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
"dev": true
},
"acorn-jsx": {
@@ -2023,9 +1977,9 @@
}
},
"cross-spawn": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
- "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"requires": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -2072,27 +2026,31 @@
"dev": true
},
"eslint": {
- "version": "9.5.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.5.0.tgz",
- "integrity": "sha512-+NAOZFrW/jFTS3dASCGBxX1pkFD0/fsO+hfAkJ4TyYKwgsXZbqzrw+seCYFCcPCYXvnD67tAnglU7GQTz6kcVw==",
+ "version": "9.21.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz",
+ "integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==",
"dev": true,
"requires": {
"@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.6.1",
- "@eslint/config-array": "^0.16.0",
- "@eslint/eslintrc": "^3.1.0",
- "@eslint/js": "9.5.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.19.2",
+ "@eslint/core": "^0.12.0",
+ "@eslint/eslintrc": "^3.3.0",
+ "@eslint/js": "9.21.0",
+ "@eslint/plugin-kit": "^0.2.7",
+ "@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
- "@humanwhocodes/retry": "^0.3.0",
- "@nodelib/fs.walk": "^1.2.8",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "@types/json-schema": "^7.0.15",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
- "cross-spawn": "^7.0.2",
+ "cross-spawn": "^7.0.6",
"debug": "^4.3.2",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^8.0.1",
- "eslint-visitor-keys": "^4.0.0",
- "espree": "^10.0.1",
+ "eslint-scope": "^8.2.0",
+ "eslint-visitor-keys": "^4.2.0",
+ "espree": "^10.3.0",
"esquery": "^1.5.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
@@ -2102,17 +2060,19 @@
"ignore": "^5.2.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
- "is-path-inside": "^3.0.3",
"json-stable-stringify-without-jsonify": "^1.0.1",
- "levn": "^0.4.1",
"lodash.merge": "^4.6.2",
"minimatch": "^3.1.2",
"natural-compare": "^1.4.0",
- "optionator": "^0.9.3",
- "strip-ansi": "^6.0.1",
- "text-table": "^0.2.0"
+ "optionator": "^0.9.3"
},
"dependencies": {
+ "@humanwhocodes/retry": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz",
+ "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==",
+ "dev": true
+ },
"chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -2124,17 +2084,17 @@
}
},
"eslint-visitor-keys": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
- "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
"dev": true
}
}
},
"eslint-scope": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz",
- "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==",
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz",
+ "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==",
"dev": true,
"requires": {
"esrecurse": "^4.3.0",
@@ -2148,20 +2108,20 @@
"dev": true
},
"espree": {
- "version": "10.0.1",
- "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz",
- "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==",
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
+ "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
"dev": true,
"requires": {
- "acorn": "^8.11.3",
+ "acorn": "^8.14.0",
"acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^4.0.0"
+ "eslint-visitor-keys": "^4.2.0"
},
"dependencies": {
"eslint-visitor-keys": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
- "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
"dev": true
}
}
@@ -2214,15 +2174,6 @@
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true
},
- "fastq": {
- "version": "1.15.0",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
- "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
- "dev": true,
- "requires": {
- "reusify": "^1.0.4"
- }
- },
"file-entry-cache": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
@@ -2386,12 +2337,6 @@
"is-extglob": "^2.1.1"
}
},
- "is-path-inside": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
- "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
- "dev": true
- },
"is-plain-object": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
@@ -2688,24 +2633,12 @@
"puppeteer-extra-plugin-user-data-dir": "^2.4.1"
}
},
- "queue-microtask": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
- "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
- "dev": true
- },
"resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true
},
- "reusify": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
- "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
- "dev": true
- },
"rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@@ -2714,15 +2647,6 @@
"glob": "^7.1.3"
}
},
- "run-parallel": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
- "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
- "dev": true,
- "requires": {
- "queue-microtask": "^1.2.2"
- }
- },
"shallow-clone": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz",
@@ -2790,12 +2714,6 @@
"has-flag": "^4.0.0"
}
},
- "text-table": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
- "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
- "dev": true
- },
"thirty-two": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz",
From 041605dcf70911c637da8d225146e30dad3acace Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 5 Mar 2025 14:08:35 +0000
Subject: [PATCH 213/253] fix(deps): update dependency chalk to v5.4.1
---
package-lock.json | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index e6f66b2..03b750e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -403,9 +403,9 @@
}
},
"node_modules/chalk": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
- "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+ "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
"engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0"
},
@@ -1978,9 +1978,9 @@
"dev": true
},
"chalk": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
- "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w=="
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+ "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="
},
"clone-deep": {
"version": "0.2.4",
From d6666aed034ef84bfdc2b3240d70df29d5b30ece Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 5 Mar 2025 15:17:34 +0100
Subject: [PATCH 214/253] super-linter: use slim version
---
.github/workflows/lint.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 792d1d4..951e26d 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -1,4 +1,4 @@
----
+# https://github.com/marketplace/actions/super-linter#get-started
name: Lint
on: # yamllint disable-line rule:truthy
@@ -27,7 +27,7 @@ jobs:
fetch-depth: 0
- name: Super-linter
- uses: super-linter/super-linter@v7.3.0 # x-release-please-version
+ uses: super-linter/super-linter/slim@v7.3.0 # x-release-please-version
env:
# To report GitHub Actions status checks
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
From bf2216f73175c74b764a278f48ae9b7331d954ce Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 5 Mar 2025 15:27:00 +0100
Subject: [PATCH 215/253] super-linter: TODO fix-lint-issues
---
.github/workflows/lint.yml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 951e26d..ab3ad59 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -8,7 +8,7 @@ on: # yamllint disable-line rule:truthy
permissions: {}
jobs:
- build:
+ lint:
name: Lint
runs-on: ubuntu-latest
@@ -31,3 +31,5 @@ jobs:
env:
# To report GitHub Actions status checks
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ # TODO automatically fix linting issues and commit them for PRs
+ # fix-lint-issues: # https://github.com/marketplace/actions/super-linter#github-actions-workflow-example-pull-request
From 6aa0f9b8177c2962d217bcd6e54caf84b3dded95 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 5 Mar 2025 15:34:03 +0100
Subject: [PATCH 216/253] super-linter: problem matchers included?
---
.github/workflows/lint.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index ab3ad59..0ed6b5b 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -28,6 +28,7 @@ jobs:
- name: Super-linter
uses: super-linter/super-linter/slim@v7.3.0 # x-release-please-version
+ # TODO need to create problem matchers for each linter? https://github.com/rhysd/actionlint/blob/v1.7.7/docs/usage.md#problem-matchers
env:
# To report GitHub Actions status checks
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
From d620ee2731ba66ae293ee69c87559744311a6a37 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 5 Mar 2025 14:46:12 +0000
Subject: [PATCH 217/253] chore(deps): update dependency
@stylistic/eslint-plugin-js to v2.13.0
---
package-lock.json | 59 +++++++++++++----------------------------------
1 file changed, 16 insertions(+), 43 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 364a707..3870b15 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -231,16 +231,13 @@
}
},
"node_modules/@stylistic/eslint-plugin-js": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.2.2.tgz",
- "integrity": "sha512-Vj2Q1YHVvJw+ThtOvmk5Yx7wZanVrIBRUTT89horLDb4xdP9GA1um9XOYQC6j67VeUC2gjZQnz5/RVJMzaOhtw==",
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.13.0.tgz",
+ "integrity": "sha512-GPPDK4+fcbsQD58a3abbng2Dx+jBoxM5cnYjBM4T24WFZRZdlNSKvR19TxP8CPevzMOodQ9QVzNeqWvMXzfJRA==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "@types/eslint": "^8.56.10",
- "acorn": "^8.11.3",
- "eslint-visitor-keys": "^4.0.0",
- "espree": "^10.0.1"
+ "eslint-visitor-keys": "^4.2.0",
+ "espree": "^10.3.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -250,11 +247,10 @@
}
},
"node_modules/@stylistic/eslint-plugin-js/node_modules/eslint-visitor-keys": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
- "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
"dev": true,
- "license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
@@ -270,17 +266,6 @@
"@types/ms": "*"
}
},
- "node_modules/@types/eslint": {
- "version": "8.56.10",
- "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz",
- "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/estree": "*",
- "@types/json-schema": "*"
- }
- },
"node_modules/@types/estree": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
@@ -1801,21 +1786,19 @@
}
},
"@stylistic/eslint-plugin-js": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.2.2.tgz",
- "integrity": "sha512-Vj2Q1YHVvJw+ThtOvmk5Yx7wZanVrIBRUTT89horLDb4xdP9GA1um9XOYQC6j67VeUC2gjZQnz5/RVJMzaOhtw==",
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.13.0.tgz",
+ "integrity": "sha512-GPPDK4+fcbsQD58a3abbng2Dx+jBoxM5cnYjBM4T24WFZRZdlNSKvR19TxP8CPevzMOodQ9QVzNeqWvMXzfJRA==",
"dev": true,
"requires": {
- "@types/eslint": "^8.56.10",
- "acorn": "^8.11.3",
- "eslint-visitor-keys": "^4.0.0",
- "espree": "^10.0.1"
+ "eslint-visitor-keys": "^4.2.0",
+ "espree": "^10.3.0"
},
"dependencies": {
"eslint-visitor-keys": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
- "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
"dev": true
}
}
@@ -1828,16 +1811,6 @@
"@types/ms": "*"
}
},
- "@types/eslint": {
- "version": "8.56.10",
- "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz",
- "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==",
- "dev": true,
- "requires": {
- "@types/estree": "*",
- "@types/json-schema": "*"
- }
- },
"@types/estree": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
From cb22abfb2fbc98664da57a33d6ee9195edd51246 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 5 Mar 2025 14:53:57 +0000
Subject: [PATCH 218/253] chore(deps): update dependency
@stylistic/eslint-plugin-js to v4
---
package-lock.json | 16 ++++++++--------
package.json | 2 +-
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 40345b6..e579b4f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -19,7 +19,7 @@
"puppeteer-extra-plugin-stealth": "^2.11.2"
},
"devDependencies": {
- "@stylistic/eslint-plugin-js": "^2.2.2",
+ "@stylistic/eslint-plugin-js": "^4.0.0",
"eslint": "^9.5.0"
},
"engines": {
@@ -231,9 +231,9 @@
}
},
"node_modules/@stylistic/eslint-plugin-js": {
- "version": "2.13.0",
- "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.13.0.tgz",
- "integrity": "sha512-GPPDK4+fcbsQD58a3abbng2Dx+jBoxM5cnYjBM4T24WFZRZdlNSKvR19TxP8CPevzMOodQ9QVzNeqWvMXzfJRA==",
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-4.2.0.tgz",
+ "integrity": "sha512-MiJr6wvyzMYl/wElmj8Jns8zH7Q1w8XoVtm+WM6yDaTrfxryMyb8n0CMxt82fo42RoLIfxAEtM6tmQVxqhk0/A==",
"dev": true,
"dependencies": {
"eslint-visitor-keys": "^4.2.0",
@@ -243,7 +243,7 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"peerDependencies": {
- "eslint": ">=8.40.0"
+ "eslint": ">=9.0.0"
}
},
"node_modules/@stylistic/eslint-plugin-js/node_modules/eslint-visitor-keys": {
@@ -1786,9 +1786,9 @@
}
},
"@stylistic/eslint-plugin-js": {
- "version": "2.13.0",
- "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.13.0.tgz",
- "integrity": "sha512-GPPDK4+fcbsQD58a3abbng2Dx+jBoxM5cnYjBM4T24WFZRZdlNSKvR19TxP8CPevzMOodQ9QVzNeqWvMXzfJRA==",
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-4.2.0.tgz",
+ "integrity": "sha512-MiJr6wvyzMYl/wElmj8Jns8zH7Q1w8XoVtm+WM6yDaTrfxryMyb8n0CMxt82fo42RoLIfxAEtM6tmQVxqhk0/A==",
"dev": true,
"requires": {
"eslint-visitor-keys": "^4.2.0",
diff --git a/package.json b/package.json
index 2c64f46..e1e3340 100644
--- a/package.json
+++ b/package.json
@@ -30,7 +30,7 @@
"puppeteer-extra-plugin-stealth": "^2.11.2"
},
"devDependencies": {
- "@stylistic/eslint-plugin-js": "^2.2.2",
+ "@stylistic/eslint-plugin-js": "^4.0.0",
"eslint": "^9.5.0"
}
}
From e75975b273a368f99306d57daa5c3b07f4a4443b Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 5 Mar 2025 14:54:01 +0000
Subject: [PATCH 219/253] chore(deps): update docker/build-push-action action
to v6
---
.github/workflows/docker.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 5e5adba..071585e 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -57,7 +57,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
-
name: Build and push
- uses: docker/build-push-action@v5
+ uses: docker/build-push-action@v6
# if: github.event_name != 'pull_request' # still want to build image
with:
context: .
From 3626fc17626f386614c1189f6a20809331065a74 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 5 Mar 2025 16:17:48 +0100
Subject: [PATCH 220/253] fixup #417
---
epic-games.js | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index 5ac6839..9aa4482 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -198,8 +198,7 @@ try {
if (btnText == 'in library') {
console.log(' Already in library! Nothing to claim.');
- //await notify(`Game Already in Library! Follow is a test link:.\n Game link ${url}`); // Decomment this line if you want to also test if a link is beeing sent correctly in a notification. The "already in library" notification was missing, I find it usefull.
- await notify(`Game Already in Library!`); // decomment the previous line and comment this one if you also want to test for proper link-handling via notification.
+ await notify(`Game already in library: ${url}`);
notify_game.status = 'existed';
db.data[user][game_id].status ||= 'existed'; // does not overwrite claimed or failed
if (db.data[user][game_id].status.startsWith('failed')) db.data[user][game_id].status = 'manual'; // was failed but now it's claimed
@@ -273,7 +272,9 @@ try {
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.');
- await notify(`epic-games: got captcha challenge.\n Game link ${url}`);// FIXED: Game link was not sent, probably error with html formatting? Anyway, the link is automatically parsed. TODO could even create purchase URL, see https://github.com/vogler/free-games-claimer/pull/130
+ // await notify(`epic-games: got captcha challenge right before claim of ${title}. Use VNC to solve it manually.`); // TODO not all apprise services understand HTML: https://github.com/vogler/free-games-claimer/pull/417
+ await notify(`epic-games: got captcha challenge for.\nGame link: ${url}`);
+ // TODO could even create purchase URL, see https://github.com/vogler/free-games-claimer/pull/130
// await page.waitForTimeout(2000);
// const p = path.resolve(cfg.dir.screenshots, 'epic-games', 'captcha', `${filenamify(datetime())}.png`);
// await captcha.screenshot({ path: p });
From ea69c76b5016e17cfb58d49764990b6459c99e20 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 11 Mar 2025 10:14:42 +0100
Subject: [PATCH 221/253] eg: fix #449: only notify once if 'Game already in
library'
---
epic-games.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/epic-games.js b/epic-games.js
index 9aa4482..f03350f 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -190,6 +190,7 @@ try {
title = await page.locator('h1').first().innerText();
}
const game_id = page.url().split('/').pop();
+ const existedInDb = db.data[user][game_id];
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);
if (bundle_includes) console.log(' This bundle includes:', bundle_includes);
@@ -198,7 +199,7 @@ try {
if (btnText == 'in library') {
console.log(' Already in library! Nothing to claim.');
- await notify(`Game already in library: ${url}`);
+ if (!existedInDb) await notify(`Game already in library: ${url}`);
notify_game.status = 'existed';
db.data[user][game_id].status ||= 'existed'; // does not overwrite claimed or failed
if (db.data[user][game_id].status.startsWith('failed')) db.data[user][game_id].status = 'manual'; // was failed but now it's claimed
From 8b7018f54c6427532ae65b99829d9cd5a1826b92 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 27 Mar 2025 16:26:40 +0100
Subject: [PATCH 222/253] fix actionlint errors (docker.yml)
Double quote to prevent globbing and word splitting
---
.github/workflows/docker.yml | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 071585e..6371f4c 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -28,12 +28,12 @@ jobs:
name: Set environment variables
run: |
BRANCH="${GITHUB_REF#refs/heads/}"
- echo "BRANCH=$BRANCH" >> $GITHUB_ENV
- echo "NOW=$(date -R)" >> $GITHUB_ENV # date -Iseconds; date +'%Y-%m-%dT%H:%M:%S'
+ echo "BRANCH=$BRANCH" >> "$GITHUB_ENV"
+ echo "NOW=$(date -R)" >> "$GITHUB_ENV" # date -Iseconds; date +'%Y-%m-%dT%H:%M:%S'
if [[ "$BRANCH" == "main" ]]; then
- echo "IMAGE_TAG=latest" >> $GITHUB_ENV
+ echo "IMAGE_TAG=latest" >> "$GITHUB_ENV"
else
- echo "IMAGE_TAG=$BRANCH" >> $GITHUB_ENV
+ echo "IMAGE_TAG=$BRANCH" >> "$GITHUB_ENV"
fi
-
name: Set up QEMU
From cfe42db805d7082f0f32d095f52e502164f4fc94 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 27 Mar 2025 16:53:27 +0100
Subject: [PATCH 223/253] pg: fix logo
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 2431c55..e3da438 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
Claims free games periodically on
-
[Epic Games Store](https://www.epicgames.com/store/free-games)
--
[Amazon Prime Gaming](https://gaming.amazon.com)
+-
[Amazon Prime Gaming](https://gaming.amazon.com)
-
[GOG](https://www.gog.com)
-
[Unreal Engine (Assets)](https://www.unrealengine.com/marketplace/en-US/assets?count=20&sortBy=effectiveDate&sortDir=DESC&start=0&tag=4910) ([experimental](https://github.com/vogler/free-games-claimer/issues/44), same login as Epic Games)
From 787522473fee1b17f89e02da9c729a8fd6d5a1f8 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 27 Mar 2025 16:53:41 +0100
Subject: [PATCH 224/253] readme: logos: vertical-align: middle
---
README.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index e3da438..cd215eb 100644
--- a/README.md
+++ b/README.md
@@ -6,10 +6,10 @@
# free-games-claimer
Claims free games periodically on
--
[Epic Games Store](https://www.epicgames.com/store/free-games)
--
[Amazon Prime Gaming](https://gaming.amazon.com)
--
[GOG](https://www.gog.com)
--
[Unreal Engine (Assets)](https://www.unrealengine.com/marketplace/en-US/assets?count=20&sortBy=effectiveDate&sortDir=DESC&start=0&tag=4910) ([experimental](https://github.com/vogler/free-games-claimer/issues/44), same login as Epic Games)
+-
[Epic Games Store](https://www.epicgames.com/store/free-games)
+-
[Amazon Prime Gaming](https://gaming.amazon.com)
+-
[GOG](https://www.gog.com)
+-
[Unreal Engine (Assets)](https://www.unrealengine.com/marketplace/en-US/assets?count=20&sortBy=effectiveDate&sortDir=DESC&start=0&tag=4910) ([experimental](https://github.com/vogler/free-games-claimer/issues/44), same login as Epic Games)
Pull requests welcome :)
From 041a77d9f6b882a16dcc1186c0bb4f410acb4d7a Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 27 Mar 2025 17:01:18 +0100
Subject: [PATCH 225/253] readme: logos: align: middle - CSS is stripped
---
README.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index cd215eb..e84224c 100644
--- a/README.md
+++ b/README.md
@@ -6,10 +6,10 @@
# free-games-claimer
Claims free games periodically on
--
[Epic Games Store](https://www.epicgames.com/store/free-games)
--
[Amazon Prime Gaming](https://gaming.amazon.com)
--
[GOG](https://www.gog.com)
--
[Unreal Engine (Assets)](https://www.unrealengine.com/marketplace/en-US/assets?count=20&sortBy=effectiveDate&sortDir=DESC&start=0&tag=4910) ([experimental](https://github.com/vogler/free-games-claimer/issues/44), same login as Epic Games)
+-
[Epic Games Store](https://www.epicgames.com/store/free-games)
+-
[Amazon Prime Gaming](https://gaming.amazon.com)
+-
[GOG](https://www.gog.com)
+-
[Unreal Engine (Assets)](https://www.unrealengine.com/marketplace/en-US/assets?count=20&sortBy=effectiveDate&sortDir=DESC&start=0&tag=4910) ([experimental](https://github.com/vogler/free-games-claimer/issues/44), same login as Epic Games)
Pull requests welcome :)
From 90e3400072303f0def90991740323a63dae33afc Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 27 Mar 2025 17:11:30 +0100
Subject: [PATCH 226/253] readme: logos: use GitHub asset upload
---
README.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index e84224c..17ec854 100644
--- a/README.md
+++ b/README.md
@@ -6,10 +6,10 @@
# free-games-claimer
Claims free games periodically on
--
[Epic Games Store](https://www.epicgames.com/store/free-games)
--
[Amazon Prime Gaming](https://gaming.amazon.com)
--
[GOG](https://www.gog.com)
--
[Unreal Engine (Assets)](https://www.unrealengine.com/marketplace/en-US/assets?count=20&sortBy=effectiveDate&sortDir=DESC&start=0&tag=4910) ([experimental](https://github.com/vogler/free-games-claimer/issues/44), same login as Epic Games)
+-
[Epic Games Store](https://www.epicgames.com/store/free-games)
+-
[Amazon Prime Gaming](https://gaming.amazon.com)
+-
[GOG](https://www.gog.com)
+-
[Unreal Engine (Assets)](https://www.unrealengine.com/marketplace/en-US/assets?count=20&sortBy=effectiveDate&sortDir=DESC&start=0&tag=4910) ([experimental](https://github.com/vogler/free-games-claimer/issues/44), same login as Epic Games)
Pull requests welcome :)
From 92cb32ff2dabd622252aa20bfbda63c953ed31a8 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 3 Apr 2025 15:33:00 +0200
Subject: [PATCH 227/253] ae: try fingerprint-injector
---
aliexpress.js | 29 +++-
package-lock.json | 398 +++++++++++++++++++++++++++++++++++++++++++++-
package.json | 1 +
3 files changed, 420 insertions(+), 8 deletions(-)
diff --git a/aliexpress.js b/aliexpress.js
index c44c673..e2439a4 100644
--- a/aliexpress.js
+++ b/aliexpress.js
@@ -2,16 +2,35 @@ import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdate
import { datetime, filenamify, prompt, handleSIGINT, stealth } from './src/util.js';
import { cfg } from './src/config.js';
+// using https://github.com/apify/fingerprint-suite worked, but has no launchPersistentContext...
+// from https://github.com/apify/fingerprint-suite/issues/162
+import { FingerprintInjector } from 'fingerprint-injector';
+import { FingerprintGenerator } from 'fingerprint-generator';
+
+const { fingerprint, headers } = new FingerprintGenerator().getFingerprint({
+ devices: ["mobile"],
+ operatingSystems: ["android"],
+});
+
const context = await firefox.launchPersistentContext(cfg.dir.browser, {
headless: cfg.headless,
- viewport: { width: cfg.width, height: cfg.height },
+ // 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
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-${filenamify(datetime())}.har` } : undefined, // will record a HAR file with network requests and responses; can be imported in Chrome devtools
+ recordHar: cfg.record ? { path: `data/record/aliexpress-${filenamify(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
+ userAgent: fingerprint.navigator.userAgent,
+ viewport: {
+ width: fingerprint.screen.width,
+ height: fingerprint.screen.height,
+ },
+ extraHTTPHeaders: {
+ 'accept-language': headers['accept-language'],
+ },
});
handleSIGINT(context);
-await stealth(context);
+// await stealth(context);
+await new FingerprintInjector().attachFingerprintToPlaywright(context, { fingerprint, headers });
context.setDefaultTimeout(cfg.debug ? 0 : cfg.timeout);
@@ -88,11 +107,11 @@ const merge = async () => {
try {
// await coins();
await [
- coins,
+ // coins,
// grow,
// gogo,
// euro,
- // merge,
+ merge,
].reduce((a, f) => a.then(async _ => { await auth(urls[f.name]); await f(); console.log() }), Promise.resolve());
// await page.pause();
diff --git a/package-lock.json b/package-lock.json
index e579b4f..dd57aa8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,6 +13,7 @@
"cross-env": "^7.0.3",
"dotenv": "^16.4.5",
"enquirer": "^2.4.1",
+ "fingerprint-injector": "^2.1.52",
"lowdb": "^7.0.1",
"otplib": "^12.0.1",
"playwright-firefox": "^1.45.0",
@@ -230,6 +231,18 @@
"@otplib/plugin-thirty-two": "^12.0.1"
}
},
+ "node_modules/@sindresorhus/is": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+ "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/is?sponsor=1"
+ }
+ },
"node_modules/@stylistic/eslint-plugin-js": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-4.2.0.tgz",
@@ -306,6 +319,15 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
+ "node_modules/adm-zip": {
+ "version": "0.5.16",
+ "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz",
+ "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0"
+ }
+ },
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -383,16 +405,67 @@
"concat-map": "0.0.1"
}
},
+ "node_modules/browserslist": {
+ "version": "4.24.4",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
+ "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001688",
+ "electron-to-chromium": "^1.5.73",
+ "node-releases": "^2.0.19",
+ "update-browserslist-db": "^1.1.1"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001709",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001709.tgz",
+ "integrity": "sha512-NgL3vUTnDrPCZ3zTahp4fsugQ4dc7EKTSzwQDPEel6DMoMnfH2jhry9n2Zm8onbSR+f/QtKHFOA+iAQu4kbtWA==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
"node_modules/chalk": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
@@ -502,6 +575,21 @@
"node": ">=0.10.0"
}
},
+ "node_modules/dot-prop": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz",
+ "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==",
+ "license": "MIT",
+ "dependencies": {
+ "is-obj": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/dotenv": {
"version": "16.4.7",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
@@ -513,6 +601,12 @@
"url": "https://dotenvx.com"
}
},
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.130",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.130.tgz",
+ "integrity": "sha512-Ou2u7L9j2XLZbhqzyX0jWDj6gA8D3jIfVzt4rikLf3cGBa0VdReuFimBKS9tQJA4+XpeCxj1NoWlfBXzbMa9IA==",
+ "license": "ISC"
+ },
"node_modules/enquirer": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz",
@@ -525,6 +619,15 @@
"node": ">=8.6"
}
},
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -787,6 +890,45 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/fingerprint-generator": {
+ "version": "2.1.63",
+ "resolved": "https://registry.npmjs.org/fingerprint-generator/-/fingerprint-generator-2.1.63.tgz",
+ "integrity": "sha512-9ud3dO2aD0wKc9/zEAletQZ/iPuGqTwmkA7Y33OeOv/N+OvTM6OStxXbNLCsGSD7jUYe4ei/TK9uXwiQntbZGA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "generative-bayesian-network": "^2.1.63",
+ "header-generator": "^2.1.63",
+ "tslib": "^2.4.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/fingerprint-injector": {
+ "version": "2.1.63",
+ "resolved": "https://registry.npmjs.org/fingerprint-injector/-/fingerprint-injector-2.1.63.tgz",
+ "integrity": "sha512-OZpxOKi4iyU2hzqXuJDddb5ayfLgWJvnaDwWAExC5/P+XuXbAMf9cLIOAEQ3B2SnAbXUXZw6k8vQqeg6vSrWIA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fingerprint-generator": "^2.1.63",
+ "tslib": "^2.4.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ },
+ "peerDependencies": {
+ "playwright": "^1.22.2",
+ "puppeteer": ">= 9.x"
+ },
+ "peerDependenciesMeta": {
+ "playwright": {
+ "optional": true
+ },
+ "puppeteer": {
+ "optional": true
+ }
+ }
+ },
"node_modules/flat-cache": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
@@ -845,6 +987,16 @@
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
+ "node_modules/generative-bayesian-network": {
+ "version": "2.1.63",
+ "resolved": "https://registry.npmjs.org/generative-bayesian-network/-/generative-bayesian-network-2.1.63.tgz",
+ "integrity": "sha512-nH1t4R9nlWSmvFoI4DEcpXd0+yoGZcySVuUBkXhR09/Mf7O9AWFmR8lWqVGIoxpBS0WRNpV/qj4swKGGQAxAPQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "adm-zip": "^0.5.9",
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -903,6 +1055,21 @@
"node": ">=8"
}
},
+ "node_modules/header-generator": {
+ "version": "2.1.63",
+ "resolved": "https://registry.npmjs.org/header-generator/-/header-generator-2.1.63.tgz",
+ "integrity": "sha512-dicAWZb/zXmI3fPuCw1Ra1sTTC9x9cc5EOhwugmI/keXnRc3BzxDQTGHge+MeAdqhpo2ZOFTEV6u08lXJUA2uQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "browserslist": "^4.21.1",
+ "generative-bayesian-network": "^2.1.63",
+ "ow": "^0.28.1",
+ "tslib": "^2.4.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
"node_modules/ignore": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
@@ -987,6 +1154,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-obj": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
+ "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-plain-object": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
@@ -1112,6 +1288,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/lodash.isequal": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
+ "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
+ "license": "MIT"
+ },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -1188,6 +1371,12 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true
},
+ "node_modules/node-releases": {
+ "version": "2.0.19",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
+ "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
+ "license": "MIT"
+ },
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -1223,6 +1412,25 @@
"@otplib/preset-v11": "^12.0.1"
}
},
+ "node_modules/ow": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/ow/-/ow-0.28.2.tgz",
+ "integrity": "sha512-dD4UpyBh/9m4X2NVjA+73/ZPBRF+uF4zIMFvvQsabMiEK8x41L3rQ8EENOi35kyyoaJwNxEeJcP6Fj1H4U409Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@sindresorhus/is": "^4.2.0",
+ "callsites": "^3.1.0",
+ "dot-prop": "^6.0.1",
+ "lodash.isequal": "^4.5.0",
+ "vali-date": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -1291,6 +1499,12 @@
"node": ">=8"
}
},
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
"node_modules/playwright-core": {
"version": "1.45.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.0.tgz",
@@ -1572,6 +1786,12 @@
"node": ">=0.2.6"
}
},
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -1592,6 +1812,36 @@
"node": ">= 10.0.0"
}
},
+ "node_modules/update-browserslist-db": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
+ "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -1602,6 +1852,15 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/vali-date": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz",
+ "integrity": "sha512-sgECfZthyaCKW10N0fm27cg8HYTFK5qMWgypqkXMQ4Wbl/zZKx7xZICgcoxIIE+WFAP/MBL2EFwC/YvLxw3Zeg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -1785,6 +2044,11 @@
"@otplib/plugin-thirty-two": "^12.0.1"
}
},
+ "@sindresorhus/is": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+ "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="
+ },
"@stylistic/eslint-plugin-js": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-4.2.0.tgz",
@@ -1841,6 +2105,11 @@
"dev": true,
"requires": {}
},
+ "adm-zip": {
+ "version": "0.5.16",
+ "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz",
+ "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ=="
+ },
"ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -1897,11 +2166,26 @@
"concat-map": "0.0.1"
}
},
+ "browserslist": {
+ "version": "4.24.4",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
+ "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
+ "requires": {
+ "caniuse-lite": "^1.0.30001688",
+ "electron-to-chromium": "^1.5.73",
+ "node-releases": "^2.0.19",
+ "update-browserslist-db": "^1.1.1"
+ }
+ },
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
- "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
+ },
+ "caniuse-lite": {
+ "version": "1.0.30001709",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001709.tgz",
+ "integrity": "sha512-NgL3vUTnDrPCZ3zTahp4fsugQ4dc7EKTSzwQDPEel6DMoMnfH2jhry9n2Zm8onbSR+f/QtKHFOA+iAQu4kbtWA=="
},
"chalk": {
"version": "5.4.1",
@@ -1977,11 +2261,24 @@
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
},
+ "dot-prop": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz",
+ "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==",
+ "requires": {
+ "is-obj": "^2.0.0"
+ }
+ },
"dotenv": {
"version": "16.4.7",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
"integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ=="
},
+ "electron-to-chromium": {
+ "version": "1.5.130",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.130.tgz",
+ "integrity": "sha512-Ou2u7L9j2XLZbhqzyX0jWDj6gA8D3jIfVzt4rikLf3cGBa0VdReuFimBKS9tQJA4+XpeCxj1NoWlfBXzbMa9IA=="
+ },
"enquirer": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz",
@@ -1991,6 +2288,11 @@
"strip-ansi": "^6.0.1"
}
},
+ "escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="
+ },
"escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -2165,6 +2467,25 @@
"path-exists": "^4.0.0"
}
},
+ "fingerprint-generator": {
+ "version": "2.1.63",
+ "resolved": "https://registry.npmjs.org/fingerprint-generator/-/fingerprint-generator-2.1.63.tgz",
+ "integrity": "sha512-9ud3dO2aD0wKc9/zEAletQZ/iPuGqTwmkA7Y33OeOv/N+OvTM6OStxXbNLCsGSD7jUYe4ei/TK9uXwiQntbZGA==",
+ "requires": {
+ "generative-bayesian-network": "^2.1.63",
+ "header-generator": "^2.1.63",
+ "tslib": "^2.4.0"
+ }
+ },
+ "fingerprint-injector": {
+ "version": "2.1.63",
+ "resolved": "https://registry.npmjs.org/fingerprint-injector/-/fingerprint-injector-2.1.63.tgz",
+ "integrity": "sha512-OZpxOKi4iyU2hzqXuJDddb5ayfLgWJvnaDwWAExC5/P+XuXbAMf9cLIOAEQ3B2SnAbXUXZw6k8vQqeg6vSrWIA==",
+ "requires": {
+ "fingerprint-generator": "^2.1.63",
+ "tslib": "^2.4.0"
+ }
+ },
"flat-cache": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
@@ -2209,6 +2530,15 @@
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
+ "generative-bayesian-network": {
+ "version": "2.1.63",
+ "resolved": "https://registry.npmjs.org/generative-bayesian-network/-/generative-bayesian-network-2.1.63.tgz",
+ "integrity": "sha512-nH1t4R9nlWSmvFoI4DEcpXd0+yoGZcySVuUBkXhR09/Mf7O9AWFmR8lWqVGIoxpBS0WRNpV/qj4swKGGQAxAPQ==",
+ "requires": {
+ "adm-zip": "^0.5.9",
+ "tslib": "^2.4.0"
+ }
+ },
"glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -2248,6 +2578,17 @@
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
+ "header-generator": {
+ "version": "2.1.63",
+ "resolved": "https://registry.npmjs.org/header-generator/-/header-generator-2.1.63.tgz",
+ "integrity": "sha512-dicAWZb/zXmI3fPuCw1Ra1sTTC9x9cc5EOhwugmI/keXnRc3BzxDQTGHge+MeAdqhpo2ZOFTEV6u08lXJUA2uQ==",
+ "requires": {
+ "browserslist": "^4.21.1",
+ "generative-bayesian-network": "^2.1.63",
+ "ow": "^0.28.1",
+ "tslib": "^2.4.0"
+ }
+ },
"ignore": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
@@ -2309,6 +2650,11 @@
"is-extglob": "^2.1.1"
}
},
+ "is-obj": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
+ "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="
+ },
"is-plain-object": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
@@ -2404,6 +2750,11 @@
"p-locate": "^5.0.0"
}
},
+ "lodash.isequal": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
+ },
"lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -2463,6 +2814,11 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true
},
+ "node-releases": {
+ "version": "2.0.19",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
+ "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="
+ },
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -2495,6 +2851,18 @@
"@otplib/preset-v11": "^12.0.1"
}
},
+ "ow": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/ow/-/ow-0.28.2.tgz",
+ "integrity": "sha512-dD4UpyBh/9m4X2NVjA+73/ZPBRF+uF4zIMFvvQsabMiEK8x41L3rQ8EENOi35kyyoaJwNxEeJcP6Fj1H4U409Q==",
+ "requires": {
+ "@sindresorhus/is": "^4.2.0",
+ "callsites": "^3.1.0",
+ "dot-prop": "^6.0.1",
+ "lodash.isequal": "^4.5.0",
+ "vali-date": "^1.0.0"
+ }
+ },
"p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -2538,6 +2906,11 @@
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
+ "picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
+ },
"playwright-core": {
"version": "1.45.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.0.tgz",
@@ -2691,6 +3064,11 @@
"resolved": "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz",
"integrity": "sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA=="
},
+ "tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
+ },
"type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -2705,6 +3083,15 @@
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ=="
},
+ "update-browserslist-db": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
+ "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
+ "requires": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ }
+ },
"uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -2714,6 +3101,11 @@
"punycode": "^2.1.0"
}
},
+ "vali-date": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz",
+ "integrity": "sha512-sgECfZthyaCKW10N0fm27cg8HYTFK5qMWgypqkXMQ4Wbl/zZKx7xZICgcoxIIE+WFAP/MBL2EFwC/YvLxw3Zeg=="
+ },
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
diff --git a/package.json b/package.json
index e1e3340..95027a2 100644
--- a/package.json
+++ b/package.json
@@ -24,6 +24,7 @@
"cross-env": "^7.0.3",
"dotenv": "^16.4.5",
"enquirer": "^2.4.1",
+ "fingerprint-injector": "^2.1.52",
"lowdb": "^7.0.1",
"otplib": "^12.0.1",
"playwright-firefox": "^1.45.0",
From 0fefcc47b7271eff6997ff06aa58fcc90f39e682 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 15 Apr 2025 17:08:34 +0200
Subject: [PATCH 228/253] pg: skipBasedOnTime if days left > PG_TIMELEFT
---
prime-gaming.js | 36 +++++++++++++++++++-----------------
src/config.js | 2 +-
2 files changed, 20 insertions(+), 18 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index adfdbd4..7ef528b 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -131,23 +131,25 @@ try {
// bottom to top: oldest to newest games
internal.reverse();
external.reverse();
- const checkTimeLeft = async url => {
- // console.log(' Checking time left for game:', url);
- const check = async p => {
- console.log(' ', await p.locator('.availability-date').innerText());
- const dueDateOrg = await p.locator('.availability-date .tw-bold').innerText();
- const dueDate = datetime(new Date(Date.parse(dueDateOrg + ' 17:00')));
- console.log(' Due date:', dueDate);
- };
- if (page.url() == url) {
- await check(page);
- } else {
- const p = await context.newPage();
+ const sameOrNewPage = async url => new Promise(async (resolve, _reject) => {
+ const isNew = page.url() != url;
+ let p = page;
+ if (isNew) {
+ p = await context.newPage();
await p.goto(url, { waitUntil: 'domcontentloaded' });
- await check(p);
- p.close();
}
- };
+ resolve([p, isNew]);
+ });
+ const skipBasedOnTime = async url => {
+ // console.log(' Checking time left for game:', url);
+ const [p, isNew] = await sameOrNewPage(url);
+ const dueDateOrg = await p.locator('.availability-date .tw-bold').innerText();
+ const dueDate = new Date(Date.parse(dueDateOrg + ' 17:00'));
+ const daysLeft = (dueDate.getTime() - Date.now())/1000/60/60/24;
+ console.log(' ', await p.locator('.availability-date').innerText(), '->', daysLeft.toFixed(2));
+ if (isNew) await p.close();
+ return daysLeft > cfg.pg_timeLeft;
+ }
console.log('Number of free unclaimed games (Prime Gaming):', internal.length);
// claim games in internal store
for (const card of internal) {
@@ -156,7 +158,7 @@ try {
const slug = await (await card.$('a')).getAttribute('href');
const url = 'https://gaming.amazon.com' + slug.split('?')[0];
console.log('Current free game:', title);
- if (cfg.pg_timeLeft) await checkTimeLeft(url);
+ if (cfg.pg_timeLeft && await skipBasedOnTime(url)) continue;
if (cfg.dryrun) continue;
if (cfg.interactive && !await confirm()) continue;
await (await card.$('.tw-button:has-text("Claim")')).click();
@@ -184,7 +186,7 @@ try {
const item_text = await page.innerText('[data-a-target="DescriptionItemDetails"]');
const store = item_text.toLowerCase().replace(/.* on /, '').slice(0, -1);
console.log(' External store:', store);
- if (cfg.pg_timeLeft) await checkTimeLeft(url);
+ if (cfg.pg_timeLeft && await skipBasedOnTime(url)) continue;
if (cfg.dryrun) continue;
if (cfg.interactive && !await confirm()) continue;
await Promise.any([page.click('[data-a-target="buy-box"] .tw-button:has-text("Get game")'), page.click('[data-a-target="buy-box"] .tw-button:has-text("Claim")'), page.click('.tw-button:has-text("Complete Claim")'), page.waitForSelector('div:has-text("Link game account")'), page.waitForSelector('.thank-you-title:has-text("Success")')]); // waits for navigation
diff --git a/src/config.js b/src/config.js
index 4a384b8..a8b1817 100644
--- a/src/config.js
+++ b/src/config.js
@@ -49,5 +49,5 @@ export const cfg = {
pg_redeem: process.env.PG_REDEEM == '1', // prime-gaming: redeem keys on external stores
lg_email: process.env.LG_EMAIL || process.env.PG_EMAIL || process.env.EMAIL, // prime-gaming: external: legacy-games: email to use for redeeming
pg_claimdlc: process.env.PG_CLAIMDLC == '1', // prime-gaming: claim in-game content
- pg_timeLeft: process.env.PG_TIMELEFT == '1', // prime-gaming: list time left to claim
+ pg_timeLeft: Number(process.env.PG_TIMELEFT), // prime-gaming: check time left to claim and skip game if there are more than PG_TIMELEFT days left to claim it
};
From 04d1b7ea9e95af8f3e569397c53bb9ff591ddb4a Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 15 Apr 2025 17:22:21 +0200
Subject: [PATCH 229/253] pg: fix auto-login; rememberMe was removed
---
prime-gaming.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 7ef528b..3077717 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -55,7 +55,7 @@ try {
await page.fill('[name=email]', email);
await page.click('input[type="submit"]');
await page.fill('[name=password]', password);
- await page.check('[name=rememberMe]');
+ // await page.check('[name=rememberMe]'); // no longer exists
await page.click('input[type="submit"]');
page.waitForURL('**/ap/signin**').then(async () => { // check for wrong credentials
const error = await page.locator('.a-alert-content').first().innerText();
From 726db4527be872e01ffa9b0a66df02c188fa3cd4 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 15 Apr 2025 17:26:27 +0200
Subject: [PATCH 230/253] Current free game title in blue
---
epic-games.js | 3 ++-
gog.js | 3 ++-
prime-gaming.js | 8 ++++----
3 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/epic-games.js b/epic-games.js
index f03350f..4db1e75 100644
--- a/epic-games.js
+++ b/epic-games.js
@@ -1,5 +1,6 @@
import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdated playwright-extra
import { authenticator } from 'otplib';
+import chalk from 'chalk';
import path from 'path';
import { existsSync, writeFileSync, appendFileSync } from 'fs';
import { resolve, jsonDb, datetime, stealth, filenamify, prompt, notify, html_game_list, handleSIGINT } from './src/util.js';
@@ -192,7 +193,7 @@ try {
const game_id = page.url().split('/').pop();
const existedInDb = db.data[user][game_id];
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);
+ console.log('Current free game:', chalk.blue(title));
if (bundle_includes) console.log(' This bundle includes:', bundle_includes);
const notify_game = { title, url, status: 'failed' };
notify_games.push(notify_game); // status is updated below
diff --git a/gog.js b/gog.js
index c4d5078..6269fc2 100644
--- a/gog.js
+++ b/gog.js
@@ -1,4 +1,5 @@
import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdated playwright-extra
+import chalk from 'chalk';
import { resolve, jsonDb, datetime, filenamify, prompt, notify, html_game_list, handleSIGINT } from './src/util.js';
import { cfg } from './src/config.js';
@@ -102,7 +103,7 @@ try {
const match_all = text.match(/Claim (.*) and don't miss the|Success! (.*) was added to/);
const title = match_all[1] ? match_all[1] : match_all[2];
const url = await banner.locator('a').first().getAttribute('href');
- console.log(`Current free game: ${title} - ${url}`);
+ console.log(`Current free game: ${chalk.blue(title)} - ${url}`);
db.data[user][title] ||= { title, time: datetime(), url };
if (cfg.dryrun) process.exit(1);
// await page.locator('#giveaway:not(.is-loading)').waitFor(); // otherwise screenshot is sometimes with loading indicator instead of game title; #TODO fix, skipped due to timeout, see #240
diff --git a/prime-gaming.js b/prime-gaming.js
index 3077717..a425ebe 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -150,14 +150,14 @@ try {
if (isNew) await p.close();
return daysLeft > cfg.pg_timeLeft;
}
- console.log('Number of free unclaimed games (Prime Gaming):', internal.length);
+ console.log('\nNumber of free unclaimed games (Prime Gaming):', internal.length);
// claim games in internal store
for (const card of internal) {
await card.scrollIntoViewIfNeeded();
const title = await (await card.$('.item-card-details__body__primary')).innerText();
const slug = await (await card.$('a')).getAttribute('href');
const url = 'https://gaming.amazon.com' + slug.split('?')[0];
- console.log('Current free game:', title);
+ console.log('Current free game:', chalk.blue(title));
if (cfg.pg_timeLeft && await skipBasedOnTime(url)) continue;
if (cfg.dryrun) continue;
if (cfg.interactive && !await confirm()) continue;
@@ -168,7 +168,7 @@ try {
// console.log('Image:', img);
await card.screenshot({ path: screenshot('internal', `${filenamify(title)}.png`) });
}
- console.log('Number of free unclaimed games (external stores):', external.length);
+ console.log('\nNumber of free unclaimed games (external stores):', external.length);
// claim games in external/linked stores. Linked: origin.com, epicgames.com; Redeem-key: gog.com, legacygames.com, microsoft
const external_info = [];
for (const card of external) { // need to get data incl. URLs in this loop and then navigate in another, otherwise .all() would update after coming back and .elementHandles() like above would lead to error due to page navigation: elementHandle.$: Protocol error (Page.adoptNode)
@@ -180,7 +180,7 @@ try {
}
// external_info = [ { title: 'Fallout 76 (XBOX)', url: 'https://gaming.amazon.com/fallout-76-xbox-fgwp/dp/amzn1.pg.item.9fe17d7b-b6c2-4f58-b494-cc4e79528d0b?ingress=amzn&ref_=SM_Fallout76XBOX_S01_FGWP_CRWN' } ];
for (const { title, url } of external_info) {
- console.log('Current free game:', title); // , url);
+ console.log('Current free game:', chalk.blue(title)); // , url);
await page.goto(url, { waitUntil: 'domcontentloaded' });
if (cfg.debug) await page.pause();
const item_text = await page.innerText('[data-a-target="DescriptionItemDetails"]');
From dee47d22184614a2856c354a65cc5cfc694e9d46 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 15 Apr 2025 17:27:21 +0200
Subject: [PATCH 231/253] pg: redeem gog: detect captcha
---
prime-gaming.js | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index a425ebe..dada15f 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -258,10 +258,14 @@ try {
const r2 = page2.waitForResponse(r => r.request().method() == 'POST' && r.url().startsWith('https://redeem.gog.com/'));
await page2.click('[type="submit"]'); // click Redeem
const r2t = await (await r2).text();
+ const reason2 = JSON.parse(r2t).reason;
if (r2t == '{}') {
redeem_action = 'redeemed';
console.log(' Redeemed successfully.');
db.data[user][title].status = 'claimed and redeemed';
+ } else if (reason2?.includes('captcha')) {
+ redeem_action = 'redeem (got captcha)';
+ console.error(' Got captcha; could not redeem!');
} else {
console.debug(` Response 2: ${r2t}`);
console.log(' Unknown Response 2 - please report in https://github.com/vogler/free-games-claimer/issues/5');
@@ -271,7 +275,7 @@ try {
console.error(` Redeem on ${store} is experimental!`);
// await page2.pause();
if (page2.url().startsWith('https://login.')) {
- console.error(' Not logged in! Use the browser to login manually. Waiting for 60s.');
+ console.error(' Not logged in! Please redeem the code above manually. You can now login in the browser for next time. Waiting for 60s.');
await page2.waitForTimeout(60 * 1000);
redeem_action = 'redeem (login)';
} else {
@@ -308,6 +312,7 @@ try {
}
}
} else if (store == 'legacy games') {
+ // await page2.pause();
await page2.fill('[name=coupon_code]', code);
await page2.fill('[name=email]', cfg.lg_email);
await page2.fill('[name=email_validate]', cfg.lg_email);
From 3e13e9fba858aeb5e5eff6144e0a6ddc28350c10 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Tue, 13 May 2025 20:29:21 +0000
Subject: [PATCH 232/253] chore(deps): update super-linter/super-linter action
to v7.4.0
---
.github/workflows/lint.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 0ed6b5b..02ca3cb 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -27,7 +27,7 @@ jobs:
fetch-depth: 0
- name: Super-linter
- uses: super-linter/super-linter/slim@v7.3.0 # x-release-please-version
+ uses: super-linter/super-linter/slim@v7.4.0 # x-release-please-version
# TODO need to create problem matchers for each linter? https://github.com/rhysd/actionlint/blob/v1.7.7/docs/usage.md#problem-matchers
env:
# To report GitHub Actions status checks
From 5cfe57870a21382a6ce3607934f21f1368115016 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Tue, 13 May 2025 23:43:29 +0200
Subject: [PATCH 233/253] note on git describe for version
---
src/version.js | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/version.js b/src/version.js
index 7f875cf..bfcd12a 100644
--- a/src/version.js
+++ b/src/version.js
@@ -42,6 +42,11 @@ const gh = await (await fetch('https://api.github.com/repos/vogler/free-games-cl
log('Local commit:', sha, new Date(date));
log('Online commit:', gh.sha, new Date(gh.commit.committer.date));
+// git describe --all --long --dirty
+// --> heads/main-0-gdee47d2-dirty
+// git describe --tags --long --dirty
+// --> v1.7-35-gdee47d2-dirty
+
if (sha == gh.sha) {
log('Running the latest version!');
} else {
From 97ef14f5146ff27686a9c9c430aa82629e22c479 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 14 May 2025 00:18:22 +0200
Subject: [PATCH 234/253] uncommited steam-games.js from 2024-09-24 - WIP just
saves all games and their stats
---
steam-games.js | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
create mode 100644 steam-games.js
diff --git a/steam-games.js b/steam-games.js
new file mode 100644
index 0000000..9b307fc
--- /dev/null
+++ b/steam-games.js
@@ -0,0 +1,73 @@
+import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdated playwright-extra
+import { jsonDb, prompt } from './src/util.js';
+import { cfg } from './src/config.js';
+
+const db = await jsonDb('steam-games.json', {});
+
+const user = cfg.steam_id || await prompt({ message: 'Enter Steam community id ("View my profile", then copy from URL)' });
+
+// using https://github.com/apify/fingerprint-suite worked, but has no launchPersistentContext...
+// from https://github.com/apify/fingerprint-suite/issues/162
+import { FingerprintInjector } from 'fingerprint-injector';
+import { FingerprintGenerator } from 'fingerprint-generator';
+
+const { fingerprint, headers } = new FingerprintGenerator().getFingerprint({
+ devices: ["desktop"],
+ operatingSystems: ["windows"],
+});
+
+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
+ userAgent: fingerprint.navigator.userAgent,
+ viewport: {
+ width: fingerprint.screen.width,
+ height: fingerprint.screen.height,
+ },
+ extraHTTPHeaders: {
+ 'accept-language': headers['accept-language'],
+ },
+});
+// await stealth(context);
+await new FingerprintInjector().attachFingerprintToPlaywright(context, { fingerprint, headers });
+
+context.setDefaultTimeout(cfg.debug ? 0 : cfg.timeout);
+
+const page = context.pages().length ? context.pages()[0] : await context.newPage(); // should always exist
+
+try {
+ await page.goto(`https://steamcommunity.com/id/${user}/games?tab=all`);
+ const games = page.locator('div[data-featuretarget="gameslist-root"] > div.Panel > div.Panel > div');
+ await games.last().waitFor();
+ await page.keyboard.press('End');
+ await page.waitForLoadState('networkidle');
+ console.log('All Games:', await games.count());
+ for (const game of await games.all()) {
+ const title = await game.locator('span a').innerText();
+ let time, last, achievements, size;
+ const ltime = game.locator('span:has-text("total played")');
+ if (await ltime.count()) time = (await ltime.first().innerText()).split('\n')[1];
+ const llast = game.locator('span:has-text("last played")');
+ if (await llast.count()) last = (await llast.first().innerText()).split('\n')[1];
+ const lachievements = game.locator('a:has-text("achievements") + span');
+ if (await lachievements.count()) achievements = (await lachievements.first().innerText()).split('\n');
+ const lsize = game.locator('span:has(+ button)');
+ if (await lsize.count()) size = await lsize.first().innerText();
+ const url = await game.locator('a').first().getAttribute('href');
+ const img = await game.locator('img').first().getAttribute('src');
+ const stat = { title, time, last, achievements, size, url, img };
+ console.log(stat);
+ db.data[title] = stat;
+ }
+
+ // await page.pause();
+} catch (error) {
+ process.exitCode ||= 1;
+ console.error('--- Exception:');
+ console.error(error); // .toString()?
+} finally {
+ await db.write(); // write out json db
+}
+if (page.video()) console.log('Recorded video:', await page.video().path());
+await context.close();
From 6aea18836dd47683bba7e6ca54ebc09c1397f5b6 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 14 May 2025 00:30:39 +0200
Subject: [PATCH 235/253] enquirer: esc is fine, but after first prompt SIGINT
will be ignore; onRawSIGINT keeps process running -> switch to inquirer
---
test/sigint-enquirer-raw-keeps-running.js | 21 +++++++++++++++++++++
test/sigint-enquirer-raw.js | 17 +++++++++--------
test/sigint-enquirer-simple.js | 5 +++++
3 files changed, 35 insertions(+), 8 deletions(-)
create mode 100644 test/sigint-enquirer-raw-keeps-running.js
diff --git a/test/sigint-enquirer-raw-keeps-running.js b/test/sigint-enquirer-raw-keeps-running.js
new file mode 100644
index 0000000..23d9983
--- /dev/null
+++ b/test/sigint-enquirer-raw-keeps-running.js
@@ -0,0 +1,21 @@
+// open issue: prevents handleSIGINT() to work if prompt is cancelled with Ctrl-C instead of Escape: https://github.com/enquirer/enquirer/issues/372
+function onRawSIGINT(fn) {
+ const { stdin, stdout } = process;
+ stdin.setRawMode(true);
+ stdin.resume();
+ stdin.on('data', data => {
+ const key = data.toString('utf-8');
+ if (key === '\u0003') { // ctrl + c
+ fn();
+ } else {
+ stdout.write(key);
+ }
+ });
+}
+console.log(1)
+onRawSIGINT(() => {
+ console.log('raw'); process.exit(1);
+});
+console.log(2)
+
+// onRawSIGINT workaround for enquirer keeps the process from exiting here...
diff --git a/test/sigint-enquirer-raw.js b/test/sigint-enquirer-raw.js
index 0a95892..e6b538d 100644
--- a/test/sigint-enquirer-raw.js
+++ b/test/sigint-enquirer-raw.js
@@ -1,10 +1,10 @@
// https://github.com/enquirer/enquirer/issues/372
-import { prompt } from '../src/util.js';
+import { prompt, handleSIGINT } from '../src/util.js';
-const handleSIGINT = () => process.on('SIGINT', () => { // e.g. when killed by Ctrl-C
- console.log('\nInterrupted by SIGINT. Exit!');
- process.exitCode = 130;
-});
+// const handleSIGINT = () => process.on('SIGINT', () => { // e.g. when killed by Ctrl-C
+// console.log('\nInterrupted by SIGINT. Exit!');
+// process.exitCode = 130;
+// });
handleSIGINT();
function onRawSIGINT(fn) {
@@ -20,15 +20,16 @@ function onRawSIGINT(fn) {
}
});
}
-onRawSIGINT(() => {
- console.log('raw'); process.exit(1);
-});
+// onRawSIGINT(() => {
+// console.log('raw'); process.exit(1);
+// });
console.log('hello');
console.error('hello error');
try {
let i = 'foo';
i = await prompt(); // SIGINT no longer handled if this is executed
+ i = await prompt(); // SIGINT no longer handled if this is executed
// handleSIGINT();
console.log('value:', i);
setTimeout(() => console.log('timeout 3s'), 3000);
diff --git a/test/sigint-enquirer-simple.js b/test/sigint-enquirer-simple.js
index 13e7d02..25f13ee 100644
--- a/test/sigint-enquirer-simple.js
+++ b/test/sigint-enquirer-simple.js
@@ -13,3 +13,8 @@ await enquirer.prompt({
name: 'username',
message: 'What is your username?',
});
+await enquirer.prompt({
+ type: 'input',
+ name: 'username',
+ message: 'What is your username 2?',
+});
From a5ce5ec81615cb4d344a31553a2b23028e9ac451 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 14 May 2025 00:46:37 +0200
Subject: [PATCH 236/253] fix CodeQL alerts 5-7
https://github.com/vogler/free-games-claimer/security/code-scanning/5
https://github.com/vogler/free-games-claimer/security/code-scanning/6
https://github.com/vogler/free-games-claimer/security/code-scanning/7
---
.github/workflows/docker.yml | 3 +++
.github/workflows/sonar.yml | 7 ++++++-
aliexpress.js | 2 +-
3 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 6371f4c..722206a 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -17,6 +17,9 @@ on:
branches:
- "main" # only PRs against main
+permissions:
+ contents: read
+
jobs:
docker:
runs-on: ubuntu-latest
diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml
index eb4ad62..29f81c6 100644
--- a/.github/workflows/sonar.yml
+++ b/.github/workflows/sonar.yml
@@ -1,3 +1,5 @@
+name: Sonar
+
on:
# Trigger analysis when pushing in main or pull requests, and when creating a pull request.
push:
@@ -5,7 +7,10 @@ on:
- main
pull_request:
types: [opened, synchronize, reopened]
-name: Sonar
+
+permissions:
+ contents: read
+
jobs:
sonarcloud:
runs-on: ubuntu-latest
diff --git a/aliexpress.js b/aliexpress.js
index e2439a4..52e75a0 100644
--- a/aliexpress.js
+++ b/aliexpress.js
@@ -40,7 +40,7 @@ const auth = async (url) => {
console.log('auth', url);
await page.goto(url, { waitUntil: 'domcontentloaded' });
// redirects to https://login.aliexpress.com/?return_url=https%3A%2F%2Fwww.aliexpress.com%2Fp%2Fcoin-pc-index%2Findex.html
- await Promise.any([page.waitForURL(/.*login.aliexpress.com.*/).then(async () => {
+ await Promise.any([page.waitForURL(/.*login\.aliexpress.com.*/).then(async () => {
// manual login
console.error('Not logged in! Will wait for 120s for you to login...');
// await page.waitForTimeout(120*1000);
From 7c8682ac91b2c2a193dad4190972d2d1d5ccc592 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 14 May 2025 01:31:04 +0200
Subject: [PATCH 237/253] build docker image for each branch/PR?
---
.github/workflows/docker.yml | 35 ++++++++++++++---------------------
1 file changed, 14 insertions(+), 21 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 722206a..360e72a 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -2,24 +2,20 @@ name: Build and push Docker image (amd64, arm64 to hub.docker.com and ghcr.io)
on:
workflow_dispatch: # allow manual trigger
- # https://github.com/orgs/community/discussions/26276
- push:
- branches:
- - "main"
- - "v*"
- tags:
- - "v*"
- paths: # ignore changes to certain files
+ push: # build for each branch
+ # branches: ["main"]
+ paths: # ignore changes to .md files
- '**'
- '!*.md'
# - '!.github/**'
- pull_request: # runs when opened/reopned or when the head branch is updated, see https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request
- branches:
- - "main" # only PRs against main
+ pull_request: # runs when opened/reopned or when the head branch is updated
permissions:
contents: read
+env:
+ BRANCH: ${{ github.head_ref || github.ref_name }} # head_ref/base_ref are only set for PRs, for branches ref_name will be used
+
jobs:
docker:
runs-on: ubuntu-latest
@@ -30,13 +26,11 @@ jobs:
-
name: Set environment variables
run: |
- BRANCH="${GITHUB_REF#refs/heads/}"
- echo "BRANCH=$BRANCH" >> "$GITHUB_ENV"
- echo "NOW=$(date -R)" >> "$GITHUB_ENV" # date -Iseconds; date +'%Y-%m-%dT%H:%M:%S'
+ echo "NOW=$(date -R)" >> $GITHUB_ENV # date -Iseconds; date +'%Y-%m-%dT%H:%M:%S'
if [[ "$BRANCH" == "main" ]]; then
- echo "IMAGE_TAG=latest" >> "$GITHUB_ENV"
+ echo "IMAGE_TAG=latest" >> $GITHUB_ENV
else
- echo "IMAGE_TAG=$BRANCH" >> "$GITHUB_ENV"
+ echo "IMAGE_TAG=$BRANCH" >> $GITHUB_ENV
fi
-
name: Set up QEMU
@@ -47,7 +41,7 @@ jobs:
-
name: Login to Docker Hub
uses: docker/login-action@v3
- if: github.event_name != 'pull_request' # TODO if DOCKERHUB_* are set?
+ if: ${{ secrets.DOCKERHUB_USERNAME != '' && secrets.DOCKERHUB_TOKEN != '' }}
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
@@ -61,16 +55,15 @@ jobs:
-
name: Build and push
uses: docker/build-push-action@v6
- # if: github.event_name != 'pull_request' # still want to build image
+ if: ${{ env.IMAGE_TAG != '' }}
with:
context: .
- push: ${{ github.event_name != 'pull_request' }} # TODO push for forks?
+ push: ${{ secrets.DOCKERHUB_USERNAME != '' }}
build-args: |
COMMIT=${{ github.sha }}
BRANCH=${{ env.BRANCH }}
NOW=${{ env.NOW }}
- platforms: linux/amd64,linux/arm64 # ,linux/arm/v7
- # TODO docker tag only if DOCKERHUB_* are set?
+ platforms: linux/amd64,linux/arm64
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/free-games-claimer:${{env.IMAGE_TAG}}
ghcr.io/${{ github.actor }}/free-games-claimer:${{env.IMAGE_TAG}}
From 4ce0d44b793a2298374a4cb4ffc6014f56dee366 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 14 May 2025 01:44:14 +0200
Subject: [PATCH 238/253] can't check secrets in if expr...
---
.github/workflows/docker.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 360e72a..26ce96d 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -41,7 +41,7 @@ jobs:
-
name: Login to Docker Hub
uses: docker/login-action@v3
- if: ${{ secrets.DOCKERHUB_USERNAME != '' && secrets.DOCKERHUB_TOKEN != '' }}
+ # if: ${{ secrets.DOCKERHUB_USERNAME != '' && secrets.DOCKERHUB_TOKEN != '' }} # does not work: Unrecognized named-value: 'secrets' - https://www.cloudtruth.com/blog/skipping-jobs-in-github-actions-when-secrets-are-unavailable-securely-inject-configuration-secrets-into-github
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
From bf0c9032cc4fc5a27e5366c0770992cc01a79e4b Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Wed, 14 May 2025 01:59:37 +0200
Subject: [PATCH 239/253] need 'packages: write' permission for ghcr.io
---
.github/workflows/docker.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 26ce96d..2c42006 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -12,6 +12,7 @@ on:
permissions:
contents: read
+ packages: write
env:
BRANCH: ${{ github.head_ref || github.ref_name }} # head_ref/base_ref are only set for PRs, for branches ref_name will be used
From 0a4cac35717722b51b4d795f3a4fd8c3eaa2da16 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Thu, 15 May 2025 23:51:49 +0200
Subject: [PATCH 240/253] ncu -u
---
package-lock.json | 1998 ++++++++++++++++++++++++++++++++++++++++-----
package.json | 12 +-
2 files changed, 1814 insertions(+), 196 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index dd57aa8..9ab1fff 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,19 +9,19 @@
"version": "1.4.0",
"license": "AGPL-3.0-only",
"dependencies": {
- "chalk": "^5.3.0",
+ "chalk": "^5.4.1",
"cross-env": "^7.0.3",
- "dotenv": "^16.4.5",
+ "dotenv": "^16.5.0",
"enquirer": "^2.4.1",
- "fingerprint-injector": "^2.1.52",
+ "fingerprint-injector": "^2.1.66",
"lowdb": "^7.0.1",
"otplib": "^12.0.1",
- "playwright-firefox": "^1.45.0",
+ "playwright-firefox": "^1.52.0",
"puppeteer-extra-plugin-stealth": "^2.11.2"
},
"devDependencies": {
- "@stylistic/eslint-plugin-js": "^4.0.0",
- "eslint": "^9.5.0"
+ "@stylistic/eslint-plugin-js": "^4.2.0",
+ "eslint": "^9.26.0"
},
"engines": {
"node": ">=17"
@@ -61,10 +61,11 @@
}
},
"node_modules/@eslint/config-array": {
- "version": "0.19.2",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz",
- "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==",
+ "version": "0.20.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz",
+ "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==",
"dev": true,
+ "license": "Apache-2.0",
"dependencies": {
"@eslint/object-schema": "^2.1.6",
"debug": "^4.3.1",
@@ -74,11 +75,22 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
- "node_modules/@eslint/core": {
- "version": "0.12.0",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz",
- "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==",
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz",
+ "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==",
"dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
+ "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
+ "dev": true,
+ "license": "Apache-2.0",
"dependencies": {
"@types/json-schema": "^7.0.15"
},
@@ -87,10 +99,11 @@
}
},
"node_modules/@eslint/eslintrc": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz",
- "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==",
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
+ "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
@@ -110,10 +123,11 @@
}
},
"node_modules/@eslint/js": {
- "version": "9.21.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.21.0.tgz",
- "integrity": "sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==",
+ "version": "9.26.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.26.0.tgz",
+ "integrity": "sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
@@ -123,17 +137,19 @@
"resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
"integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
"dev": true,
+ "license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/plugin-kit": {
- "version": "0.2.7",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz",
- "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==",
+ "version": "0.2.8",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz",
+ "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==",
"dev": true,
+ "license": "Apache-2.0",
"dependencies": {
- "@eslint/core": "^0.12.0",
+ "@eslint/core": "^0.13.0",
"levn": "^0.4.1"
},
"engines": {
@@ -189,6 +205,28 @@
"url": "https://github.com/sponsors/nzakas"
}
},
+ "node_modules/@modelcontextprotocol/sdk": {
+ "version": "1.11.3",
+ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.11.3.tgz",
+ "integrity": "sha512-rmOWVRUbUJD7iSvJugjUbFZshTAuJ48MXoZ80Osx1GM0K/H1w7rSEvmw8m6vdWxNASgtaHIhAgre4H/E9GJiYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "content-type": "^1.0.5",
+ "cors": "^2.8.5",
+ "cross-spawn": "^7.0.5",
+ "eventsource": "^3.0.2",
+ "express": "^5.0.1",
+ "express-rate-limit": "^7.5.0",
+ "pkce-challenge": "^5.0.0",
+ "raw-body": "^3.0.0",
+ "zod": "^3.23.8",
+ "zod-to-json-schema": "^3.24.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@otplib/core": {
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/@otplib/core/-/core-12.0.1.tgz",
@@ -272,9 +310,10 @@
}
},
"node_modules/@types/debug": {
- "version": "4.1.8",
- "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz",
- "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==",
+ "version": "4.1.12",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
+ "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
+ "license": "MIT",
"dependencies": {
"@types/ms": "*"
}
@@ -293,9 +332,24 @@
"license": "MIT"
},
"node_modules/@types/ms": {
- "version": "0.7.31",
- "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
- "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA=="
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
+ "license": "MIT"
+ },
+ "node_modules/accepts": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
+ "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "^3.0.0",
+ "negotiator": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
},
"node_modules/acorn": {
"version": "8.14.1",
@@ -387,6 +441,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
"integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
@@ -396,6 +451,27 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
+ "node_modules/body-parser": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
+ "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "^3.1.2",
+ "content-type": "^1.0.5",
+ "debug": "^4.4.0",
+ "http-errors": "^2.0.0",
+ "iconv-lite": "^0.6.3",
+ "on-finished": "^2.4.1",
+ "qs": "^6.14.0",
+ "raw-body": "^3.0.0",
+ "type-is": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -406,9 +482,9 @@
}
},
"node_modules/browserslist": {
- "version": "4.24.4",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
- "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
+ "version": "4.24.5",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz",
+ "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==",
"funding": [
{
"type": "opencollective",
@@ -425,10 +501,10 @@
],
"license": "MIT",
"dependencies": {
- "caniuse-lite": "^1.0.30001688",
- "electron-to-chromium": "^1.5.73",
+ "caniuse-lite": "^1.0.30001716",
+ "electron-to-chromium": "^1.5.149",
"node-releases": "^2.0.19",
- "update-browserslist-db": "^1.1.1"
+ "update-browserslist-db": "^1.1.3"
},
"bin": {
"browserslist": "cli.js"
@@ -437,6 +513,47 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -447,9 +564,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001709",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001709.tgz",
- "integrity": "sha512-NgL3vUTnDrPCZ3zTahp4fsugQ4dc7EKTSzwQDPEel6DMoMnfH2jhry9n2Zm8onbSR+f/QtKHFOA+iAQu4kbtWA==",
+ "version": "1.0.30001717",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz",
+ "integrity": "sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw==",
"funding": [
{
"type": "opencollective",
@@ -481,6 +598,7 @@
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz",
"integrity": "sha512-we+NuQo2DHhSl+DP6jlUiAhyAjBQrYnpOk15rN6c6JSPScjiCLh8IbSU+VTcph6YS3o7mASE8a0+gbZ7ChLpgg==",
+ "license": "MIT",
"dependencies": {
"for-own": "^0.1.3",
"is-plain-object": "^2.0.1",
@@ -515,6 +633,63 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
+ "node_modules/content-disposition": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
+ "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
+ "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.6.0"
+ }
+ },
+ "node_modules/cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
"node_modules/cross-env": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
@@ -546,11 +721,12 @@
}
},
"node_modules/debug": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "license": "MIT",
"dependencies": {
- "ms": "2.1.2"
+ "ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
@@ -571,10 +747,21 @@
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/dot-prop": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz",
@@ -591,9 +778,10 @@
}
},
"node_modules/dotenv": {
- "version": "16.4.7",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
- "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
+ "version": "16.5.0",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
+ "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
+ "license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
@@ -601,12 +789,44 @@
"url": "https://dotenvx.com"
}
},
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/electron-to-chromium": {
- "version": "1.5.130",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.130.tgz",
- "integrity": "sha512-Ou2u7L9j2XLZbhqzyX0jWDj6gA8D3jIfVzt4rikLf3cGBa0VdReuFimBKS9tQJA4+XpeCxj1NoWlfBXzbMa9IA==",
+ "version": "1.5.150",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.150.tgz",
+ "integrity": "sha512-rOOkP2ZUMx1yL4fCxXQKDHQ8ZXwisb2OycOQVKHgvB3ZI4CvehOd4y2tfnnLDieJ3Zs1RL1Dlp3cMkyIn7nnXA==",
"license": "ISC"
},
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/enquirer": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz",
@@ -619,6 +839,39 @@
"node": ">=8.6"
}
},
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
@@ -628,6 +881,13 @@
"node": ">=6"
}
},
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -641,21 +901,24 @@
}
},
"node_modules/eslint": {
- "version": "9.21.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz",
- "integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==",
+ "version": "9.26.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.26.0.tgz",
+ "integrity": "sha512-Hx0MOjPh6uK9oq9nVsATZKE/Wlbai7KFjfCuw9UHaguDW3x+HF0O5nIi3ud39TWgrTjTO5nHxmL3R1eANinWHQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1",
- "@eslint/config-array": "^0.19.2",
- "@eslint/core": "^0.12.0",
- "@eslint/eslintrc": "^3.3.0",
- "@eslint/js": "9.21.0",
- "@eslint/plugin-kit": "^0.2.7",
+ "@eslint/config-array": "^0.20.0",
+ "@eslint/config-helpers": "^0.2.1",
+ "@eslint/core": "^0.13.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.26.0",
+ "@eslint/plugin-kit": "^0.2.8",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",
+ "@modelcontextprotocol/sdk": "^1.8.0",
"@types/estree": "^1.0.6",
"@types/json-schema": "^7.0.15",
"ajv": "^6.12.4",
@@ -663,7 +926,7 @@
"cross-spawn": "^7.0.6",
"debug": "^4.3.2",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^8.2.0",
+ "eslint-scope": "^8.3.0",
"eslint-visitor-keys": "^4.2.0",
"espree": "^10.3.0",
"esquery": "^1.5.0",
@@ -679,7 +942,8 @@
"lodash.merge": "^4.6.2",
"minimatch": "^3.1.2",
"natural-compare": "^1.4.0",
- "optionator": "^0.9.3"
+ "optionator": "^0.9.3",
+ "zod": "^3.24.2"
},
"bin": {
"eslint": "bin/eslint.js"
@@ -700,10 +964,11 @@
}
},
"node_modules/eslint-scope": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz",
- "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==",
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz",
+ "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==",
"dev": true,
+ "license": "BSD-2-Clause",
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
@@ -841,6 +1106,98 @@
"node": ">=0.10.0"
}
},
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/eventsource": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
+ "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eventsource-parser": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/eventsource-parser": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.2.tgz",
+ "integrity": "sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/express": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
+ "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "^2.0.0",
+ "body-parser": "^2.2.0",
+ "content-disposition": "^1.0.0",
+ "content-type": "^1.0.5",
+ "cookie": "^0.7.1",
+ "cookie-signature": "^1.2.1",
+ "debug": "^4.4.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "finalhandler": "^2.1.0",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.0",
+ "merge-descriptors": "^2.0.0",
+ "mime-types": "^3.0.0",
+ "on-finished": "^2.4.1",
+ "once": "^1.4.0",
+ "parseurl": "^1.3.3",
+ "proxy-addr": "^2.0.7",
+ "qs": "^6.14.0",
+ "range-parser": "^1.2.1",
+ "router": "^2.2.0",
+ "send": "^1.1.0",
+ "serve-static": "^2.2.0",
+ "statuses": "^2.0.1",
+ "type-is": "^2.0.1",
+ "vary": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/express-rate-limit": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz",
+ "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/express-rate-limit"
+ },
+ "peerDependencies": {
+ "express": "^4.11 || 5 || ^5.0.0-beta.1"
+ }
+ },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -874,6 +1231,24 @@
"node": ">=16.0.0"
}
},
+ "node_modules/finalhandler": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
+ "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "on-finished": "^2.4.1",
+ "parseurl": "^1.3.3",
+ "statuses": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/find-up": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
@@ -891,13 +1266,13 @@
}
},
"node_modules/fingerprint-generator": {
- "version": "2.1.63",
- "resolved": "https://registry.npmjs.org/fingerprint-generator/-/fingerprint-generator-2.1.63.tgz",
- "integrity": "sha512-9ud3dO2aD0wKc9/zEAletQZ/iPuGqTwmkA7Y33OeOv/N+OvTM6OStxXbNLCsGSD7jUYe4ei/TK9uXwiQntbZGA==",
+ "version": "2.1.66",
+ "resolved": "https://registry.npmjs.org/fingerprint-generator/-/fingerprint-generator-2.1.66.tgz",
+ "integrity": "sha512-2CvoY+OPcCOWkoIMQim80uNH+ED1+2rM9QXIcSih7ovBMLOmyr3Sp9IOtfccd05QlGDzulU2M9Oav8jOgTlCBA==",
"license": "Apache-2.0",
"dependencies": {
- "generative-bayesian-network": "^2.1.63",
- "header-generator": "^2.1.63",
+ "generative-bayesian-network": "^2.1.66",
+ "header-generator": "^2.1.66",
"tslib": "^2.4.0"
},
"engines": {
@@ -905,12 +1280,12 @@
}
},
"node_modules/fingerprint-injector": {
- "version": "2.1.63",
- "resolved": "https://registry.npmjs.org/fingerprint-injector/-/fingerprint-injector-2.1.63.tgz",
- "integrity": "sha512-OZpxOKi4iyU2hzqXuJDddb5ayfLgWJvnaDwWAExC5/P+XuXbAMf9cLIOAEQ3B2SnAbXUXZw6k8vQqeg6vSrWIA==",
+ "version": "2.1.66",
+ "resolved": "https://registry.npmjs.org/fingerprint-injector/-/fingerprint-injector-2.1.66.tgz",
+ "integrity": "sha512-h5llsoG0xoDeEo2czjzvo1niEU0xgCMwhs5/jtAxiBf7IiP/wW1Is3DJMEB+4V4PwIvqNQqLlnD07X23D7tErA==",
"license": "Apache-2.0",
"dependencies": {
- "fingerprint-generator": "^2.1.63",
+ "fingerprint-generator": "^2.1.66",
"tslib": "^2.4.0"
},
"engines": {
@@ -954,6 +1329,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
"integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
@@ -962,6 +1338,7 @@
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz",
"integrity": "sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw==",
+ "license": "MIT",
"dependencies": {
"for-in": "^1.0.1"
},
@@ -969,10 +1346,31 @@
"node": ">=0.10.0"
}
},
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
+ "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
@@ -985,22 +1383,74 @@
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "license": "ISC"
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
"node_modules/generative-bayesian-network": {
- "version": "2.1.63",
- "resolved": "https://registry.npmjs.org/generative-bayesian-network/-/generative-bayesian-network-2.1.63.tgz",
- "integrity": "sha512-nH1t4R9nlWSmvFoI4DEcpXd0+yoGZcySVuUBkXhR09/Mf7O9AWFmR8lWqVGIoxpBS0WRNpV/qj4swKGGQAxAPQ==",
+ "version": "2.1.66",
+ "resolved": "https://registry.npmjs.org/generative-bayesian-network/-/generative-bayesian-network-2.1.66.tgz",
+ "integrity": "sha512-gbBsyaaEJj/LHp3473TQrMDdcKiRzI8Sn2CbcG/lwONZkp0n9/ChC1mjzcbZQtHHCuqjn+JouSSbzLeepUMbuw==",
"license": "Apache-2.0",
"dependencies": {
"adm-zip": "^0.5.9",
"tslib": "^2.4.0"
}
},
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -1041,10 +1491,24 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
- "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "license": "ISC"
},
"node_modules/has-flag": {
"version": "4.0.0",
@@ -1055,14 +1519,40 @@
"node": ">=8"
}
},
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/header-generator": {
- "version": "2.1.63",
- "resolved": "https://registry.npmjs.org/header-generator/-/header-generator-2.1.63.tgz",
- "integrity": "sha512-dicAWZb/zXmI3fPuCw1Ra1sTTC9x9cc5EOhwugmI/keXnRc3BzxDQTGHge+MeAdqhpo2ZOFTEV6u08lXJUA2uQ==",
+ "version": "2.1.66",
+ "resolved": "https://registry.npmjs.org/header-generator/-/header-generator-2.1.66.tgz",
+ "integrity": "sha512-g0jd79o0CyzyK0Jega4pAG1eJhykhPNfBLpOnUINtX2YkToVvRSBZ+B2wtmIjqwKHXK8DNWxylKuXnZmLs1yMQ==",
"license": "Apache-2.0",
"dependencies": {
"browserslist": "^4.21.1",
- "generative-bayesian-network": "^2.1.63",
+ "generative-bayesian-network": "^2.1.66",
"ow": "^0.28.1",
"tslib": "^2.4.0"
},
@@ -1070,10 +1560,40 @@
"node": ">=16.0.0"
}
},
+ "node_modules/http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/ignore": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
- "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1081,9 +1601,9 @@
}
},
"node_modules/import-fresh": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
- "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1110,6 +1630,8 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "license": "ISC",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
@@ -1118,17 +1640,30 @@
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
},
"node_modules/is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "license": "MIT"
},
"node_modules/is-extendable": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
"integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
@@ -1167,6 +1702,7 @@
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
"integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "license": "MIT",
"dependencies": {
"isobject": "^3.0.1"
},
@@ -1174,6 +1710,13 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-promise": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
+ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -1183,6 +1726,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
"integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
@@ -1224,6 +1768,7 @@
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
@@ -1245,6 +1790,7 @@
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "license": "MIT",
"dependencies": {
"is-buffer": "^1.1.5"
},
@@ -1256,6 +1802,7 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
"integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
@@ -1316,10 +1863,31 @@
"url": "https://github.com/sponsors/typicode"
}
},
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
+ "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/merge-deep": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/merge-deep/-/merge-deep-3.0.3.tgz",
"integrity": "sha512-qtmzAS6t6grwEkNrunqTBdn0qKwFgNWvlxUbAV8es9M7Ot1EbyApytCnvE0jALPa46ZpKDUo527kKiaWplmlFA==",
+ "license": "MIT",
"dependencies": {
"arr-union": "^3.1.0",
"clone-deep": "^0.2.4",
@@ -1329,6 +1897,42 @@
"node": ">=0.10.0"
}
},
+ "node_modules/merge-descriptors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
+ "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.54.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
+ "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -1344,6 +1948,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz",
"integrity": "sha512-ALGF1Jt9ouehcaXaHhn6t1yGWRqGaHkPFndtFVHfZXOvkIZ/yoGaSi0AHVTafb3ZBGg4dr/bDwnaEKqCXzchMA==",
+ "license": "MIT",
"dependencies": {
"for-in": "^0.1.3",
"is-extendable": "^0.1.1"
@@ -1356,14 +1961,16 @@
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz",
"integrity": "sha512-F0to7vbBSHP8E3l6dCjxNOLuSFAACIxFy3UehTUlG7svlXi37HHsDkyVcHo0Pq8QwrE+pXvWSVX3ZT1T9wAZ9g==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
},
"node_modules/natural-compare": {
"version": "1.4.0",
@@ -1371,16 +1978,63 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true
},
+ "node_modules/negotiator": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
+ "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/node-releases": {
"version": "2.0.19",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
"license": "MIT"
},
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "license": "ISC",
"dependencies": {
"wrappy": "1"
}
@@ -1474,6 +2128,16 @@
"node": ">=6"
}
},
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -1487,6 +2151,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
@@ -1499,16 +2164,36 @@
"node": ">=8"
}
},
+ "node_modules/path-to-regexp": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
+ "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ }
+ },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC"
},
+ "node_modules/pkce-challenge": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz",
+ "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
"node_modules/playwright-core": {
- "version": "1.45.0",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.0.tgz",
- "integrity": "sha512-lZmHlFQ0VYSpAs43dRq1/nJ9G/6SiTI7VPqidld9TDefL9tX87bTKExWZZUF5PeRyqtXqd8fQi2qmfIedkwsNQ==",
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.52.0.tgz",
+ "integrity": "sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==",
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
@@ -1518,13 +2203,13 @@
}
},
"node_modules/playwright-firefox": {
- "version": "1.45.0",
- "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.45.0.tgz",
- "integrity": "sha512-JmGESfFR8xTjAYQzECYO00yBbSSnu4dBImsrmJVeOXTvT+i9p1dpVUaxKz6lTFMI/xzYROqB4E4Km8NBiOgslw==",
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.52.0.tgz",
+ "integrity": "sha512-x4niD4HHffaYNmz0XwgXqvf7NUhCmAbcpa+DX16kAacd+6T8cwf+W/+Q0S+VLsYpQ2BJowR2HuJanO1Ge1Phzg==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
- "playwright-core": "1.45.0"
+ "playwright-core": "1.52.0"
},
"bin": {
"playwright": "cli.js"
@@ -1542,6 +2227,20 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -1556,6 +2255,7 @@
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/puppeteer-extra-plugin/-/puppeteer-extra-plugin-3.2.3.tgz",
"integrity": "sha512-6RNy0e6pH8vaS3akPIKGg28xcryKscczt4wIl0ePciZENGE2yoaQJNd17UiEbdmh5/6WW6dPcfRWT9lxBwCi2Q==",
+ "license": "MIT",
"dependencies": {
"@types/debug": "^4.1.0",
"debug": "^4.1.1",
@@ -1581,6 +2281,7 @@
"version": "2.11.2",
"resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-stealth/-/puppeteer-extra-plugin-stealth-2.11.2.tgz",
"integrity": "sha512-bUemM5XmTj9i2ZerBzsk2AN5is0wHMNE6K0hXBzBXOzP5m5G3Wl0RHhiqKeHToe/uIH8AoZiGhc1tCkLZQPKTQ==",
+ "license": "MIT",
"dependencies": {
"debug": "^4.1.1",
"puppeteer-extra-plugin": "^3.2.3",
@@ -1606,6 +2307,7 @@
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-user-data-dir/-/puppeteer-extra-plugin-user-data-dir-2.4.1.tgz",
"integrity": "sha512-kH1GnCcqEDoBXO7epAse4TBPJh9tEpVEK/vkedKfjOVOhZAvLkHGc9swMs5ChrJbRnf8Hdpug6TJlEuimXNQ+g==",
+ "license": "MIT",
"dependencies": {
"debug": "^4.1.1",
"fs-extra": "^10.0.0",
@@ -1632,6 +2334,7 @@
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-user-preferences/-/puppeteer-extra-plugin-user-preferences-2.4.1.tgz",
"integrity": "sha512-i1oAZxRbc1bk8MZufKCruCEC3CCafO9RKMkkodZltI4OqibLFXF3tj6HZ4LZ9C5vCXZjYcDWazgtY69mnmrQ9A==",
+ "license": "MIT",
"dependencies": {
"debug": "^4.1.1",
"deepmerge": "^4.2.2",
@@ -1654,6 +2357,48 @@
}
}
},
+ "node_modules/qs": {
+ "version": "6.14.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
+ "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
+ "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "3.1.2",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.6.3",
+ "unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@@ -1668,6 +2413,8 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "license": "ISC",
"dependencies": {
"glob": "^7.1.3"
},
@@ -1678,10 +2425,102 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/router": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
+ "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "depd": "^2.0.0",
+ "is-promise": "^4.0.0",
+ "parseurl": "^1.3.3",
+ "path-to-regexp": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/send": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
+ "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.3.5",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.0",
+ "mime-types": "^3.0.1",
+ "ms": "^2.1.3",
+ "on-finished": "^2.4.1",
+ "range-parser": "^1.2.1",
+ "statuses": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/serve-static": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
+ "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "parseurl": "^1.3.3",
+ "send": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/shallow-clone": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz",
"integrity": "sha512-J1zdXCky5GmNnuauESROVu31MQSnLoYvlyEn6j2Ztk6Q5EHFIhxkMhYcv6vuDzl2XEzoRr856QwzMgWM/TmZgw==",
+ "license": "MIT",
"dependencies": {
"is-extendable": "^0.1.1",
"kind-of": "^2.0.1",
@@ -1696,6 +2535,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz",
"integrity": "sha512-0u8i1NZ/mg0b+W3MGGw5I7+6Eib2nx72S/QvXa0hYjEkjTknYmEYQJwGu3mLC0BrhtJjtQafTkyRUQ75Kx0LVg==",
+ "license": "MIT",
"dependencies": {
"is-buffer": "^1.0.2"
},
@@ -1707,6 +2547,7 @@
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz",
"integrity": "sha512-gkX52wvU/R8DVMMt78ATVPFMJqfW8FPz1GZ1sVHBVQHmu/WvhIWE4cE1GBzhJNFicDeYhnwp6Rl35BcAIM3YOQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
@@ -1730,6 +2571,92 @@
"node": ">=8"
}
},
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/steno": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/steno/-/steno-4.0.2.tgz",
@@ -1786,6 +2713,16 @@
"node": ">=0.2.6"
}
},
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -1804,14 +2741,40 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/type-is": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
+ "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "content-type": "^1.0.5",
+ "media-typer": "^1.1.0",
+ "mime-types": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/universalify": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
- "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
},
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/update-browserslist-db": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
@@ -1861,6 +2824,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -1878,7 +2851,8 @@
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "license": "ISC"
},
"node_modules/yocto-queue": {
"version": "0.1.0",
@@ -1891,6 +2865,26 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/zod": {
+ "version": "3.24.4",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.4.tgz",
+ "integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-to-json-schema": {
+ "version": "3.24.5",
+ "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz",
+ "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==",
+ "dev": true,
+ "license": "ISC",
+ "peerDependencies": {
+ "zod": "^3.24.1"
+ }
}
},
"dependencies": {
@@ -1916,9 +2910,9 @@
"dev": true
},
"@eslint/config-array": {
- "version": "0.19.2",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz",
- "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==",
+ "version": "0.20.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz",
+ "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==",
"dev": true,
"requires": {
"@eslint/object-schema": "^2.1.6",
@@ -1926,19 +2920,25 @@
"minimatch": "^3.1.2"
}
},
+ "@eslint/config-helpers": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz",
+ "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==",
+ "dev": true
+ },
"@eslint/core": {
- "version": "0.12.0",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz",
- "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==",
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
+ "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.15"
}
},
"@eslint/eslintrc": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz",
- "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==",
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
+ "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
"dev": true,
"requires": {
"ajv": "^6.12.4",
@@ -1953,9 +2953,9 @@
}
},
"@eslint/js": {
- "version": "9.21.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.21.0.tgz",
- "integrity": "sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==",
+ "version": "9.26.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.26.0.tgz",
+ "integrity": "sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==",
"dev": true
},
"@eslint/object-schema": {
@@ -1965,12 +2965,12 @@
"dev": true
},
"@eslint/plugin-kit": {
- "version": "0.2.7",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz",
- "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==",
+ "version": "0.2.8",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz",
+ "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==",
"dev": true,
"requires": {
- "@eslint/core": "^0.12.0",
+ "@eslint/core": "^0.13.0",
"levn": "^0.4.1"
}
},
@@ -2002,6 +3002,24 @@
"integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==",
"dev": true
},
+ "@modelcontextprotocol/sdk": {
+ "version": "1.11.3",
+ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.11.3.tgz",
+ "integrity": "sha512-rmOWVRUbUJD7iSvJugjUbFZshTAuJ48MXoZ80Osx1GM0K/H1w7rSEvmw8m6vdWxNASgtaHIhAgre4H/E9GJiYQ==",
+ "dev": true,
+ "requires": {
+ "content-type": "^1.0.5",
+ "cors": "^2.8.5",
+ "cross-spawn": "^7.0.5",
+ "eventsource": "^3.0.2",
+ "express": "^5.0.1",
+ "express-rate-limit": "^7.5.0",
+ "pkce-challenge": "^5.0.0",
+ "raw-body": "^3.0.0",
+ "zod": "^3.23.8",
+ "zod-to-json-schema": "^3.24.1"
+ }
+ },
"@otplib/core": {
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/@otplib/core/-/core-12.0.1.tgz",
@@ -2068,9 +3086,9 @@
}
},
"@types/debug": {
- "version": "4.1.8",
- "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz",
- "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==",
+ "version": "4.1.12",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
+ "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
"requires": {
"@types/ms": "*"
}
@@ -2088,9 +3106,19 @@
"dev": true
},
"@types/ms": {
- "version": "0.7.31",
- "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
- "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA=="
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="
+ },
+ "accepts": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
+ "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
+ "dev": true,
+ "requires": {
+ "mime-types": "^3.0.0",
+ "negotiator": "^1.0.0"
+ }
},
"acorn": {
"version": "8.14.1",
@@ -2157,6 +3185,23 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
+ "body-parser": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
+ "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
+ "dev": true,
+ "requires": {
+ "bytes": "^3.1.2",
+ "content-type": "^1.0.5",
+ "debug": "^4.4.0",
+ "http-errors": "^2.0.0",
+ "iconv-lite": "^0.6.3",
+ "on-finished": "^2.4.1",
+ "qs": "^6.14.0",
+ "raw-body": "^3.0.0",
+ "type-is": "^2.0.0"
+ }
+ },
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -2167,14 +3212,40 @@
}
},
"browserslist": {
- "version": "4.24.4",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
- "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
+ "version": "4.24.5",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz",
+ "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==",
"requires": {
- "caniuse-lite": "^1.0.30001688",
- "electron-to-chromium": "^1.5.73",
+ "caniuse-lite": "^1.0.30001716",
+ "electron-to-chromium": "^1.5.149",
"node-releases": "^2.0.19",
- "update-browserslist-db": "^1.1.1"
+ "update-browserslist-db": "^1.1.3"
+ }
+ },
+ "bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "dev": true
+ },
+ "call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "requires": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ }
+ },
+ "call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "dev": true,
+ "requires": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
}
},
"callsites": {
@@ -2183,9 +3254,9 @@
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
},
"caniuse-lite": {
- "version": "1.0.30001709",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001709.tgz",
- "integrity": "sha512-NgL3vUTnDrPCZ3zTahp4fsugQ4dc7EKTSzwQDPEel6DMoMnfH2jhry9n2Zm8onbSR+f/QtKHFOA+iAQu4kbtWA=="
+ "version": "1.0.30001717",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz",
+ "integrity": "sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw=="
},
"chalk": {
"version": "5.4.1",
@@ -2224,6 +3295,43 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
+ "content-disposition": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
+ "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "5.2.1"
+ }
+ },
+ "content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "dev": true
+ },
+ "cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "dev": true
+ },
+ "cookie-signature": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
+ "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
+ "dev": true
+ },
+ "cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4",
+ "vary": "^1"
+ }
+ },
"cross-env": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
@@ -2243,11 +3351,11 @@
}
},
"debug": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"requires": {
- "ms": "2.1.2"
+ "ms": "^2.1.3"
}
},
"deep-is": {
@@ -2261,6 +3369,12 @@
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
},
+ "depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "dev": true
+ },
"dot-prop": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz",
@@ -2270,14 +3384,37 @@
}
},
"dotenv": {
- "version": "16.4.7",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
- "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ=="
+ "version": "16.5.0",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
+ "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg=="
+ },
+ "dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "requires": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ }
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "dev": true
},
"electron-to-chromium": {
- "version": "1.5.130",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.130.tgz",
- "integrity": "sha512-Ou2u7L9j2XLZbhqzyX0jWDj6gA8D3jIfVzt4rikLf3cGBa0VdReuFimBKS9tQJA4+XpeCxj1NoWlfBXzbMa9IA=="
+ "version": "1.5.150",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.150.tgz",
+ "integrity": "sha512-rOOkP2ZUMx1yL4fCxXQKDHQ8ZXwisb2OycOQVKHgvB3ZI4CvehOd4y2tfnnLDieJ3Zs1RL1Dlp3cMkyIn7nnXA=="
+ },
+ "encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "dev": true
},
"enquirer": {
"version": "2.4.1",
@@ -2288,11 +3425,38 @@
"strip-ansi": "^6.0.1"
}
},
+ "es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true
+ },
+ "es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true
+ },
+ "es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dev": true,
+ "requires": {
+ "es-errors": "^1.3.0"
+ }
+ },
"escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="
},
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "dev": true
+ },
"escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -2300,21 +3464,23 @@
"dev": true
},
"eslint": {
- "version": "9.21.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz",
- "integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==",
+ "version": "9.26.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.26.0.tgz",
+ "integrity": "sha512-Hx0MOjPh6uK9oq9nVsATZKE/Wlbai7KFjfCuw9UHaguDW3x+HF0O5nIi3ud39TWgrTjTO5nHxmL3R1eANinWHQ==",
"dev": true,
"requires": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1",
- "@eslint/config-array": "^0.19.2",
- "@eslint/core": "^0.12.0",
- "@eslint/eslintrc": "^3.3.0",
- "@eslint/js": "9.21.0",
- "@eslint/plugin-kit": "^0.2.7",
+ "@eslint/config-array": "^0.20.0",
+ "@eslint/config-helpers": "^0.2.1",
+ "@eslint/core": "^0.13.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.26.0",
+ "@eslint/plugin-kit": "^0.2.8",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",
+ "@modelcontextprotocol/sdk": "^1.8.0",
"@types/estree": "^1.0.6",
"@types/json-schema": "^7.0.15",
"ajv": "^6.12.4",
@@ -2322,7 +3488,7 @@
"cross-spawn": "^7.0.6",
"debug": "^4.3.2",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^8.2.0",
+ "eslint-scope": "^8.3.0",
"eslint-visitor-keys": "^4.2.0",
"espree": "^10.3.0",
"esquery": "^1.5.0",
@@ -2338,7 +3504,8 @@
"lodash.merge": "^4.6.2",
"minimatch": "^3.1.2",
"natural-compare": "^1.4.0",
- "optionator": "^0.9.3"
+ "optionator": "^0.9.3",
+ "zod": "^3.24.2"
},
"dependencies": {
"@humanwhocodes/retry": {
@@ -2366,9 +3533,9 @@
}
},
"eslint-scope": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz",
- "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==",
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz",
+ "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==",
"dev": true,
"requires": {
"esrecurse": "^4.3.0",
@@ -2430,6 +3597,69 @@
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true
},
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "dev": true
+ },
+ "eventsource": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
+ "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
+ "dev": true,
+ "requires": {
+ "eventsource-parser": "^3.0.1"
+ }
+ },
+ "eventsource-parser": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.2.tgz",
+ "integrity": "sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA==",
+ "dev": true
+ },
+ "express": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
+ "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
+ "dev": true,
+ "requires": {
+ "accepts": "^2.0.0",
+ "body-parser": "^2.2.0",
+ "content-disposition": "^1.0.0",
+ "content-type": "^1.0.5",
+ "cookie": "^0.7.1",
+ "cookie-signature": "^1.2.1",
+ "debug": "^4.4.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "finalhandler": "^2.1.0",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.0",
+ "merge-descriptors": "^2.0.0",
+ "mime-types": "^3.0.0",
+ "on-finished": "^2.4.1",
+ "once": "^1.4.0",
+ "parseurl": "^1.3.3",
+ "proxy-addr": "^2.0.7",
+ "qs": "^6.14.0",
+ "range-parser": "^1.2.1",
+ "router": "^2.2.0",
+ "send": "^1.1.0",
+ "serve-static": "^2.2.0",
+ "statuses": "^2.0.1",
+ "type-is": "^2.0.1",
+ "vary": "^1.1.2"
+ }
+ },
+ "express-rate-limit": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz",
+ "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==",
+ "dev": true,
+ "requires": {}
+ },
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -2457,6 +3687,20 @@
"flat-cache": "^4.0.0"
}
},
+ "finalhandler": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
+ "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.4.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "on-finished": "^2.4.1",
+ "parseurl": "^1.3.3",
+ "statuses": "^2.0.1"
+ }
+ },
"find-up": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
@@ -2468,21 +3712,21 @@
}
},
"fingerprint-generator": {
- "version": "2.1.63",
- "resolved": "https://registry.npmjs.org/fingerprint-generator/-/fingerprint-generator-2.1.63.tgz",
- "integrity": "sha512-9ud3dO2aD0wKc9/zEAletQZ/iPuGqTwmkA7Y33OeOv/N+OvTM6OStxXbNLCsGSD7jUYe4ei/TK9uXwiQntbZGA==",
+ "version": "2.1.66",
+ "resolved": "https://registry.npmjs.org/fingerprint-generator/-/fingerprint-generator-2.1.66.tgz",
+ "integrity": "sha512-2CvoY+OPcCOWkoIMQim80uNH+ED1+2rM9QXIcSih7ovBMLOmyr3Sp9IOtfccd05QlGDzulU2M9Oav8jOgTlCBA==",
"requires": {
- "generative-bayesian-network": "^2.1.63",
- "header-generator": "^2.1.63",
+ "generative-bayesian-network": "^2.1.66",
+ "header-generator": "^2.1.66",
"tslib": "^2.4.0"
}
},
"fingerprint-injector": {
- "version": "2.1.63",
- "resolved": "https://registry.npmjs.org/fingerprint-injector/-/fingerprint-injector-2.1.63.tgz",
- "integrity": "sha512-OZpxOKi4iyU2hzqXuJDddb5ayfLgWJvnaDwWAExC5/P+XuXbAMf9cLIOAEQ3B2SnAbXUXZw6k8vQqeg6vSrWIA==",
+ "version": "2.1.66",
+ "resolved": "https://registry.npmjs.org/fingerprint-injector/-/fingerprint-injector-2.1.66.tgz",
+ "integrity": "sha512-h5llsoG0xoDeEo2czjzvo1niEU0xgCMwhs5/jtAxiBf7IiP/wW1Is3DJMEB+4V4PwIvqNQqLlnD07X23D7tErA==",
"requires": {
- "fingerprint-generator": "^2.1.63",
+ "fingerprint-generator": "^2.1.66",
"tslib": "^2.4.0"
}
},
@@ -2515,6 +3759,18 @@
"for-in": "^1.0.1"
}
},
+ "forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "dev": true
+ },
+ "fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
+ "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
+ "dev": true
+ },
"fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
@@ -2530,15 +3786,49 @@
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
+ "function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true
+ },
"generative-bayesian-network": {
- "version": "2.1.63",
- "resolved": "https://registry.npmjs.org/generative-bayesian-network/-/generative-bayesian-network-2.1.63.tgz",
- "integrity": "sha512-nH1t4R9nlWSmvFoI4DEcpXd0+yoGZcySVuUBkXhR09/Mf7O9AWFmR8lWqVGIoxpBS0WRNpV/qj4swKGGQAxAPQ==",
+ "version": "2.1.66",
+ "resolved": "https://registry.npmjs.org/generative-bayesian-network/-/generative-bayesian-network-2.1.66.tgz",
+ "integrity": "sha512-gbBsyaaEJj/LHp3473TQrMDdcKiRzI8Sn2CbcG/lwONZkp0n9/ChC1mjzcbZQtHHCuqjn+JouSSbzLeepUMbuw==",
"requires": {
"adm-zip": "^0.5.9",
"tslib": "^2.4.0"
}
},
+ "get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "requires": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ }
+ },
+ "get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "requires": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ }
+ },
"glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -2567,6 +3857,12 @@
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"dev": true
},
+ "gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true
+ },
"graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -2578,27 +3874,64 @@
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
+ "has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true
+ },
+ "hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.2"
+ }
+ },
"header-generator": {
- "version": "2.1.63",
- "resolved": "https://registry.npmjs.org/header-generator/-/header-generator-2.1.63.tgz",
- "integrity": "sha512-dicAWZb/zXmI3fPuCw1Ra1sTTC9x9cc5EOhwugmI/keXnRc3BzxDQTGHge+MeAdqhpo2ZOFTEV6u08lXJUA2uQ==",
+ "version": "2.1.66",
+ "resolved": "https://registry.npmjs.org/header-generator/-/header-generator-2.1.66.tgz",
+ "integrity": "sha512-g0jd79o0CyzyK0Jega4pAG1eJhykhPNfBLpOnUINtX2YkToVvRSBZ+B2wtmIjqwKHXK8DNWxylKuXnZmLs1yMQ==",
"requires": {
"browserslist": "^4.21.1",
- "generative-bayesian-network": "^2.1.63",
+ "generative-bayesian-network": "^2.1.66",
"ow": "^0.28.1",
"tslib": "^2.4.0"
}
},
+ "http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "dev": true,
+ "requires": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ }
+ },
"ignore": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
- "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
"dev": true
},
"import-fresh": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
- "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
"dev": true,
"requires": {
"parent-module": "^1.0.0",
@@ -2625,6 +3958,12 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
+ "ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "dev": true
+ },
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
@@ -2663,6 +4002,12 @@
"isobject": "^3.0.1"
}
},
+ "is-promise": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
+ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
+ "dev": true
+ },
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -2769,6 +4114,18 @@
"steno": "^4.0.2"
}
},
+ "math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true
+ },
+ "media-typer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
+ "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
+ "dev": true
+ },
"merge-deep": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/merge-deep/-/merge-deep-3.0.3.tgz",
@@ -2779,6 +4136,27 @@
"kind-of": "^3.0.2"
}
},
+ "merge-descriptors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
+ "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
+ "dev": true
+ },
+ "mime-db": {
+ "version": "1.54.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
+ "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
+ "dev": true,
+ "requires": {
+ "mime-db": "^1.54.0"
+ }
+ },
"minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -2804,9 +4182,9 @@
}
},
"ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"natural-compare": {
"version": "1.4.0",
@@ -2814,11 +4192,38 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true
},
+ "negotiator": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
+ "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
+ "dev": true
+ },
"node-releases": {
"version": "2.0.19",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="
},
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true
+ },
+ "object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "dev": true
+ },
+ "on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "dev": true,
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -2890,6 +4295,12 @@
"callsites": "^3.0.0"
}
},
+ "parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "dev": true
+ },
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -2906,22 +4317,34 @@
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
+ "path-to-regexp": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
+ "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
+ "dev": true
+ },
"picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
},
+ "pkce-challenge": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz",
+ "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==",
+ "dev": true
+ },
"playwright-core": {
- "version": "1.45.0",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.0.tgz",
- "integrity": "sha512-lZmHlFQ0VYSpAs43dRq1/nJ9G/6SiTI7VPqidld9TDefL9tX87bTKExWZZUF5PeRyqtXqd8fQi2qmfIedkwsNQ=="
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.52.0.tgz",
+ "integrity": "sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg=="
},
"playwright-firefox": {
- "version": "1.45.0",
- "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.45.0.tgz",
- "integrity": "sha512-JmGESfFR8xTjAYQzECYO00yBbSSnu4dBImsrmJVeOXTvT+i9p1dpVUaxKz6lTFMI/xzYROqB4E4Km8NBiOgslw==",
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/playwright-firefox/-/playwright-firefox-1.52.0.tgz",
+ "integrity": "sha512-x4niD4HHffaYNmz0XwgXqvf7NUhCmAbcpa+DX16kAacd+6T8cwf+W/+Q0S+VLsYpQ2BJowR2HuJanO1Ge1Phzg==",
"requires": {
- "playwright-core": "1.45.0"
+ "playwright-core": "1.52.0"
}
},
"prelude-ls": {
@@ -2930,6 +4353,16 @@
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true
},
+ "proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "dev": true,
+ "requires": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ }
+ },
"punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -2978,6 +4411,33 @@
"puppeteer-extra-plugin-user-data-dir": "^2.4.1"
}
},
+ "qs": {
+ "version": "6.14.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
+ "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
+ "dev": true,
+ "requires": {
+ "side-channel": "^1.1.0"
+ }
+ },
+ "range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "dev": true
+ },
+ "raw-body": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
+ "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
+ "dev": true,
+ "requires": {
+ "bytes": "3.1.2",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.6.3",
+ "unpipe": "1.0.0"
+ }
+ },
"resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@@ -2992,6 +4452,68 @@
"glob": "^7.1.3"
}
},
+ "router": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
+ "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.4.0",
+ "depd": "^2.0.0",
+ "is-promise": "^4.0.0",
+ "parseurl": "^1.3.3",
+ "path-to-regexp": "^8.0.0"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "send": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
+ "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.3.5",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.0",
+ "mime-types": "^3.0.1",
+ "ms": "^2.1.3",
+ "on-finished": "^2.4.1",
+ "range-parser": "^1.2.1",
+ "statuses": "^2.0.1"
+ }
+ },
+ "serve-static": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
+ "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
+ "dev": true,
+ "requires": {
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "parseurl": "^1.3.3",
+ "send": "^1.2.0"
+ }
+ },
+ "setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "dev": true
+ },
"shallow-clone": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz",
@@ -3031,6 +4553,60 @@
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
},
+ "side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "dev": true,
+ "requires": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ }
+ },
+ "side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "dev": true,
+ "requires": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ }
+ },
+ "side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "dev": true,
+ "requires": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ }
+ },
+ "side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dev": true,
+ "requires": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ }
+ },
+ "statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "dev": true
+ },
"steno": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/steno/-/steno-4.0.2.tgz",
@@ -3064,6 +4640,12 @@
"resolved": "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz",
"integrity": "sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA=="
},
+ "toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "dev": true
+ },
"tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -3078,10 +4660,27 @@
"prelude-ls": "^1.2.1"
}
},
+ "type-is": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
+ "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
+ "dev": true,
+ "requires": {
+ "content-type": "^1.0.5",
+ "media-typer": "^1.1.0",
+ "mime-types": "^3.0.0"
+ }
+ },
"universalify": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
- "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ=="
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "dev": true
},
"update-browserslist-db": {
"version": "1.1.3",
@@ -3106,6 +4705,12 @@
"resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz",
"integrity": "sha512-sgECfZthyaCKW10N0fm27cg8HYTFK5qMWgypqkXMQ4Wbl/zZKx7xZICgcoxIIE+WFAP/MBL2EFwC/YvLxw3Zeg=="
},
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "dev": true
+ },
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -3124,6 +4729,19 @@
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"dev": true
+ },
+ "zod": {
+ "version": "3.24.4",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.4.tgz",
+ "integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==",
+ "dev": true
+ },
+ "zod-to-json-schema": {
+ "version": "3.24.5",
+ "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz",
+ "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==",
+ "dev": true,
+ "requires": {}
}
}
}
diff --git a/package.json b/package.json
index 95027a2..0487277 100644
--- a/package.json
+++ b/package.json
@@ -20,18 +20,18 @@
"node": ">=17"
},
"dependencies": {
- "chalk": "^5.3.0",
+ "chalk": "^5.4.1",
"cross-env": "^7.0.3",
- "dotenv": "^16.4.5",
+ "dotenv": "^16.5.0",
"enquirer": "^2.4.1",
- "fingerprint-injector": "^2.1.52",
+ "fingerprint-injector": "^2.1.66",
"lowdb": "^7.0.1",
"otplib": "^12.0.1",
- "playwright-firefox": "^1.45.0",
+ "playwright-firefox": "^1.52.0",
"puppeteer-extra-plugin-stealth": "^2.11.2"
},
"devDependencies": {
- "@stylistic/eslint-plugin-js": "^4.0.0",
- "eslint": "^9.5.0"
+ "@stylistic/eslint-plugin-js": "^4.2.0",
+ "eslint": "^9.26.0"
}
}
From 6adc7e529a63dddc74663e5b5903adabd651b5f5 Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Fri, 16 May 2025 00:05:48 +0200
Subject: [PATCH 241/253] disable Renovate for now
---
renovate.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/renovate.json b/renovate.json
index 5db72dd..ecfd5ff 100644
--- a/renovate.json
+++ b/renovate.json
@@ -1,5 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
+ "enabled": false,
"extends": [
"config:recommended"
]
From cea5b11c955cc7fd49dcf9e6ddcf924b922a7e8f Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Fri, 16 May 2025 00:06:39 +0200
Subject: [PATCH 242/253] mv renovate.json .github/
---
renovate.json => .github/renovate.json | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename renovate.json => .github/renovate.json (100%)
diff --git a/renovate.json b/.github/renovate.json
similarity index 100%
rename from renovate.json
rename to .github/renovate.json
From 991e0a1449ab7b931be467ee00c459b2679e1eca Mon Sep 17 00:00:00 2001
From: Ralf Vogler
Date: Fri, 16 May 2025 00:14:10 +0200
Subject: [PATCH 243/253] docker: only build branches main, dev
Otherwise branches will accumulate in 'Recent tagged image version'
https://github.com/vogler/free-games-claimer/pkgs/container/free-games-claimer
---
.github/workflows/docker.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 2c42006..8c12487 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -1,9 +1,9 @@
name: Build and push Docker image (amd64, arm64 to hub.docker.com and ghcr.io)
on:
- workflow_dispatch: # allow manual trigger
- push: # build for each branch
- # branches: ["main"]
+ workflow_dispatch: # allows manual trigger
+ push: # push on branch
+ branches: [main, dev]
paths: # ignore changes to .md files
- '**'
- '!*.md'
From faf22aafb1db5c51af6bbad32abc2be2ad1274a3 Mon Sep 17 00:00:00 2001
From: nocci
Date: Mon, 29 Dec 2025 11:41:09 +0000
Subject: [PATCH 244/253] =?UTF-8?q?=F0=9F=90=9B=20fix(prime-gaming):=20upd?=
=?UTF-8?q?ate=20URL=20and=20selectors=20for=20claims?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- change URL_CLAIM to point to new Luna claims home
- update selectors for sign-in and user verification
- improve handling of cookies acceptance
✨ feat(prime-gaming): enhance game claiming logic
- add support for new layout and game list detection
- implement flexible scrolling for loading all game cards
- refine logic for internal and external game claims
- improve store identification for external claims
♻️ refactor(prime-gaming): modularize game tab and list location
- extract functions for opening games tab and locating games list
- enhance code readability and maintainability
🐛 fix(prime-gaming): handle dynamic selectors for availability dates
- support multiple selectors for availability date detection
- improve error handling and logging for missing elements
---
prime-gaming.js | 156 ++++++++++++++++++++++++++++++++++++++----------
1 file changed, 125 insertions(+), 31 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index dada15f..8e755b8 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -7,7 +7,7 @@ import { cfg } from './src/config.js';
const screenshot = (...a) => resolve(cfg.dir.screenshots, 'prime-gaming', ...a);
// 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';
+const URL_CLAIM = 'https://luna.amazon.com/claims/home';
console.log(datetime(), 'started checking prime-gaming');
@@ -40,9 +40,14 @@ let user;
try {
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 Promise.any([
+ 'button:has-text("Sign in")',
+ 'button:has-text("Anmelden")',
+ '[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?
- while (await page.locator('button:has-text("Sign in")').count() > 0) {
+ 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")');
if (!cfg.debug) context.setDefaultTimeout(cfg.login_timeout); // give user some extra time to log in
@@ -85,7 +90,7 @@ try {
await page.waitForURL('https://gaming.amazon.com/home?signedIn=true');
if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
}
- user = await page.locator('[data-a-target="user-dropdown-first-name-text"]').first().innerText();
+ user = await page.locator('[data-a-target="user-dropdown-first-name-text"], [data-testid="user-dropdown-first-name-text"]').first().innerText();
console.log(`Signed in as ${user}`);
// await page.click('button[aria-label="User dropdown and more options"]');
// const twitch = await page.locator('[data-a-target="TwitchDisplayName"]').first().innerText();
@@ -119,15 +124,83 @@ try {
await page.waitForTimeout(3000);
});
- await page.click('button[data-type="Game"]');
- const games = page.locator('div[data-a-target="offer-list-FGWP_FULL"]');
- await games.waitFor();
- // await scrollUntilStable(() => games.locator('.item-card__action').count()); // number of games
- await scrollUntilStable(() => page.evaluate(() => document.querySelector('.tw-full-width').scrollHeight)); // height may change during loading while number of games is still the same?
- console.log('Number of already claimed games (total):', await games.locator('p:has-text("Collected")').count());
- // can't use .all() since the list of elements via locator will change after click while we iterate over it
- const internal = await games.locator('.item-card__action:has(button[data-a-target="FGWPOffer"])').elementHandles();
- const external = await games.locator('.item-card__action:has(a[data-a-target="FGWPOffer"])').all();
+ const openGamesTab = async () => {
+ const selectors = [
+ 'button[data-type="Game"]', // old layout
+ 'button:has-text("Games")',
+ '[data-test-selector="category-picker"] button:has-text("Games")',
+ '[data-testid="category-picker"] button:has-text("Games")',
+ ];
+ for (const sel of selectors) {
+ const btn = page.locator(sel).first();
+ if (await btn.count()) {
+ await btn.click();
+ return;
+ }
+ }
+ // New Luna claims home already shows games list
+ };
+
+ await openGamesTab();
+
+ const locateGamesList = async () => {
+ const selectors = [
+ 'div[data-a-target="offer-list-FGWP_FULL"]', // old layout
+ '[data-testid="offer-list"]',
+ '[data-test-selector="offer-list"]',
+ 'section:has(h2:has-text("Games with Prime"))',
+ 'section:has(h2:has-text("Games"))',
+ ];
+ for (const sel of selectors) {
+ const loc = page.locator(sel).first();
+ if (await loc.count()) return loc;
+ }
+ return null;
+ };
+
+ 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));
+
+ const cards = [];
+ const anchorClaims = page.locator('a[href*="/claims/"][href*="amzn1.pg.item"]');
+ if (await anchorClaims.count()) {
+ const hrefs = [...new Set(await anchorClaims.evaluateAll(anchors => anchors.map(a => a.getAttribute('href')).filter(Boolean)))];
+ for (const href of hrefs) {
+ let url = href;
+ if (url.startsWith('/')) url = 'https://luna.amazon.com' + url;
+ const title = url.split('/claims/')[1]?.split('/')[0] || (await anchorClaims.first().innerText()) || 'Unknown title';
+ cards.push({ kind: 'external', title, url });
+ }
+ }
+
+ if (!cards.length && games) {
+ const cardLocator = games.locator([
+ '[data-testid="offer-card"]',
+ '[data-test-selector="offer-card"]',
+ '.item-card__action',
+ ].join(','));
+ if (await cardLocator.count() === 0) {
+ console.log('No games found in list.');
+ } else {
+ for (const handle of await cardLocator.elementHandles()) {
+ const text = (await handle.textContent() || '').toLowerCase();
+ if (text.includes('collected')) continue; // skip already claimed
+ const title = await (await handle.$('h3, h4, [data-testid="item-card-title"], [data-test-selector="item-card-title"], .item-card-details__body__primary'))?.innerText() || 'Unknown title';
+ const linkEl = await handle.$('a[href]');
+ let url = linkEl && await linkEl.getAttribute('href');
+ if (url?.startsWith('/')) url = 'https://gaming.amazon.com' + url;
+ const hasLinkClaim = await handle.$('a:has-text("Claim"), a:has-text("Get"), a:has-text("Details")');
+ const hasButtonClaim = await handle.$('button:has-text("Claim"), button:has-text("Get"), button:has-text("Get game"), button:has-text("Play")');
+ if (hasLinkClaim) cards.push({ kind: 'external', title, url });
+ else if (hasButtonClaim) cards.push({ kind: 'internal', title, url, handle });
+ }
+ }
+ }
+
+ const internal = cards.filter(c => c.kind == 'internal');
+ const external = cards.filter(c => c.kind == 'external');
// bottom to top: oldest to newest games
internal.reverse();
external.reverse();
@@ -143,39 +216,41 @@ try {
const skipBasedOnTime = async url => {
// console.log(' Checking time left for game:', url);
const [p, isNew] = await sameOrNewPage(url);
- const dueDateOrg = await p.locator('.availability-date .tw-bold').innerText();
+ const dueDateLoc = p.locator('.availability-date .tw-bold, [data-testid="availability-end-date"], [data-test-selector="availability-end-date"]');
+ if (!await dueDateLoc.count()) {
+ if (isNew) await p.close();
+ return false;
+ }
+ 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;
- console.log(' ', await p.locator('.availability-date').innerText(), '->', daysLeft.toFixed(2));
+ 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) {
- await card.scrollIntoViewIfNeeded();
- const title = await (await card.$('.item-card-details__body__primary')).innerText();
- const slug = await (await card.$('a')).getAttribute('href');
- const url = 'https://gaming.amazon.com' + slug.split('?')[0];
+ await card.handle.scrollIntoViewIfNeeded();
+ const title = card.title;
+ const url = card.url;
console.log('Current free game:', chalk.blue(title));
- if (cfg.pg_timeLeft && await skipBasedOnTime(url)) continue;
+ if (cfg.pg_timeLeft && url && await skipBasedOnTime(url)) continue;
if (cfg.dryrun) continue;
if (cfg.interactive && !await confirm()) continue;
- await (await card.$('.tw-button:has-text("Claim")')).click();
+ await card.handle.locator('.tw-button:has-text("Claim"), .tw-button:has-text("Get"), button:has-text("Claim"), button:has-text("Get")').first().click();
db.data[user][title] ||= { title, time: datetime(), url, store: 'internal' };
notify_games.push({ title, status: 'claimed', url });
- // const img = await (await card.$('img.tw-image')).getAttribute('src');
- // console.log('Image:', img);
- await card.screenshot({ path: screenshot('internal', `${filenamify(title)}.png`) });
+ await card.handle.screenshot({ path: screenshot('internal', `${filenamify(title)}.png`) });
}
console.log('\nNumber of free unclaimed games (external stores):', external.length);
// claim games in external/linked stores. Linked: origin.com, epicgames.com; Redeem-key: gog.com, legacygames.com, microsoft
const external_info = [];
for (const card of external) { // need to get data incl. URLs in this loop and then navigate in another, otherwise .all() would update after coming back and .elementHandles() like above would lead to error due to page navigation: elementHandle.$: Protocol error (Page.adoptNode)
- const title = await card.locator('.item-card-details__body__primary').innerText();
- const slug = await card.locator('a:has-text("Claim")').first().getAttribute('href');
- const url = 'https://gaming.amazon.com' + slug.split('?')[0];
- // await (await card.$('text=Claim')).click(); // goes to URL of game, no need to wait
+ const title = card.title;
+ const url = card.url ? card.url.split('?')[0] : undefined;
+ if (!url) continue;
external_info.push({ title, url });
}
// external_info = [ { title: 'Fallout 76 (XBOX)', url: 'https://gaming.amazon.com/fallout-76-xbox-fgwp/dp/amzn1.pg.item.9fe17d7b-b6c2-4f58-b494-cc4e79528d0b?ingress=amzn&ref_=SM_Fallout76XBOX_S01_FGWP_CRWN' } ];
@@ -183,13 +258,32 @@ try {
console.log('Current free game:', chalk.blue(title)); // , url);
await page.goto(url, { waitUntil: 'domcontentloaded' });
if (cfg.debug) await page.pause();
- const item_text = await page.innerText('[data-a-target="DescriptionItemDetails"]');
- const store = item_text.toLowerCase().replace(/.* on /, '').slice(0, -1);
+ let store = 'unknown';
+ const detailLoc = page.locator('[data-a-target="DescriptionItemDetails"], [data-testid="DescriptionItemDetails"]');
+ if (await detailLoc.count()) {
+ const item_text = await detailLoc.first().innerText();
+ store = item_text.toLowerCase().replace(/.* on /, '').slice(0, -1);
+ } else if (url.includes('/claims/')) {
+ const slug = url.split('/claims/')[1]?.split('/')[0] || '';
+ if (slug.includes('gog')) store = 'gog.com';
+ else if (slug.includes('epic')) store = 'epic-games';
+ else if (slug.includes('origin')) store = 'origin';
+ else if (slug.includes('xbox') || slug.includes('microsoft')) store = 'microsoft store';
+ else if (slug.includes('legacy')) store = 'legacy games';
+ }
console.log(' External store:', store);
if (cfg.pg_timeLeft && await skipBasedOnTime(url)) continue;
if (cfg.dryrun) continue;
if (cfg.interactive && !await confirm()) continue;
- await Promise.any([page.click('[data-a-target="buy-box"] .tw-button:has-text("Get game")'), page.click('[data-a-target="buy-box"] .tw-button:has-text("Claim")'), page.click('.tw-button:has-text("Complete Claim")'), page.waitForSelector('div:has-text("Link game account")'), page.waitForSelector('.thank-you-title:has-text("Success")')]); // waits for navigation
+ await Promise.any([
+ page.click('[data-a-target="buy-box"] .tw-button:has-text("Get game")'),
+ page.click('[data-a-target="buy-box"] .tw-button:has-text("Claim")'),
+ page.click('.tw-button:has-text("Complete Claim")'),
+ page.click('[data-a-target="buy-box_call-to-action-text"]'),
+ page.click('p[data-a-target="buy-box_call-to-action-text"]'),
+ page.waitForSelector('div:has-text("Link game account")'),
+ page.waitForSelector('.thank-you-title:has-text("Success")'),
+ ]); // waits for navigation
db.data[user][title] ||= { title, time: datetime(), url, store };
const notify_game = { title, url };
notify_games.push(notify_game); // status is updated below
From 76f578e2e6edd24d1ea374ab9736ec92c598a475 Mon Sep 17 00:00:00 2001
From: nocci
Date: Mon, 29 Dec 2025 14:15:22 +0000
Subject: [PATCH 245/253] =?UTF-8?q?=E2=9C=A8=20feat(auth):=20enhance=20aut?=
=?UTF-8?q?omatic=20login=20and=20MFA=20handling?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- add handleMFA function for improved two-step verification
- implement direct login page handling with automatic sign-in
🐛 fix(claim): improve game claim process and duplicate prevention
- normalize claim URLs and deduplicate by URL
- fix various selectors for claim buttons and handle different languages
- prevent duplicate game claims by checking existing records
♻️ refactor(utils): improve code readability and maintainability
- extract normalizeClaimUrl function for URL handling
- restructure logic for claim and notification processes
🌐 i18n(claim): add support for game claim text in German
- handle German text for claim buttons and status checks
---
prime-gaming.js | 230 +++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 199 insertions(+), 31 deletions(-)
diff --git a/prime-gaming.js b/prime-gaming.js
index 8e755b8..dc39bba 100644
--- a/prime-gaming.js
+++ b/prime-gaming.js
@@ -37,8 +37,53 @@ await page.setViewportSize({ width: cfg.width, height: cfg.height }); // TODO wo
const notify_games = [];
let user;
+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(_ => {});
+ 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();
+ return true;
+};
+
try {
await page.goto(URL_CLAIM, { waitUntil: 'domcontentloaded' }); // default 'load' takes forever
+ const handleDirectLoginPage = async () => {
+ if (!page.url().includes('/ap/signin')) return false;
+ console.log('On Amazon login page (redirect). Trying to sign in automatically.');
+ if (!cfg.debug) context.setDefaultTimeout(cfg.login_timeout);
+ const email = cfg.pg_email || await prompt({ message: 'Enter email' });
+ const password = email && (cfg.pg_password || await prompt({ type: 'password', message: 'Enter password' }));
+ if (email && password) {
+ await page.fill('[name=email]', email);
+ await page.click('input[type="submit"]');
+ await page.fill('[name=password]', password);
+ await page.click('input[type="submit"]');
+ 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;
+ console.error('Login error:', error);
+ await notify(`prime-gaming: login: ${error}`);
+ await context.close(); // finishes potential recording
+ process.exit(1);
+ });
+ await page.waitForURL(/luna\.amazon\.com\/claims\/.*signedIn=true/);
+ if (!cfg.debug) context.setDefaultTimeout(cfg.timeout);
+ return true;
+ } else {
+ console.log('Waiting for manual login on redirect page.');
+ if (cfg.headless) {
+ console.log('Run `SHOW=1 node prime-gaming` to login in the opened browser.');
+ await context.close(); // finishes potential recording
+ process.exit(1);
+ }
+ return true;
+ }
+ };
+ await handleDirectLoginPage();
// need to wait for some elements to exist before checking if signed in or accepting cookies:
await Promise.any([
'button:has-text("Sign in")',
@@ -70,14 +115,7 @@ try {
await context.close(); // finishes potential recording
process.exit(1);
});
- // handle MFA, but don't await it
- page.waitForURL('**/ap/mfa**').then(async () => {
- console.log('Two-Step Verification - enter the One Time Password (OTP), e.g. generated by your Authenticator App');
- await page.check('[name=rememberDevice]');
- 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 page.locator('input[name=otpCode]').pressSequentially(otp.toString());
- await page.click('input[type="submit"]');
- }).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.');
@@ -128,6 +166,7 @@ try {
const selectors = [
'button[data-type="Game"]', // old layout
'button:has-text("Games")',
+ 'button:has-text("Games einlösen")',
'[data-test-selector="category-picker"] button:has-text("Games")',
'[data-testid="category-picker"] button:has-text("Games")',
];
@@ -138,6 +177,15 @@ try {
return;
}
}
+ // New Luna claims home: try the filter/CTA button with embedded
+ const gamesTitle = page.locator('p.offer-filters__button__title:has-text("Games"), p.offer-filters__button__title:has-text("einlösen")');
+ if (await gamesTitle.count()) {
+ const btn = gamesTitle.first().locator('xpath=ancestor::button[1]');
+ if (await btn.count()) {
+ await btn.click();
+ return;
+ }
+ }
// New Luna claims home already shows games list
};
@@ -163,15 +211,38 @@ try {
if (games) await scrollUntilStable(() => games.evaluate(el => el.scrollHeight).catch(() => 0));
await scrollUntilStable(() => page.evaluate(() => document.scrollingElement?.scrollHeight ?? 0));
+ const normalizeClaimUrl = url => {
+ if (!url) return { url, key: null };
+ const m = url.match(/(https?:\/\/[^/]+)?(\/claims\/[^?#]+)/);
+ if (!m) return { url, key: url };
+ const path = m[2];
+ const slug = path.split('/')[2];
+ return { url: 'https://luna.amazon.com' + path, key: slug || path };
+ };
+
const cards = [];
+ // New layout: direct claim buttons on cards (FGWPOffer) with "Spiel aktivieren"/"Claim"
+ const fgwps = page.locator('[data-a-target="FGWPOffer"]');
+ if (await fgwps.count()) {
+ for (const handle of await fgwps.elementHandles()) {
+ const href = await handle.getAttribute('href');
+ const { url, key } = normalizeClaimUrl(href?.startsWith('/') ? href : href || '');
+ const title =
+ await handle.$eval('p[title], span[title]', el => el.getAttribute('title')).catch(() => null) ||
+ await handle.$eval('p, span', el => el.textContent).catch(() => null) ||
+ key ||
+ 'Unknown title';
+ cards.push({ kind: 'external', title, url, key });
+ }
+ }
+
const anchorClaims = page.locator('a[href*="/claims/"][href*="amzn1.pg.item"]');
if (await anchorClaims.count()) {
const hrefs = [...new Set(await anchorClaims.evaluateAll(anchors => anchors.map(a => a.getAttribute('href')).filter(Boolean)))];
for (const href of hrefs) {
- let url = href;
- if (url.startsWith('/')) url = 'https://luna.amazon.com' + url;
- const title = url.split('/claims/')[1]?.split('/')[0] || (await anchorClaims.first().innerText()) || 'Unknown title';
- cards.push({ kind: 'external', title, url });
+ const { url, key } = normalizeClaimUrl(href);
+ const title = key || (await anchorClaims.first().innerText()) || 'Unknown title';
+ cards.push({ kind: 'external', title, url, key });
}
}
@@ -190,17 +261,28 @@ try {
const title = await (await handle.$('h3, h4, [data-testid="item-card-title"], [data-test-selector="item-card-title"], .item-card-details__body__primary'))?.innerText() || 'Unknown title';
const linkEl = await handle.$('a[href]');
let url = linkEl && await linkEl.getAttribute('href');
- if (url?.startsWith('/')) url = 'https://gaming.amazon.com' + url;
+ if (url?.startsWith('/')) url = 'https://luna.amazon.com' + url;
+ const { url: normUrl, key } = normalizeClaimUrl(url);
const hasLinkClaim = await handle.$('a:has-text("Claim"), a:has-text("Get"), a:has-text("Details")');
const hasButtonClaim = await handle.$('button:has-text("Claim"), button:has-text("Get"), button:has-text("Get game"), button:has-text("Play")');
- if (hasLinkClaim) cards.push({ kind: 'external', title, url });
- else if (hasButtonClaim) cards.push({ kind: 'internal', title, url, handle });
+ const hasGermanCTA = await handle.$(':is(button,p,a):has-text("Spiel aktivieren"), :is(button,p,a):has-text("Spiel holen")');
+ if (hasLinkClaim || hasGermanCTA) cards.push({ kind: 'external', title, url: normUrl, key });
+ else if (hasButtonClaim) cards.push({ kind: 'internal', title, url: normUrl, key, handle });
}
}
}
- const internal = cards.filter(c => c.kind == 'internal');
- const external = cards.filter(c => c.kind == 'external');
+ // dedup by URL to avoid duplicates from multiple selectors
+ const seenUrl = new Set();
+ const uniq = cards.filter(c => {
+ const key = c.key || c.url || c.title;
+ if (seenUrl.has(key)) return false;
+ seenUrl.add(key);
+ return true;
+ });
+
+ const internal = uniq.filter(c => c.kind == 'internal');
+ const external = uniq.filter(c => c.kind == 'external');
// bottom to top: oldest to newest games
internal.reverse();
external.reverse();
@@ -254,9 +336,46 @@ try {
external_info.push({ title, url });
}
// external_info = [ { title: 'Fallout 76 (XBOX)', url: 'https://gaming.amazon.com/fallout-76-xbox-fgwp/dp/amzn1.pg.item.9fe17d7b-b6c2-4f58-b494-cc4e79528d0b?ingress=amzn&ref_=SM_Fallout76XBOX_S01_FGWP_CRWN' } ];
+
+ const clickCTA = async p => {
+ const candidates = [
+ p.locator('button[data-a-target="buy-box_call-to-action"]').first(),
+ p.locator('[data-a-target="buy-box_call-to-action"]').first(),
+ p.locator('[data-a-target="buy-box"] .tw-button:has-text("Get game")').first(),
+ p.locator('[data-a-target="buy-box"] .tw-button:has-text("Claim")').first(),
+ p.locator('.tw-button:has-text("Complete Claim")').first(),
+ p.locator('[data-a-target="buy-box_call-to-action-text"]').first().locator('xpath=ancestor::button[1]'),
+ p.locator('.tw-button:has-text("Spiel holen"), .tw-button:has-text("Spiel aktivieren")').first(),
+ p.locator('p:has-text("Spiel holen"), p:has-text("Spiel aktivieren")').first().locator('xpath=ancestor::button[1]'),
+ ];
+ for (const c of candidates) {
+ if (await c.count()) {
+ try {
+ await c.waitFor({ state: 'visible', timeout: 5000 });
+ if (!await c.isEnabled()) {
+ await c.evaluate(el => { el.disabled = false; el.removeAttribute('disabled'); el.click(); });
+ } else {
+ await c.click();
+ }
+ return true;
+ } catch (_) {
+ // try next candidate
+ }
+ }
+ }
+ return false;
+ };
+
for (const { title, url } of external_info) {
console.log('Current free game:', chalk.blue(title)); // , url);
+ const existing = db.data[user]?.[title];
+ if (existing && existing.status && !existing.status.startsWith('failed')) {
+ console.log(` Already recorded as ${existing.status}, skipping.`);
+ notify_games.push({ title, url, status: 'existed' });
+ continue;
+ }
await page.goto(url, { waitUntil: 'domcontentloaded' });
+ 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"]');
@@ -270,23 +389,59 @@ try {
else if (slug.includes('origin')) store = 'origin';
else if (slug.includes('xbox') || slug.includes('microsoft')) store = 'microsoft store';
else if (slug.includes('legacy')) store = 'legacy games';
+ const lunaPlay = await page.locator('[data-a-target="LunaOffer"], button[data-a-target="LunaOffer"], button:has-text("Spielen")').count();
+ if (store == 'unknown' && lunaPlay) store = 'luna';
}
console.log(' External store:', store);
+ const notify_game = { title, url };
+ notify_games.push(notify_game); // status is updated below
+ // Already collected? skip
+ const collectedLoc = page.locator('[data-a-target="ClaimStateQuantityAndDateContent"], [data-a-target="ClaimStateClaimCodeContent"]:has-text("Collected"), [data-a-target="ClaimStateVendorContent"], [data-a-target="ClaimStateViewDetailsAndInstructions"]');
+ const collectedText = page.getByText(/You collected this on/i);
+ const collectedEpic = page.getByText(/Sent to your Epic Games Store library/i);
+ const collectedBanner = page.locator('p.tw-c-text-alert-success:has-text("Collected"), p.tw-c-text-alert-success:has-text("Collected this")');
+ const collectedSuccessIcon = page.locator('[data-a-target="ItemCardDetailSuccessStatus"], .claim-state__success-icon');
+ const disabledCTA = page.locator('[data-a-target="buy-box_call-to-action"][disabled], button[disabled]:has-text("Get game")');
+ const collectedAny = await Promise.all([
+ collectedLoc.count(),
+ collectedBanner.count(),
+ collectedText.count(),
+ collectedEpic.count(),
+ collectedSuccessIcon.count(),
+ disabledCTA.count(),
+ ]).then(([a, b, c, d, e, f]) => a + b + c + d + e + f > 0);
+ if (collectedAny) {
+ console.log(' Already collected, skipping.');
+ notify_game.status = 'existed';
+ db.data[user][title] ||= { title, time: datetime(), url, store, status: 'existed' };
+ continue;
+ }
+ // Disabled CTA (e.g., needs linking or not available)
+ if (await disabledCTA.count()) {
+ if (store !== 'epic-games') {
+ console.log(' CTA is disabled, skipping (likely needs linking/not available).');
+ notify_game.status = 'disabled';
+ db.data[user][title] ||= { title, time: datetime(), url, store, status: 'disabled' };
+ continue;
+ } else {
+ console.log(' CTA disabled for epic-games, will still try to link/claim.');
+ }
+ }
+ if (store == 'luna') {
+ console.log(' Luna cloud title detected, skipping code redemption.');
+ notify_game.status = 'luna (play)';
+ db.data[user][title] ||= { title, time: datetime(), url, store: 'luna', status: 'luna (play)' };
+ continue;
+ }
if (cfg.pg_timeLeft && await skipBasedOnTime(url)) continue;
if (cfg.dryrun) continue;
if (cfg.interactive && !await confirm()) continue;
+ await clickCTA(page);
await Promise.any([
- page.click('[data-a-target="buy-box"] .tw-button:has-text("Get game")'),
- page.click('[data-a-target="buy-box"] .tw-button:has-text("Claim")'),
- page.click('.tw-button:has-text("Complete Claim")'),
- page.click('[data-a-target="buy-box_call-to-action-text"]'),
- page.click('p[data-a-target="buy-box_call-to-action-text"]'),
- page.waitForSelector('div:has-text("Link game account")'),
- page.waitForSelector('.thank-you-title:has-text("Success")'),
- ]); // waits for navigation
+ 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 };
- const notify_game = { title, url };
- notify_games.push(notify_game); // status is updated below
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()) {
console.error(' Account linking is required to claim this offer!');
@@ -308,7 +463,21 @@ try {
'legacy games': 'https://www.legacygames.com/primedeal',
};
if (store in redeem) { // did not work for linked origin: && !await page.locator('div:has-text("Successfully Claimed")').count()
- const code = await Promise.any([page.inputValue('input[type="text"]'), page.textContent('[data-a-target="ClaimStateClaimCodeContent"]').then(s => s.replace('Your code: ', ''))]); // input: Legacy Games; text: gog.com
+ let code;
+ try {
+ // ensure CTA was clicked in case code is behind it
+ 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 (_) {
+ 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(_ => {});
+ continue;
+ }
console.log(' Code to redeem game:', chalk.blue(code));
if (store == 'legacy games') { // may be different URL like https://legacygames.com/primeday/puzzleoftheyear/
redeem[store] = await (await page.$('li:has-text("Click here") a')).getAttribute('href'); // full text: Click here to enter your redemption code.
@@ -441,11 +610,10 @@ try {
// await page.pause();
}
await page.goto(URL_CLAIM, { waitUntil: 'domcontentloaded' });
- await page.click('button[data-type="Game"]');
+ page.click('button[data-type="Game"]').catch(_ => {});
- if (notify_games.length) { // make screenshot of all games if something was claimed
+ if (notify_games.length && games) { // make screenshot of all games if something was claimed and list exists
const p = screenshot(`${filenamify(datetime())}.png`);
- // await page.screenshot({ path: p, fullPage: true }); // fullPage does not make a difference since scroll not on body but on some element
await scrollUntilStable(() => games.locator('.item-card__action').count());
const viewportSize = page.viewportSize(); // current viewport size
await page.setViewportSize({ ...viewportSize, height: 3000 }); // increase height, otherwise element screenshot is cut off at the top and bottom
From a21dced86ec47540efc722cb43ce900343509bc7 Mon Sep 17 00:00:00 2001
From: nocci
Date: Mon, 29 Dec 2025 14:31:25 +0000
Subject: [PATCH 246/253] =?UTF-8?q?=F0=9F=93=A6=20build(ci):=20add=20build?=
=?UTF-8?q?-and-push=20workflow=20for=20docker=20images?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- create a new CI workflow to automate image building and pushing
- trigger workflow on push to main branch
- include steps for checkout, login, build, and push docker images
---
.forgejo/workflows/build.yml | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
create mode 100644 .forgejo/workflows/build.yml
diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml
new file mode 100644
index 0000000..97fa8a0
--- /dev/null
+++ b/.forgejo/workflows/build.yml
@@ -0,0 +1,26 @@
+name: build-and-push
+
+on:
+ push:
+ branches:
+ - main
+
+jobs:
+ docker:
+ runs-on: self-hosted
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Login to registry
+ run: echo "${{ secrets.REG_TOKEN }}" | docker login "${{ secrets.REGISTRY }}" -u "${{ secrets.REG_USER }}" --password-stdin
+
+ - name: Build image
+ run: |
+ docker build -t "${{ secrets.REGISTRY_IMAGE }}:${{ github.sha }}" \
+ -t "${{ secrets.REGISTRY_IMAGE }}:latest" .
+
+ - name: Push image
+ run: |
+ docker push "${{ secrets.REGISTRY_IMAGE }}:${{ github.sha }}"
+ docker push "${{ secrets.REGISTRY_IMAGE }}:latest"
From 6f778d71ac751dec0f079739141a48c4d11ebf1d Mon Sep 17 00:00:00 2001
From: nocci
Date: Mon, 29 Dec 2025 14:33:14 +0000
Subject: [PATCH 247/253] =?UTF-8?q?=F0=9F=93=9D=20docs(README):=20add=20in?=
=?UTF-8?q?structions=20for=20building=20images=20in=20Forgejo?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- include steps for building and pushing Docker images using Forgejo
- provide details on setting Forgejo secrets and using self-hosted runners
---
README.md | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/README.md b/README.md
index 17ec854..5e0ab4d 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,14 @@ _This currently gives you a captcha challenge for epic-games. Until [issue #183]
This will run `node epic-games; node prime-gaming; node gog` - if you only want to claim games for one of the stores, you can override the default command by appending e.g. `node epic-games` at the end of the `docker run` command, or if you want several `bash -c "node epic-games.js; node gog.js"`.
Data (including json files with claimed games, codes to redeem, screenshots) is stored in the Docker volume `fgc`.
+### Eigene Images aus Forgejo bauen
+Falls du den Fork in einer selbst gehosteten Forgejo-Instanz pflegst:
+
+- Der Workflow `.forgejo/workflows/build.yml` baut/pusht das Docker-Image auf `push` nach `main`.
+- Setze in Forgejo die Secrets `REGISTRY`, `REGISTRY_IMAGE`, `REG_USER`, `REG_TOKEN` (PAT mit Paket-Push).
+- Self-hosted Runner mit Docker muss registriert sein (`runs-on: self-hosted`).
+- Danach kannst du das Image ziehen, z.B.: `docker pull $REGISTRY_IMAGE:latest`.
+
I want to run without Docker or develop locally.
From bde0f34a80e8f59dce8b6b8e74b5e5286fda3320 Mon Sep 17 00:00:00 2001
From: nocci
Date: Mon, 29 Dec 2025 14:36:18 +0000
Subject: [PATCH 248/253] =?UTF-8?q?=F0=9F=93=9D=20docs(README):=20simplify?=
=?UTF-8?q?=20and=20update=20documentation?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- remove detailed setup instructions and history
- add quick-start guide for Docker and Docker Compose
- update CI and configuration sections
- streamline language and formatting for clarity
---
README.md | 300 +++++++++++++++---------------------------------------
1 file changed, 80 insertions(+), 220 deletions(-)
diff --git a/README.md b/README.md
index 5e0ab4d..be82e97 100644
--- a/README.md
+++ b/README.md
@@ -1,232 +1,92 @@
-
-
-
+Free Games Claimer (Fork)
+==========================
-[](https://sonarcloud.io/project/overview?id=vogler_free-games-claimer)
-# free-games-claimer
+Automates claiming of free games for:
+- Epic Games Store (and Epic-linked assets)
+- Amazon Prime Gaming / Luna claims (incl. external stores like GOG, Legacy, Microsoft)
+- GOG giveaways
+- Optional extras: Steam stats, AliExpress dailies (experimental)
-Claims free games periodically on
--
[Epic Games Store](https://www.epicgames.com/store/free-games)
--
[Amazon Prime Gaming](https://gaming.amazon.com)
--
[GOG](https://www.gog.com)
--
[Unreal Engine (Assets)](https://www.unrealengine.com/marketplace/en-US/assets?count=20&sortBy=effectiveDate&sortDir=DESC&start=0&tag=4910) ([experimental](https://github.com/vogler/free-games-claimer/issues/44), same login as Epic Games)
-
+This fork adds a Forgejo CI pipeline and instructions for running the pre-built image from your own registry.
-Pull requests welcome :)
+Requirements
+------------
+- Docker or Podman (empfohlen) **oder** Node.js ≥ 20 zum lokalen Lauf
+- (Optional) Python `apprise` für Benachrichtigungen: `pip install apprise`
+- Für Playwright: Linux-Desktop-Abhängigkeiten sind im Container enthalten; lokal ggf. `npm install` zieht Firefox mit.
-
-
-_Works on Windows/macOS/Linux._
-
-Raspberry Pi (3, 4, Zero 2): [requires 64-bit OS](https://github.com/vogler/free-games-claimer/issues/3) like Raspberry Pi OS or Ubuntu (Raspbian won't work since it's 32-bit).
-
-## How to run
-Easy option: [install Docker](https://docs.docker.com/get-docker/) (or [podman](https://podman-desktop.io/)) and run this command in a terminal:
+Schnellstart (Docker Run)
+-------------------------
```
-docker run --rm -it -p 6080:6080 -v fgc:/fgc/data --pull=always ghcr.io/vogler/free-games-claimer
+docker run --rm -it \
+ -p 6080:6080 \
+ -v fgc:/fgc/data \
+ -e SHOW=1 \
+ :latest \
+ node prime-gaming.js
+```
+- `` z. B. `git.sky-net.it/nocci/free-games-claimer`
+- Ports 6080/5900: noVNC/VNC (nur nötig mit `SHOW=1`)
+- Daten/Configs landen in Volume `fgc` unter `/fgc/data`
+
+Docker Compose Beispiel
+-----------------------
+```yaml
+services:
+ fgc:
+ image: :latest
+ container_name: fgc
+ environment:
+ - SHOW=1 # Browser sichtbar via VNC/noVNC
+ # - PG_EMAIL=...
+ # - PG_PASSWORD=...
+ # - PG_OTPKEY=...
+ volumes:
+ - fgc:/fgc/data
+ ports:
+ - "6080:6080" # noVNC
+ # - "5900:5900" # VNC optional
+ command: bash -c "node epic-games; node prime-gaming; node gog"
+volumes:
+ fgc:
```
-_This currently gives you a captcha challenge for epic-games. Until [issue #183](https://github.com/vogler/free-games-claimer/issues/183) is fixed, it is recommended to just run `node epic-games` without docker (see below)._
+CI / eigenes Image
+------------------
+- Workflow: `.forgejo/workflows/build.yml` baut/pusht auf `push` nach `main`.
+- Secrets in Forgejo setzen:
+ - `REGISTRY` (z. B. `git.sky-net.it`)
+ - `REGISTRY_IMAGE` (z. B. `git.sky-net.it/nocci/free-games-claimer`)
+ - `REG_USER`, `REG_TOKEN` (PAT mit Paket-Push)
+- Self-hosted Runner mit Docker-Access (`runs-on: self-hosted`) wird benötigt.
-This will run `node epic-games; node prime-gaming; node gog` - if you only want to claim games for one of the stores, you can override the default command by appending e.g. `node epic-games` at the end of the `docker run` command, or if you want several `bash -c "node epic-games.js; node gog.js"`.
-Data (including json files with claimed games, codes to redeem, screenshots) is stored in the Docker volume `fgc`.
+Konfiguration (Umgebungsvariablen)
+----------------------------------
+Typische Optionen:
+- `SHOW=0/1` (0 = headless, 1 = UI)
+- `WIDTH`, `HEIGHT` (Browsergröße)
+- `TIMEOUT`, `LOGIN_TIMEOUT` (Sek.)
+- Login: `EMAIL`, `PASSWORD` global; spezifisch `EG_EMAIL`, `EG_PASSWORD`, `EG_OTPKEY`, `PG_EMAIL`, `PG_PASSWORD`, `PG_OTPKEY`, `GOG_EMAIL`, `GOG_PASSWORD`
+- Prime-Gaming: `PG_REDEEM=1` (Keys automatisch einlösen, experimentell), `PG_CLAIMDLC=1`, `PG_TIMELEFT=` zum Überspringen mit langer Restzeit
+- Screenshots: `SCREENSHOTS_DIR` (Standard `data/screenshots`)
+- Notifications: `NOTIFY='...'` (Apprise-URL), optional `NOTIFY_TITLE`
-### Eigene Images aus Forgejo bauen
-Falls du den Fork in einer selbst gehosteten Forgejo-Instanz pflegst:
+Du kannst eine `data/config.env` anlegen; sie wird per dotenv geladen und überschreibt nichts, was bereits in der Umgebung gesetzt ist.
-- Der Workflow `.forgejo/workflows/build.yml` baut/pusht das Docker-Image auf `push` nach `main`.
-- Setze in Forgejo die Secrets `REGISTRY`, `REGISTRY_IMAGE`, `REG_USER`, `REG_TOKEN` (PAT mit Paket-Push).
-- Self-hosted Runner mit Docker muss registriert sein (`runs-on: self-hosted`).
-- Danach kannst du das Image ziehen, z.B.: `docker pull $REGISTRY_IMAGE:latest`.
+Lokal ohne Docker
+-----------------
+```
+npm install
+SHOW=0 PG_EMAIL=... PG_PASSWORD=... PG_OTPKEY=... node prime-gaming.js
+```
+- Playwright lädt Firefox beim `npm install` in `~/.cache/ms-playwright`.
+- Für sichtbaren Browser `SHOW=1` (GUI/Xvfb nötig).
-
- I want to run without Docker or develop locally.
+Persistenz & Ausgaben
+---------------------
+- Daten & Status: `data/*.json` (pro Store)
+- Browserprofil: `data/browser`
+- Screenshots: `data/screenshots//`
+- Optionale Videos/HAR: `RECORD=1` → `data/record/`
-1. [Install Node.js](https://nodejs.org/en/download)
-2. Clone/download this repository and `cd` into it in a terminal
-3. Run `npm install`
-4. Run `pip install apprise` (or use [pipx](https://github.com/pypa/pipx) if you have [problems](https://stackoverflow.com/questions/75608323/how-do-i-solve-error-externally-managed-environment-every-time-i-use-pip-3)) to install [apprise](https://github.com/caronc/apprise) if you want notifications
-5. To get updates: `git pull; npm install`
-6. Run `node epic-games`, `node prime-gaming`, `node gog`...
-
-During `npm install` Playwright will download its Firefox to a cache in home ([doc](https://playwright.dev/docs/browsers#managing-browser-binaries)).
-If you are missing some dependencies for the browser on your system, you can use `sudo npx playwright install firefox --with-deps`.
-
-If you don't want to use Docker for quasi-headless mode, you could run inside a virtual machine, on a server, or you wake your PC at night to avoid being interrupted.
-
-
-## Usage
-All scripts start an automated Firefox instance, either with the browser GUI shown or hidden (*headless mode*). By default, you won't see any browser open on your host system.
-
-- When running inside Docker, the browser will be shown only inside the container. You can open http://localhost:6080 to interact with the browser running inside the container via noVNC (or use other VNC clients on port 5900).
-- When running the scripts outside of Docker, the browser will be hidden by default; you can use `SHOW=1 ...` to show the UI (see options below).
-
-When running the first time, you have to login for each store you want to claim games on.
-You can login indirectly via the terminal or directly in the browser. The scripts will wait until you are successfully logged in.
-
-There will be prompts in the terminal asking you to enter email, password, and afterwards some OTP (one time password / security code) if you have 2FA/MFA (two-/multi-factor authentication) enabled. If you want to login yourself via the browser, you can press escape in the terminal to skip the prompts.
-
-After login, the script will continue claiming the current games. If it still waits after you are already logged in, you can restart it (and open an issue). If you run the scripts regularly, you should not have to login again.
-
-### Configuration / Options
-Options are set via [environment variables](https://kinsta.com/knowledgebase/what-is-an-environment-variable/) which allow for flexible configuration.
-
-TODO: ~~On the first run, the script will guide you through configuration and save all settings to `data/config.env`. You can edit this file directly or run `node fgc config` to run the configuration assistant again.~~
-
-Available options/variables and their default values:
-
-| Option | Default | Description |
-|--------------- |--------- |------------------------------------------------------------------------ |
-| SHOW | 1 | Show browser if 1. Default for Docker, not shown when running outside. |
-| WIDTH | 1280 | Width of the opened browser (and of screen for VNC in Docker). |
-| HEIGHT | 1280 | Height of the opened browser (and of screen for VNC in Docker). |
-| VNC_PASSWORD | | VNC password for Docker. No password used by default! |
-| NOTIFY | | Notification services to use (Pushover, Slack, Telegram...), see below. [Apprise](https://github.com/caronc/apprise) |
-| NOTIFY_TITLE | | Optional title for notifications, e.g. for Pushover. |
-| BROWSER_DIR | data/browser | Directory for browser profile, e.g. for multiple accounts. |
-| TIMEOUT | 60 | Timeout for any page action. Should be fine even on slow machines. |
-| LOGIN_TIMEOUT | 180 | Timeout for login in seconds. Will wait twice (prompt + manual login). |
-| EMAIL | | Default email for any login. |
-| PASSWORD | | Default password for any login. |
-| EG_EMAIL | | Epic Games email for login. Overrides EMAIL. |
-| EG_PASSWORD | | Epic Games password for login. Overrides PASSWORD. |
-| EG_OTPKEY | | Epic Games MFA OTP key. |
-| EG_PARENTALPIN | | Epic Games Parental Controls PIN. |
-| PG_EMAIL | | Prime Gaming email for login. Overrides EMAIL. |
-| PG_PASSWORD | | Prime Gaming password for login. Overrides PASSWORD. |
-| PG_OTPKEY | | Prime Gaming MFA OTP key. |
-| PG_REDEEM | 0 | Prime Gaming: try to redeem keys on external stores ([experimental](https://github.com/vogler/free-games-claimer/issues/5)). |
-| PG_CLAIMDLC | 0 | Prime Gaming: try to claim DLCs ([experimental](https://github.com/vogler/free-games-claimer/issues/55)). |
-| GOG_EMAIL | | GOG email for login. Overrides EMAIL. |
-| GOG_PASSWORD | | GOG password for login. Overrides PASSWORD. |
-| GOG_NEWSLETTER | 0 | Do not unsubscribe from newsletter after claiming a game if 1. |
-| LG_EMAIL | | Legacy Games: email to use for redeeming (if not set, defaults to PG_EMAIL) |
-
-See `src/config.js` for all options.
-
-#### How to set options
-You can add options directly in the command or put them in a file to load.
-
-##### Docker
-You can pass variables using `-e VAR=VAL`, for example `docker run -e EMAIL=foo@bar.baz -e NOTIFY='tgram://bottoken/ChatID' ...` or using `--env-file fgc.env` where `fgc.env` is a file on your host system (see [docs](https://docs.docker.com/engine/reference/commandline/run/#env)). You can also `docker cp` your configuration file to `/fgc/data/config.env` in the `fgc` volume to store it with the rest of the data instead of on the host ([example](https://github.com/moby/moby/issues/25245#issuecomment-365980572)).
-If you are using [docker compose](https://docs.docker.com/compose/environment-variables/) (or Portainer etc.), you can put options in the `environment:` section.
-
-##### Without Docker
-On Linux/macOS you can prefix the variables you want to set, for example `EMAIL=foo@bar.baz SHOW=1 node epic-games` will show the browser and skip asking you for your login email. On Windows you have to use `set`, [example](https://github.com/vogler/free-games-claimer/issues/314).
-You can also put options in `data/config.env` which will be loaded by [dotenv](https://github.com/motdotla/dotenv).
-
-### Notifications
-The scripts will try to send notifications for successfully claimed games and any errors like needing to log in or encountered captchas (should not happen).
-
-[apprise](https://github.com/caronc/apprise) is used for notifications and offers many services including Pushover, Slack, Telegram, SMS, Email, desktop and custom notifications.
-You just need to set `NOTIFY` to the notification services you want to use, e.g. `NOTIFY='mailto://myemail:mypass@gmail.com' 'pbul://o.gn5kj6nfhv736I7jC3cj3QLRiyhgl98b'` - refer to their list of services and [examples](https://github.com/caronc/apprise#command-line-usage).
-
-### Automatic login, two-factor authentication
-If you set the options for email, password and OTP key, there will be no prompts and logins should happen automatically. This is optional since all stores should stay logged in since cookies are refreshed.
-To get the OTP key, it is easiest to follow the store's guide for adding an authenticator app. You should also scan the shown QR code with your favorite app to have an alternative method for 2FA.
-
-- **Epic Games**: visit [password & security](https://www.epicgames.com/account/password), enable 'third-party authenticator app', copy the 'Manual Entry Key' and use it to set `EG_OTPKEY`.
-- **Prime Gaming**: visit Amazon 'Your Account › Login & security', 2-step verification › Manage › Add new app › Can't scan the barcode, copy the bold key and use it to set `PG_OTPKEY`
-- **GOG**: only offers OTP via email
-
-
-Beware that storing passwords and OTP keys as clear text may be a security risk. Use a unique/generated password! TODO: maybe at least offer to base64 encode for storage.
-
-### Epic Games Store
-Run `node epic-games` (locally or in Docker).
-
-### Amazon Prime Gaming
-Run `node prime-gaming` (locally or in Docker).
-
-Claiming the Amazon Games works out-of-the-box, however, for games on external stores you need to either link your account or redeem a key.
-
-- Stores that require account linking: Epic Games, Battle.net, Origin.
-- Stores that require redeeming a key: GOG.com, Microsoft Games, Legacy Games.
-
- Keys and URLs are printed to the console, included in notifications and saved in `data/prime-gaming.json`. A screenshot of the page with the key is also saved to `data/screenshots`.
- [TODO](https://github.com/vogler/free-games-claimer/issues/5): ~~redeem keys on external stores.~~
-
-
-
-
-### Run periodically
-#### How often?
-Epic Games usually has two free games *every week*, before Christmas every day.
-Prime Gaming has new games *every month* or more often during Prime days.
-GOG usually has one new game every couples of weeks.
-Unreal Engine has new assets to claim *every first Tuesday of a month*.
-
-
-It is safe to run the scripts every day.
-
-#### How to schedule?
-The container/scripts will claim currently available games and then exit.
-If you want it to run regularly, you have to schedule the runs yourself:
-
-- Linux/macOS: `crontab -e` ([example](https://github.com/vogler/free-games-claimer/discussions/56))
-- macOS: [launchd](https://stackoverflow.com/questions/132955/how-do-i-set-a-task-to-run-every-so-often)
-- Windows: [task scheduler](https://active-directory-wp.com/docs/Usage/How_to_add_a_cron_job_on_Windows/Scheduled_tasks_and_cron_jobs_on_Windows/index.html) ([example](https://github.com/vogler/free-games-claimer/wiki/%5BHowTo%5D-Schedule-runs-on-Windows)), [other options](https://stackoverflow.com/questions/132971/what-is-the-windows-version-of-cron), or just put the command in a `.bat` file in Autostart if you restart often...
-- any OS: use a process manager like [pm2](https://pm2.keymetrics.io/docs/usage/restart-strategies/)
-- Docker Compose `command: bash -c "node epic-games; node prime-gaming; node gog; echo sleeping; sleep 1d"` additionally add `restart: unless-stopped` to it.
-
-TODO: ~~add some server-mode where the script just keeps running and claims games e.g. every day.~~
-
-### Problems?
-
-Check the open [issues](https://github.com/vogler/free-games-claimer/issues) and comment there or open a new issue.
-
-If you're a developer, you can use `PWDEBUG=1 ...` to [inspect](https://playwright.dev/docs/inspector) which opens a debugger where you can step through the script.
-
-
-## History/DevLog
-
- Click to expand
-
-Tried [epicgames-freebies-claimer](https://github.com/Revadike/epicgames-freebies-claimer), but had problems since epicgames introduced hcaptcha (see [issue](https://github.com/Revadike/epicgames-freebies-claimer/issues/172)).
-
-Played around with puppeteer before, now trying newer https://playwright.dev which is pretty similar.
-Playwright Inspector and `codegen` to generate scripts are nice, but failed to generate the right code for clicking a button in an iframe.
-
-Added [main.spec.ts](https://github.com/vogler/epicgames-claimer/commit/e5ce7916ab6329cfc7134677c4d89c2b3fa3ba97#diff-d18d03e9c407a20e05fbf03cbd6f9299857740544fb6b50d6a70b9c6fbc35831) which was the test script generated by `npx playwright codegen` with manual fix for clicking buttons in the created iframe. Can be executed by `npx playwright test`. The test runner has options `--debug` and `--timeout` and can execute typescript which is nice. However, this only worked up to the button 'I Agree', and then showed an hcaptcha.
-
-Added [main.captcha.js](https://github.com/vogler/epicgames-claimer/commit/e5ce7916ab6329cfc7134677c4d89c2b3fa3ba97#diff-d18d03e9c407a20e05fbf03cbd6f9299857740544fb6b50d6a70b9c6fbc35831) which uses beta of `playwright-extra@next` and `@extra/recaptcha@next` (from [comment on puppeteer-extra](https://github.com/berstend/puppeteer-extra/pull/303#issuecomment-775277480)).
-However, `playwright-extra` seems to be old and missing `:has-text` selector (fixed [here](https://github.com/vogler/epicgames-claimer/commit/ba97a0e840b65f4476cca18e28d8461b0c703420)) and `page.frameLocator`, so the script did not run without adjustments.
-Also, solving via [2captcha](https://2captcha.com?from=13225256) is a paid service which takes time and may be unreliable.
-
-
-Added [main.stealth.js](https://github.com/vogler/epicgames-claimer/commit/64d0ba8ce71baec3947d1b64acd567befcb39340#diff-f70d3bd29df4a343f11062a97063953173491ce30fe34f69a0fc52517adbf342) which uses the stealth plugin without `playwright-extra` wrapper but up-to-date `playwright` (from [comment](https://github.com/berstend/puppeteer-extra/issues/454#issuecomment-917437212)).
-The listed evasions are enough to not show an hcaptcha. Script claimed game successfully in non-headless mode.
-
-Removed `main.captcha.js`.
-Using Playwright Test (`main.spec.ts`) instead of Library (`main.stealth.js`) has the advantage of free CLI like `--debug` and `--timeout`.
-
-
-Button selectors should preferably use text in order to be more stable against changes in the DOM.
-
-Renamed repository from epicgames-claimer to free-games-claimer since a script for Amazon Prime Gaming was also added. Removed all old scripts in favor of just `epic-games.js` and `prime-gaming.js`.
-
-epic games: `headless` mode gets hcaptcha challenge. More details/references in [issue](https://github.com/vogler/free-games-claimer/issues/2).
-
-https://github.com/vogler/free-games-claimer/pull/11 introduced a Dockerfile for running non-headless inside the container via xvfb which makes it headless for the host running the container.
-
-v1.0 Standalone scripts node epic-games and node prime-gaming using Chromium.
-
-Changed to Firefox for all scripts since Chromium led to captchas. Claiming then also worked in headless mode without Docker.
-
-Added options via env vars, configurable in `data/config.env`.
-
-Added OTP generation via otplib for automatic login, even with 2FA.
-
-Added notifications via [apprise](https://github.com/caronc/apprise).
-
-
-[](https://star-history.com/#vogler/free-games-claimer&Date)
-
-
-
-
----
-
-Logo with smaller aspect ratio (for Telegram bot etc.): 👾 - [emojipedia](https://emojipedia.org/alien-monster/)
-
-
+Tipp: Bei Captchas oder Erst-Login `SHOW=1` nutzen und einmal manuell einloggen; Cookies bleiben im Profil. Notifications via `NOTIFY` helfen bei Fehlermeldungen (z. B. Captcha, Login).***
From eba07721ca09a04c0c95e2f9672f87d084c6ee15 Mon Sep 17 00:00:00 2001
From: nocci
Date: Mon, 29 Dec 2025 14:38:54 +0000
Subject: [PATCH 249/253] =?UTF-8?q?=F0=9F=93=9D=20docs(README):=20update?=
=?UTF-8?q?=20instructions=20and=20clarify=20configurations?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- update registry image references to point to `git.sky-net.it/nocci/free-games-claimer`
- improve clarity on Docker and Docker Compose examples
- translate German sections into English for wider accessibility
- add detailed explanations for environment variables and configurations
- enhance quickstart and CI instructions for better understanding
---
README.md | 83 +++++++++++++++++++++++++++----------------------------
1 file changed, 41 insertions(+), 42 deletions(-)
diff --git a/README.md b/README.md
index be82e97..8ccf31c 100644
--- a/README.md
+++ b/README.md
@@ -3,41 +3,40 @@ Free Games Claimer (Fork)
Automates claiming of free games for:
- Epic Games Store (and Epic-linked assets)
-- Amazon Prime Gaming / Luna claims (incl. external stores like GOG, Legacy, Microsoft)
+- Amazon Prime Gaming / Luna claims (including external stores like GOG, Legacy, Microsoft)
- GOG giveaways
- Optional extras: Steam stats, AliExpress dailies (experimental)
-This fork adds a Forgejo CI pipeline and instructions for running the pre-built image from your own registry.
+This fork adds a Forgejo CI pipeline and instructions for running the pre-built image from the registry at `git.sky-net.it/nocci/free-games-claimer`.
Requirements
------------
-- Docker or Podman (empfohlen) **oder** Node.js ≥ 20 zum lokalen Lauf
-- (Optional) Python `apprise` für Benachrichtigungen: `pip install apprise`
-- Für Playwright: Linux-Desktop-Abhängigkeiten sind im Container enthalten; lokal ggf. `npm install` zieht Firefox mit.
+- Docker or Podman (recommended), or Node.js ≥ 20 for local runs
+- Optional notifications: `pip install apprise`
+- Playwright dependencies are baked into the container; locally, `npm install` downloads Firefox.
-Schnellstart (Docker Run)
--------------------------
+Quickstart (Docker Run)
+-----------------------
```
docker run --rm -it \
-p 6080:6080 \
-v fgc:/fgc/data \
-e SHOW=1 \
- :latest \
+ git.sky-net.it/nocci/free-games-claimer:latest \
node prime-gaming.js
```
-- `` z. B. `git.sky-net.it/nocci/free-games-claimer`
-- Ports 6080/5900: noVNC/VNC (nur nötig mit `SHOW=1`)
-- Daten/Configs landen in Volume `fgc` unter `/fgc/data`
+- Ports 6080/5900: noVNC/VNC (only needed with `SHOW=1`)
+- Data/configs are stored in volume `fgc` under `/fgc/data`
-Docker Compose Beispiel
------------------------
+Docker Compose Example
+----------------------
```yaml
services:
fgc:
- image: :latest
+ image: git.sky-net.it/nocci/free-games-claimer:latest
container_name: fgc
environment:
- - SHOW=1 # Browser sichtbar via VNC/noVNC
+ - SHOW=1 # show browser via VNC/noVNC
# - PG_EMAIL=...
# - PG_PASSWORD=...
# - PG_OTPKEY=...
@@ -51,42 +50,42 @@ volumes:
fgc:
```
-CI / eigenes Image
-------------------
-- Workflow: `.forgejo/workflows/build.yml` baut/pusht auf `push` nach `main`.
-- Secrets in Forgejo setzen:
- - `REGISTRY` (z. B. `git.sky-net.it`)
- - `REGISTRY_IMAGE` (z. B. `git.sky-net.it/nocci/free-games-claimer`)
- - `REG_USER`, `REG_TOKEN` (PAT mit Paket-Push)
-- Self-hosted Runner mit Docker-Access (`runs-on: self-hosted`) wird benötigt.
+CI / Build Your Own Image
+-------------------------
+- Workflow: `.forgejo/workflows/build.yml` builds/pushes on `push` to `main`.
+- Secrets needed in Forgejo:
+ - `REGISTRY` (e.g., `git.sky-net.it`)
+ - `REGISTRY_IMAGE` (e.g., `git.sky-net.it/nocci/free-games-claimer`)
+ - `REG_USER`, `REG_TOKEN` (PAT with package push)
+- Requires a self-hosted runner with Docker access (`runs-on: self-hosted`).
-Konfiguration (Umgebungsvariablen)
-----------------------------------
-Typische Optionen:
+Configuration (Environment Variables)
+-------------------------------------
+Common options:
- `SHOW=0/1` (0 = headless, 1 = UI)
-- `WIDTH`, `HEIGHT` (Browsergröße)
-- `TIMEOUT`, `LOGIN_TIMEOUT` (Sek.)
-- Login: `EMAIL`, `PASSWORD` global; spezifisch `EG_EMAIL`, `EG_PASSWORD`, `EG_OTPKEY`, `PG_EMAIL`, `PG_PASSWORD`, `PG_OTPKEY`, `GOG_EMAIL`, `GOG_PASSWORD`
-- Prime-Gaming: `PG_REDEEM=1` (Keys automatisch einlösen, experimentell), `PG_CLAIMDLC=1`, `PG_TIMELEFT=` zum Überspringen mit langer Restzeit
-- Screenshots: `SCREENSHOTS_DIR` (Standard `data/screenshots`)
-- Notifications: `NOTIFY='...'` (Apprise-URL), optional `NOTIFY_TITLE`
+- `WIDTH`, `HEIGHT` (browser size)
+- `TIMEOUT`, `LOGIN_TIMEOUT` (seconds)
+- Login: `EMAIL`, `PASSWORD` global; per store `EG_EMAIL`, `EG_PASSWORD`, `EG_OTPKEY`, `PG_EMAIL`, `PG_PASSWORD`, `PG_OTPKEY`, `GOG_EMAIL`, `GOG_PASSWORD`
+- Prime Gaming: `PG_REDEEM=1` (auto-redeem keys, experimental), `PG_CLAIMDLC=1`, `PG_TIMELEFT=` to skip long-remaining offers
+- Screenshots: `SCREENSHOTS_DIR` (default `data/screenshots`)
+- Notifications: `NOTIFY='...'` (Apprise URL), optional `NOTIFY_TITLE`
-Du kannst eine `data/config.env` anlegen; sie wird per dotenv geladen und überschreibt nichts, was bereits in der Umgebung gesetzt ist.
+You can place a `data/config.env`; it is loaded via dotenv and is overridden by explicitly set environment variables.
-Lokal ohne Docker
------------------
+Local Run Without Docker
+------------------------
```
npm install
SHOW=0 PG_EMAIL=... PG_PASSWORD=... PG_OTPKEY=... node prime-gaming.js
```
-- Playwright lädt Firefox beim `npm install` in `~/.cache/ms-playwright`.
-- Für sichtbaren Browser `SHOW=1` (GUI/Xvfb nötig).
+- Playwright downloads Firefox to `~/.cache/ms-playwright`.
+- Use `SHOW=1` for a visible browser (requires GUI/Xvfb).
-Persistenz & Ausgaben
+Persistence & Outputs
---------------------
-- Daten & Status: `data/*.json` (pro Store)
-- Browserprofil: `data/browser`
+- Data/status: `data/*.json` (per store)
+- Browser profile: `data/browser`
- Screenshots: `data/screenshots//`
-- Optionale Videos/HAR: `RECORD=1` → `data/record/`
+- Optional videos/HAR: `RECORD=1` → `data/record/`
-Tipp: Bei Captchas oder Erst-Login `SHOW=1` nutzen und einmal manuell einloggen; Cookies bleiben im Profil. Notifications via `NOTIFY` helfen bei Fehlermeldungen (z. B. Captcha, Login).***
+Tip: For captchas or first-time login, run with `SHOW=1` and log in once; cookies stay in the profile. Notifications via `NOTIFY` help surface errors (e.g., captcha, login).
From eb5b9bbb6e8890e51072c2a592dd14d45a469d83 Mon Sep 17 00:00:00 2001
From: nocci
Date: Mon, 29 Dec 2025 15:01:55 +0000
Subject: [PATCH 250/253] =?UTF-8?q?=F0=9F=91=B7=20ci(build):=20enhance=20d?=
=?UTF-8?q?ocker=20build=20process=20with=20buildx?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- add Docker Buildx setup for advanced build capabilities
- update build step to use buildx for multi-platform support
---
.forgejo/workflows/build.yml | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml
index 97fa8a0..3aa7b56 100644
--- a/.forgejo/workflows/build.yml
+++ b/.forgejo/workflows/build.yml
@@ -9,6 +9,9 @@ jobs:
docker:
runs-on: self-hosted
steps:
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
- name: Checkout
uses: actions/checkout@v4
@@ -17,8 +20,9 @@ jobs:
- name: Build image
run: |
- docker build -t "${{ secrets.REGISTRY_IMAGE }}:${{ github.sha }}" \
- -t "${{ secrets.REGISTRY_IMAGE }}:latest" .
+ docker buildx build --load \
+ -t "${{ secrets.REGISTRY_IMAGE }}:${{ github.sha }}" \
+ -t "${{ secrets.REGISTRY_IMAGE }}:latest" .
- name: Push image
run: |
From 0a729d0cbfa562f6913bbe2d7a0085e2da07ab72 Mon Sep 17 00:00:00 2001
From: nocci
Date: Mon, 29 Dec 2025 15:08:00 +0000
Subject: [PATCH 251/253] =?UTF-8?q?=F0=9F=94=A7=20chore(workflow):=20simpl?=
=?UTF-8?q?ify=20docker=20image=20tagging=20and=20pushing?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- remove specific sha tag from build and push steps
- streamline workflow by focusing on latest tag
---
.forgejo/workflows/build.yml | 2 --
1 file changed, 2 deletions(-)
diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml
index 3aa7b56..cb83fb0 100644
--- a/.forgejo/workflows/build.yml
+++ b/.forgejo/workflows/build.yml
@@ -21,10 +21,8 @@ jobs:
- name: Build image
run: |
docker buildx build --load \
- -t "${{ secrets.REGISTRY_IMAGE }}:${{ github.sha }}" \
-t "${{ secrets.REGISTRY_IMAGE }}:latest" .
- name: Push image
run: |
- docker push "${{ secrets.REGISTRY_IMAGE }}:${{ github.sha }}"
docker push "${{ secrets.REGISTRY_IMAGE }}:latest"
From 9d79f9ac7851f8d4dabdb0cb0e5bc1dd3cc29525 Mon Sep 17 00:00:00 2001
From: nocci
Date: Mon, 29 Dec 2025 15:54:41 +0000
Subject: [PATCH 252/253] =?UTF-8?q?=F0=9F=93=9D=20docs(README):=20update?=
=?UTF-8?q?=20configuration=20and=20remove=20CI=20section?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- remove outdated CI/build instructions
- add new environment variable options for configuration
🐛 fix(util): handle notification errors gracefully
- resolve promise instead of rejecting on notification errors
- prevent whole run from failing due to notification issues
---
README.md | 16 +++++++---------
src/util.js | 3 ++-
2 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/README.md b/README.md
index 8ccf31c..16ac157 100644
--- a/README.md
+++ b/README.md
@@ -50,15 +50,6 @@ volumes:
fgc:
```
-CI / Build Your Own Image
--------------------------
-- Workflow: `.forgejo/workflows/build.yml` builds/pushes on `push` to `main`.
-- Secrets needed in Forgejo:
- - `REGISTRY` (e.g., `git.sky-net.it`)
- - `REGISTRY_IMAGE` (e.g., `git.sky-net.it/nocci/free-games-claimer`)
- - `REG_USER`, `REG_TOKEN` (PAT with package push)
-- Requires a self-hosted runner with Docker access (`runs-on: self-hosted`).
-
Configuration (Environment Variables)
-------------------------------------
Common options:
@@ -69,6 +60,13 @@ Common options:
- Prime Gaming: `PG_REDEEM=1` (auto-redeem keys, experimental), `PG_CLAIMDLC=1`, `PG_TIMELEFT=` to skip long-remaining offers
- Screenshots: `SCREENSHOTS_DIR` (default `data/screenshots`)
- Notifications: `NOTIFY='...'` (Apprise URL), optional `NOTIFY_TITLE`
+- Browser profile: `BROWSER_DIR` (default `data/browser`)
+- Recording: `RECORD=1` to save videos/HAR to `data/record/`
+- Debugging: `DEBUG=1` (opens Playwright inspector), `DEBUG_NETWORK=1` (logs requests), `TIME=1` (prints timings)
+- Dry run / Interaction: `DRYRUN=1` (do not claim), `INTERACTIVE=1` (ask before claiming), `HEADLESS` is derived from `SHOW`/`DEBUG`
+- Directories: `SCREENSHOTS_DIR`, `BROWSER_DIR`, `DATA_DIR` (prefix for data; default under `data/`)
+- VNC/noVNC: `VNC_PASSWORD` (for Docker entrypoint), `NOVNC_PORT`/`VNC_PORT` (Docker)
+- General timeouts: `TIMEOUT` (per action), `LOGIN_TIMEOUT` (extra time for login)
You can place a `data/config.env`; it is loaded via dotenv and is overridden by explicitly set environment variables.
diff --git a/src/util.js b/src/util.js
index 308952d..49424f8 100644
--- a/src/util.js
+++ b/src/util.js
@@ -125,7 +125,8 @@ export const notify = html => new Promise((resolve, reject) => {
if (error.message.includes('command not found')) {
console.info('Run `pip install apprise`. See https://github.com/vogler/free-games-claimer#notifications');
}
- return reject(error);
+ // don't fail the whole run on notification errors
+ return resolve();
}
if (stderr) console.error(`stderr: ${stderr}`);
if (stdout) console.log(`stdout: ${stdout}`);
From e39cca93c23fe1412ca1bdecbbeda45843bd060f Mon Sep 17 00:00:00 2001
From: nocci
Date: Mon, 29 Dec 2025 17:19:39 +0100
Subject: [PATCH 253/253] better README.md
---
README.md | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index 16ac157..248995e 100644
--- a/README.md
+++ b/README.md
@@ -2,12 +2,9 @@ Free Games Claimer (Fork)
==========================
Automates claiming of free games for:
-- Epic Games Store (and Epic-linked assets)
-- Amazon Prime Gaming / Luna claims (including external stores like GOG, Legacy, Microsoft)
+- Amazon Luna Gaming / Luna claims (including external stores like GOG, Epic Games, Legacy Games )
- GOG giveaways
-- Optional extras: Steam stats, AliExpress dailies (experimental)
-
-This fork adds a Forgejo CI pipeline and instructions for running the pre-built image from the registry at `git.sky-net.it/nocci/free-games-claimer`.
+- Optional extras: Steam stats, AliExpress dailies (not implemated yet)
Requirements
------------