-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.js
93 lines (78 loc) · 3.4 KB
/
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
const fs = require('fs')
/* This is the official API used by the GFN Games Page at https://www.nvidia.com/en-us/geforce-now/games/
While officially undocumented, a third-party developer, Ighor July (@JulyIghor at https://github.com/JulyIghor), documented its inner workings in a blog post: https://ighor.medium.com/i-unlocked-nvidia-geforce-now-and-stumbled-upon-pirates-dc48a3f8ff7
*/
const GFN_API_URL = "https://api-prod.nvidia.com/gfngames/v1/gameList";
/* Fetch the games from Nvidia by requesting several pages of GraphQL results, filtered for Steam support */
const getSteamIds = async () => {
let steamIdsOfGamesOnGeForceNow = new Set();
const fetchGamesQuery = /*GraphQL*/ `
# HUGE thanks to @JulyIghor for help optimizing this query!
# Reusable fragment for the body that we want
fragment queryFields on AppQueryType
{
# We don't need this pagination info anymore since we can just fetch multiple cursors in one query
# numberReturned
#
# pageInfo {
# endCursor
# hasNextPage
# }
items {
variants
{
storeId
}
}
}
{
# Fetch games 0 to 1300
page1: apps(country:"US" appStore:"STEAM" first:1300)
{
...queryFields
}
# Fetch games 1300 to 2600, though there should only be about 1600 total games as of Sept 2023
page2: apps(country:"US" appStore:"STEAM" first:1300 after:"MTMwMA==") # Base64 encoded "1300"
{
...queryFields
}
# Future-proofing just in case. GFN probably won't grow by thousands THAT quickly
page3: apps(country:"US" appStore:"STEAM" first:1300 after:"MjYwMA==") # Base64 encoded "2600"
{
...queryFields
}
}
`;
const fetchConfig = {
body: fetchGamesQuery,
method: "POST",
};
try {
const fetchGamesResponse = await fetch(GFN_API_URL, fetchConfig);
const responseJSON = await fetchGamesResponse.json();
Object.values(responseJSON.data).forEach((page) => {
page.items.forEach((game) => {
if (!game.variants[0]?.storeId || typeof (game.variants[0].storeId) !== 'string') {
return;
}
const numericId = Number(game.variants[0].storeId)
if (numericId && typeof (numericId === 'number')) { // Test on local node will sometimes make a "Fake" entry
steamIdsOfGamesOnGeForceNow.add(numericId);
}
});
});
if (steamIdsOfGamesOnGeForceNow.size < 1000) {
throw Error("Unexpected decrease in number of games (fewer than 1000)")
}
return [...steamIdsOfGamesOnGeForceNow];
} catch (error) {
console.error("Failed to fetch games", error)
}
};
getSteamIds().then((ids) => {
const collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
fs.writeFile('data.js', `
// ${ids.length} Steam games in the list
const steamIdsOnGeForceNow = new Set(${JSON.stringify(ids.sort(collator.compare))})`,
(error) => error && console.error("Failed to save games list", error))
});