Updated GOG claiming

- additional checks for freegames for filters
- cleanup file
- updated README
This commit is contained in:
4n4n4s 2023-09-22 06:24:21 +00:00
parent 5202f7ac69
commit 804ecf98d0
2 changed files with 33 additions and 25 deletions

View file

@ -87,9 +87,9 @@ Available options/variables and their default values:
| GOG_EMAIL | | GOG email for login. Overrides EMAIL. | | GOG_EMAIL | | GOG email for login. Overrides EMAIL. |
| GOG_PASSWORD | | GOG password for login. Overrides PASSWORD. | | GOG_PASSWORD | | GOG password for login. Overrides PASSWORD. |
| GOG_NEWSLETTER | 0 | Do not unsubscribe from newsletter after claiming a game if 1. | | GOG_NEWSLETTER | 0 | Do not unsubscribe from newsletter after claiming a game if 1. |
| GOG_GIVEAWAY | 1 | Claims giveaway game(s). Is enabled by default. | | GOG_GIVEAWAY | 1 | Claims giveaway game(s). |
| GOG_FREEGAMES | 0 | Claims other free games that are not demos or prologue games. Is disabled by default. | | GOG_FREEGAMES | 0 | Claims other free games that are not demos or prologue games. |
| GOG_FREEGAMES_URL | [freegames_url](https://www.gog.com/en/games?priceRange=0,0&languages=en&order=asc:title&hideDLCs=true&excludeTags=demo&excludeTags=freegame) | URL to get games to claim additionally. You can add filters for language or certain categories that you don't like. The filter to hide owned games (hideOwned=true) is automatically added so please do not add it.| | GOG_FREEGAMES_URL | [freegames_url](https://www.gog.com/en/games?priceRange=0,0&languages=en&order=asc:title&hideDLCs=true&excludeTags=demo&excludeTags=freegame) | URL to get games to claim additionally. You can add filters for language or certain categories that you don't like. |
See `config.js` for all options. See `config.js` for all options.

52
gog.js
View file

@ -2,7 +2,6 @@ import { firefox } from 'playwright-firefox'; // stealth plugin needs no outdate
import path from 'path'; import path from 'path';
import { resolve, 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'; import { cfg } from './config.js';
import path from "path";
import { existsSync } from "fs"; import { existsSync } from "fs";
const screenshot = (...a) => resolve(cfg.dir.screenshots, 'gog', ...a); const screenshot = (...a) => resolve(cfg.dir.screenshots, 'gog', ...a);
@ -61,7 +60,7 @@ try {
// handle MFA, but don't await it // handle MFA, but don't await it
iframe.locator('form[name=second_step_authentication]').waitFor().then(async () => { iframe.locator('form[name=second_step_authentication]').waitFor().then(async () => {
console.log('Two-Step Verification - Enter security code'); console.log('Two-Step Verification - Enter security code');
console.log(await iframe.locator('.form__description').innerText()) 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 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_token_letter_1').pressSequentially(otp.toString(), {delay: 10});
await iframe.locator('#second_step_authentication_send').click(); await iframe.locator('#second_step_authentication_send').click();
@ -74,7 +73,7 @@ try {
notify('gog: got captcha during login. Please check.'); notify('gog: got captcha during login. Please check.');
// TODO solve reCAPTCHA? // TODO solve reCAPTCHA?
}).catch(_ => { }); }).catch(_ => { });
await page.waitForSelector('#menuUsername') await page.waitForSelector('#menuUsername');
} else { } else {
console.log('Waiting for you to login in the browser.'); 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.'); await notify('gog: no longer signed in and not enough options set for automatic login.');
@ -189,7 +188,7 @@ async function claimGame(url){
.first(); .first();
const inLibrary = page const inLibrary = page
.locator("button.go-to-library-button") .locator("button.go-to-library-button")
.first() .first();
const inCart = page const inCart = page
.locator('.cart-button__state-in-cart:visible') .locator('.cart-button__state-in-cart:visible')
.first(); .first();
@ -240,14 +239,23 @@ async function claimGame(url){
} }
async function claimFreegames(){ async function claimFreegames(){
console.log("claiming freegames from " + cfg.gog_freegames_url + ". (adding fitler for ownedgames manually)") var freegames_url = cfg.gog_freegames_url;
await page.goto(cfg.gog_freegames_url, { waitUntil: 'networkidle' }); if (freegames_url.includes("&hideOwned=true")) {
freegames_url = freegames_url.replace("&hideOwned=true", "");
}
if (!freegames_url.includes("priceRange=0,0")) {
console.log("Filter for only free games not detected adding it manually.");
freegames_url = freegames_url + "&priceRange=0,0";
}
console.log("claiming freegames from " + freegames_url);
await page.goto(freegames_url, { waitUntil: 'networkidle' });
await page.locator('label[selenium-id="hideOwnedCheckbox"]').click(); // when you add it to url immediately it shows more results await page.locator('label[selenium-id="hideOwnedCheckbox"]').click(); // when you add it to url immediately it shows more results
await page.waitForTimeout(2500); await page.waitForTimeout(2500);
var allLinks = []; var allLinks = [];
var hasMorePages = true; var hasMorePages = true;
do { do {
const links = await page.locator(".product-tile").all() const links = await page.locator(".product-tile").all();
const gameUrls = await Promise.all( const gameUrls = await Promise.all(
links.map(async (game) => { links.map(async (game) => {
var urlSlug = await game.getAttribute("href"); var urlSlug = await game.getAttribute("href");
@ -258,42 +266,42 @@ async function claimFreegames(){
allLinks.push(url); allLinks.push(url);
} }
if (await page.locator('.small-pagination__item--next.disabled').isVisible()){ if (await page.locator('.small-pagination__item--next.disabled').isVisible()){
hasMorePages = false hasMorePages = false;
console.log("last page") console.log("last page");
} else { } else {
await page.locator(".small-pagination__item--next").first().click(); await page.locator(".small-pagination__item--next").first().click();
console.log("next page - waiting") console.log("next page - waiting");
await page.waitForTimeout(5000); // wait until page is loaded it takes some time with filters await page.waitForTimeout(5000); // wait until page is loaded it takes some time with filters
} }
} while (hasMorePages) } while (hasMorePages);
console.log("Found total games: " + allLinks.length) console.log("Found total games: " + allLinks.length);
allLinks = allLinks.filter(function (str) { return !str.endsWith("_prologue") }); allLinks = allLinks.filter(function (str) { return !str.endsWith("_prologue"); });
allLinks = allLinks.filter(function (str) { return !str.endsWith("_demo") }); allLinks = allLinks.filter(function (str) { return !str.endsWith("_demo"); });
console.log("Filtered count: " + allLinks.length) console.log("Filtered count: " + allLinks.length);
for (const url of allLinks) for (const url of allLinks)
{ {
if (isNotClaimedUrl(url)) if (!isClaimedUrl(url))
{ {
console.log(url) console.log(url);
await claimGame(url); await claimGame(url);
} }
} }
} }
function isNotClaimedUrl(url) { function isClaimedUrl(url) {
try { try {
var status = db.data[user][url.split("/").filter((x) => !!x).pop()]["status"]; var status = db.data[user][url.split("/").filter((x) => !!x).pop()]["status"];
if (status === "existed" || status === "claimed") { if (status === "existed" || status === "claimed") {
return false; return true;
} else { } else {
return true return false;
} }
} catch (error) { } catch (error) {
return true return false;
} }
} }
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(); await context.close();