From ba5e51f529f25681928a1b57d908faf6f39d93d7 Mon Sep 17 00:00:00 2001 From: Ralf Vogler Date: Thu, 5 Jun 2025 18:08:14 +0200 Subject: [PATCH 1/2] eg: claim mobile games, closes #474 --- epic-games.js | 10 ++++++++ src/config.js | 1 + src/epic-games-mobile.js | 55 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 src/epic-games-mobile.js diff --git a/epic-games.js b/epic-games.js index a664c1b..15148df 100644 --- a/epic-games.js +++ b/epic-games.js @@ -6,6 +6,7 @@ import path from 'path'; import { existsSync, writeFileSync } from 'fs'; import { resolve, jsonDb, datetime, filenamify, prompt, confirm, notify, html_game_list, handleSIGINT } from './src/util.js'; import { cfg } from './src/config.js'; +import { getGames } from './src/epic-games-mobile.js'; const screenshot = (...a) => resolve(cfg.dir.screenshots, 'epic-games', ...a); @@ -146,6 +147,15 @@ try { // 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.all()).map(a => a.getAttribute('href'))); const urls = urlSlugs.map(s => 'https://store.epicgames.com' + s); + + // Free mobile games - https://github.com/vogler/free-games-claimer/issues/474 + // https://egs-platform-service.store.epicgames.com/api/v2/public/discover/home?count=10&country=DE&locale=en&platform=android&start=0&store=EGS + if (cfg.eg_mobile) { + console.log('Including mobile games...'); + const mobileGames = await getGames(); + urls.push(...mobileGames.map(x => x.url)); + } + console.log('Free games:', urls); for (const url of urls) { diff --git a/src/config.js b/src/config.js index a9522e9..9d710b8 100644 --- a/src/config.js +++ b/src/config.js @@ -34,6 +34,7 @@ export const cfg = { eg_password: process.env.EG_PASSWORD || process.env.PASSWORD, eg_otpkey: process.env.EG_OTPKEY, eg_parentalpin: process.env.EG_PARENTALPIN, + eg_mobile: process.env.EG_MOBILE != '0', // claim mobile games // auth prime-gaming pg_email: process.env.PG_EMAIL || process.env.EMAIL, pg_password: process.env.PG_PASSWORD || process.env.PASSWORD, diff --git a/src/epic-games-mobile.js b/src/epic-games-mobile.js new file mode 100644 index 0000000..d9acdcc --- /dev/null +++ b/src/epic-games-mobile.js @@ -0,0 +1,55 @@ +// following https://github.com/vogler/free-games-claimer/issues/474 + +const get = async (platform = 'android') => { // or ios + const r = await fetch(`https://egs-platform-service.store.epicgames.com/api/v2/public/discover/home?count=10&country=DE&locale=en&platform=${platform}&start=0&store=EGS`, { + "method": "GET", + }); + const j = await r.json(); + // console.log(j); + return j; +} + +// $ jq '.data[].topicId' -r +// $ jq '.data[] | {topicId,type} | flatten | @tsv' -r +// mobile-android-carousel featured +// mobile-android-featured-breaker featured +// mobile-android-genre-must-play interactiveIconList +// mobile-android-1pp featured +// mobile-android-fn-exp imageOnly +// android-mega-sale interactiveIconList +// mobile-android-free-game freeGame +// mobile-android-genre-action featured +// mobile-android-genre-free interactiveIconList +// mobile-android-genre-paid interactiveIconList +// $ jq '.data[].offers[].content | {slug: .mapping.slug, price: (.purchase[] | {decimal: .price.decimalPrice, type: .purchaseType})}' +// { +// "slug": "dc-heroes-united-android-de4bc2", +// "price": { +// "decimal": 0, +// "type": "Claim" +// } +// } +// { +// "slug": "ashworld-android-abd8de", +// "price": { +// "decimal": 4.79, +// "type": "Purchase" +// } +// } + +const url = s => `https://store.epicgames.com/en-US/p/${s}`; + +export const getPlatformGames = async platform => { + const json = await get(platform); + const free_game = json.data.filter(x => x.type == 'freeGame')[0]; + // console.log(free_game); + return free_game.offers.map(offer => { + const c = offer.content; + // console.log(c.purchase) + return { title: c.title, url: url(c.mapping.slug) }; + }); +}; + +export const getGames = async () => [...await getPlatformGames('android'), ...await getPlatformGames('ios')] + +// console.log(await getGames()); From 17771640f17b45601a2ffa8167dd7a7df3fe0f3a Mon Sep 17 00:00:00 2001 From: Ralf Vogler Date: Thu, 5 Jun 2025 18:17:18 +0200 Subject: [PATCH 2/2] cleanup, eslint --- src/epic-games-mobile.js | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/epic-games-mobile.js b/src/epic-games-mobile.js index d9acdcc..73fed10 100644 --- a/src/epic-games-mobile.js +++ b/src/epic-games-mobile.js @@ -1,26 +1,22 @@ // following https://github.com/vogler/free-games-claimer/issues/474 const get = async (platform = 'android') => { // or ios - const r = await fetch(`https://egs-platform-service.store.epicgames.com/api/v2/public/discover/home?count=10&country=DE&locale=en&platform=${platform}&start=0&store=EGS`, { - "method": "GET", - }); - const j = await r.json(); - // console.log(j); - return j; -} + const r = await fetch(`https://egs-platform-service.store.epicgames.com/api/v2/public/discover/home?count=10&country=DE&locale=en&platform=${platform}&start=0&store=EGS`); + return await r.json(); +}; // $ jq '.data[].topicId' -r // $ jq '.data[] | {topicId,type} | flatten | @tsv' -r -// mobile-android-carousel featured -// mobile-android-featured-breaker featured -// mobile-android-genre-must-play interactiveIconList -// mobile-android-1pp featured -// mobile-android-fn-exp imageOnly -// android-mega-sale interactiveIconList -// mobile-android-free-game freeGame -// mobile-android-genre-action featured -// mobile-android-genre-free interactiveIconList -// mobile-android-genre-paid interactiveIconList +// mobile-android-carousel featured +// mobile-android-featured-breaker featured +// mobile-android-genre-must-play interactiveIconList +// mobile-android-1pp featured +// mobile-android-fn-exp imageOnly +// android-mega-sale interactiveIconList +// mobile-android-free-game freeGame +// mobile-android-genre-action featured +// mobile-android-genre-free interactiveIconList +// mobile-android-genre-paid interactiveIconList // $ jq '.data[].offers[].content | {slug: .mapping.slug, price: (.purchase[] | {decimal: .price.decimalPrice, type: .purchaseType})}' // { // "slug": "dc-heroes-united-android-de4bc2", @@ -50,6 +46,6 @@ export const getPlatformGames = async platform => { }); }; -export const getGames = async () => [...await getPlatformGames('android'), ...await getPlatformGames('ios')] +export const getGames = async () => [...await getPlatformGames('android'), ...await getPlatformGames('ios')]; // console.log(await getGames());