Skip to content

Commit 1bdc0c6

Browse files
authored
Added additional format options for review scores (#57)
1 parent 573458a commit 1bdc0c6

9 files changed

+137
-31
lines changed

README.md

+19-9
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ Which game properties should be fetched when a new Steam game is detected, and t
138138
},
139139
"reviewScore": {
140140
"enabled": true,
141+
"format": "percentage",
141142
"notionProperty": "Review Score"
142143
},
143144
"tags": {
@@ -153,7 +154,7 @@ Which game properties should be fetched when a new Steam game is detected, and t
153154
<details>
154155
<summary><code>gameName</code></summary>
155156

156-
The name of the game as it appears on Steam. The database field in Notion must be of type "Text".
157+
The name of the game as it appears on Steam. The database field in Notion must be of type `Text`.
157158

158159
| Type | Default value | Possible values | Required |
159160
|---|---|---|---|
@@ -267,7 +268,7 @@ The URL of the image to use if the game does not have an icon through any of the
267268
<details>
268269
<summary><code>releaseDate</code></summary>
269270

270-
The release date of the game. The database field in Notion must be of type "Date".
271+
The release date of the game. The database field in Notion must be of type `Date`.
271272

272273
| Type | Default value | Possible values | Required |
273274
|---|---|---|---|
@@ -302,7 +303,7 @@ The name of the Notion property to set the release date in.
302303
<details>
303304
<summary><code>reviewScore</code></summary>
304305

305-
The user review score from 0-100. The database field in Notion must be of type "Number".
306+
The user review score for the game, formatted as one of a number of options. The database field in Notion must match the type defined by the chosen "format".
306307

307308
| Type | Default value | Possible values | Required |
308309
|---|---|---|---|
@@ -311,6 +312,7 @@ The user review score from 0-100. The database field in Notion must be of type "
311312
```json
312313
"reviewScore": {
313314
"enabled": true,
315+
"format": "percentage",
314316
"notionProperty": "Review Score"
315317
}
316318
```
@@ -325,6 +327,14 @@ Whether or not the user review score should be set in the database.
325327
|---|---|---|---|
326328
| `boolean` | `true` | `true` or `false` | Yes |
327329

330+
<h4><code>format</code></h4>
331+
332+
How the review score should be formatted.
333+
334+
| Type | Default value | Possible values | Required |
335+
|---|---|---|---|
336+
| `string` | `percentage` | `percentage`: Notion database field type: `Number`. A percentage value formatted as a float from 0.00-1.00.<br/>`sentiment`:Notion database field type: `Select`. A sentiment value such as "Overwhelmingly Positive" or "Mixed".<br/>`total`:Notion database field type: `Number`. The total number of reviews submitted for the game, across all languages.<br/>`positive`:Notion database field type: `Number`. The total number of positive reviews submitted for the game, across all languages.<br/>`negative`:Notion database field type: `Number`. The total number of negative reviews submitted for the game, across all languages.<br/>`positive/negative`:Notion database field type: `Text`. The total number of positive and negative reviews submitted for the game, across all languages, formatted as "{numPositive} positive / {numNegative} negative". | Yes |
337+
328338
<h4><code>notionProperty</code></h4>
329339

330340
The name of the Notion property to set the user review score in.
@@ -337,7 +347,7 @@ The name of the Notion property to set the user review score in.
337347
<details>
338348
<summary><code>tags</code></summary>
339349

340-
The user-defined tags of the game as they can be seen on the store page. The database field in Notion must be of type "Multi-select".
350+
The user-defined tags of the game as they can be seen on the store page. The database field in Notion must be of type `Multi-select`.
341351

342352
| Type | Default value | Possible values | Required |
343353
|---|---|---|---|
@@ -363,7 +373,7 @@ Whether or not the tags of the game should be set in the database.
363373

364374
<h4><code>notionProperty</code></h4>
365375

366-
The name of the Notion property to set the tags in. This field must be of type "multi-select".
376+
The name of the Notion property to set the tags in. This field must be of type `Multi-select`.
367377

368378
| Type | Default value | Possible values | Required |
369379
|---|---|---|---|
@@ -382,7 +392,7 @@ The language of the tags, e.g. "english" or "spanish".
382392
<details>
383393
<summary><code>gameDescription</code></summary>
384394

385-
The short description of the game as it appears on the store page. The database field in Notion must be of type "Text".
395+
The short description of the game as it appears on the store page. The database field in Notion must be of type `Text`.
386396

387397
| Type | Default value | Possible values | Required |
388398
|---|---|---|---|
@@ -417,7 +427,7 @@ The name of the Notion property to set the description in.
417427
<details>
418428
<summary><code>storePage</code></summary>
419429

420-
The URL to the store page of the game. The database field in Notion must be of type "URL".
430+
The URL to the store page of the game. The database field in Notion must be of type `URL`.
421431

422432
| Type | Default value | Possible values | Required |
423433
|---|---|---|---|
@@ -452,7 +462,7 @@ The name of the Notion property to set the store page URL in.
452462
<details>
453463
<summary><code>gamePrice</code></summary>
454464

455-
The price of the game on Steam. Does not account for current sales or discounts (as this data would be outdated too quickly). The currency depends on your current country. The database field in Notion must be of type "Number".
465+
The price of the game on Steam. Does not account for current sales or discounts (as this data would be outdated too quickly). The currency depends on your current country. The database field in Notion must be of type `Number`.
456466

457467
| Type | Default value | Possible values | Required |
458468
|---|---|---|---|
@@ -487,7 +497,7 @@ The name of the Notion property to set the price in.
487497
<details>
488498
<summary><code>steamDeckCompatibility</code></summary>
489499

490-
The Steam Deck Compatibility score, which can be one of "Verified", "Playable", "Unsupported" or "Unknown". The database field in Notion must be of type "Select".
500+
The Steam Deck Compatibility score, which can be one of "Verified", "Playable", "Unsupported" or "Unknown". The database field in Notion must be of type `Select`.
491501

492502
| Type | Default value | Possible values | Required |
493503
|---|---|---|---|

config.default.json

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
},
2727
"reviewScore": {
2828
"enabled": true,
29+
"format": "percentage",
2930
"notionProperty": "Review Score"
3031
},
3132
"tags": {

config.schema.json

+35-1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
},
6565
"reviewScore": {
6666
"enabled": true,
67+
"format": "percentage",
6768
"notionProperty": "Review Score"
6869
},
6970
"tags": {
@@ -199,10 +200,11 @@
199200
]
200201
},
201202
"reviewScore": {
202-
"description": "The user review score from 0-100. The database field in Notion must be of type \"Number\".",
203+
"description": "The user review score for the game, formatted as one of a number of options. The database field in Notion must match the type defined by the chosen \"format\".",
203204
"type": "object",
204205
"default": {
205206
"enabled": true,
207+
"format": "percentage",
206208
"notionProperty": "Review Score"
207209
},
208210
"additionalProperties": false,
@@ -212,6 +214,37 @@
212214
"type": "boolean",
213215
"default": true
214216
},
217+
"format": {
218+
"description": "How the review score should be formatted.",
219+
"type": "string",
220+
"default": "percentage",
221+
"oneOf": [
222+
{
223+
"const": "percentage",
224+
"title": "Notion database field type: \"Number\". A percentage value formatted as a float from 0.00-1.00."
225+
},
226+
{
227+
"const": "sentiment",
228+
"title": "Notion database field type: \"Select\". A sentiment value such as \"Overwhelmingly Positive\" or \"Mixed\"."
229+
},
230+
{
231+
"const": "total",
232+
"title": "Notion database field type: \"Number\". The total number of reviews submitted for the game, across all languages."
233+
},
234+
{
235+
"const": "positive",
236+
"title": "Notion database field type: \"Number\". The total number of positive reviews submitted for the game, across all languages."
237+
},
238+
{
239+
"const": "negative",
240+
"title": "Notion database field type: \"Number\". The total number of negative reviews submitted for the game, across all languages."
241+
},
242+
{
243+
"const": "positive/negative",
244+
"title": "Notion database field type: \"Text\". The total number of positive and negative reviews submitted for the game, across all languages, formatted as \"{numPositive} positive / {numNegative} negative\"."
245+
}
246+
]
247+
},
215248
"notionProperty": {
216249
"description": "The name of the Notion property to set the user review score in.",
217250
"type": "string",
@@ -220,6 +253,7 @@
220253
},
221254
"required": [
222255
"enabled",
256+
"format",
223257
"notionProperty"
224258
]
225259
},

index.js

+9-4
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
// Suppresses the warning about the fetch API being unstable
55
process.removeAllListeners('warning');
66

7-
import { getSteamAppInfoDirect, getSteamAppInfoSteamUser } from './js/steamAPI.js';
8-
import { CONFIG, localDatabase, addGameToLocalDatabase, storeAPIRequired, steamUserAPIRequired } from './js/utils.js';
7+
import { getSteamAppInfoDirect, getSteamAppInfoSteamUser, getSteamReviewScoreDirect } from './js/steamAPI.js';
8+
import { CONFIG, localDatabase, addGameToLocalDatabase, storeAPIRequired, steamUserAPIRequired, reviewAPIRequired } from './js/utils.js';
99
import { getGamesFromNotionDatabase, updateNotionPage, checkNotionPropertiesExistence, setUserIdInDatabaseIfNotSet } from './js/notion.js';
1010
import { getGameProperties } from './js/gameProperties.js';
1111

@@ -31,7 +31,7 @@ async function updateNotionDatabase() {
3131

3232
// Update the last updated timestamp
3333
// Do this before fetching to make sure we don't miss changes made between now and fetching new properties below
34-
// Subtract 60 more seconds to make sure we have some buffer in case things get changed inbetween executions
34+
// Subtract 60 more seconds to make sure we have some buffer in case things get changed in between executions
3535
const newLastUpdatedAt = new Date(Date.now() - 60000).toISOString();
3636

3737
// If we encounter an error or would hit the Steam API request limit, we don't want to update the timestamp to find the games we missed again
@@ -90,7 +90,12 @@ async function updateNotionDatabase() {
9090
? await getSteamAppInfoDirect(steamAppId)
9191
: null;
9292

93-
let notionProperties = await getGameProperties(appInfoDirect, appInfoSteamUser[steamAppId], steamAppId);
93+
// Get info about the game's review score from the reviews API, if required
94+
const appInfoReviews = reviewAPIRequired
95+
? await getSteamReviewScoreDirect(steamAppId)
96+
: null;
97+
98+
let notionProperties = await getGameProperties(appInfoDirect, appInfoSteamUser[steamAppId], appInfoReviews, steamAppId);
9499

95100
updateNotionPage(pageId, notionProperties);
96101
addGameToLocalDatabase(pageId, steamAppId);

js/gameProperties.js

+46-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { getSteamTagNames } from './steamAPI.js';
22
import { CONFIG } from './utils.js';
33

4-
export async function getGameProperties(appInfoDirect, appInfoSteamUser, steamAppId) {
4+
export async function getGameProperties(appInfoDirect, appInfoSteamUser, appInfoReviews, steamAppId) {
55
let outputProperties = {};
66
let cover;
77
let icon;
@@ -16,7 +16,7 @@ export async function getGameProperties(appInfoDirect, appInfoSteamUser, steamAp
1616
outputProperties = getGameReleaseDate(propertyValue, appInfoDirect, outputProperties);
1717
break;
1818
case "reviewScore":
19-
outputProperties = getGameReviewScore(propertyValue, appInfoSteamUser, outputProperties);
19+
outputProperties = getGameReviewScore(propertyValue, appInfoReviews, outputProperties);
2020
break;
2121
case "tags":
2222
outputProperties = await getGameTags(propertyValue, appInfoSteamUser, outputProperties);
@@ -152,15 +152,52 @@ function getGameReleaseDate(releaseDateProperty, appInfoDirect, outputProperties
152152
return outputProperties;
153153
}
154154

155-
function getGameReviewScore(reviewScoreProperty, appInfoSteamUser, outputProperties) {
156-
if (!reviewScoreProperty.enabled || !appInfoSteamUser.review_percentage) { return outputProperties; }
155+
function getGameReviewScore(reviewScoreProperty, appInfoReviews, outputProperties) {
156+
if (!reviewScoreProperty.enabled || !appInfoReviews) { return outputProperties; }
157+
let notionReviewObject;
157158

158-
// The reviewScore is not available through the Steam store API, so we have to use the SteamUser API instead
159-
const steamReviewScore = parseInt(appInfoSteamUser.review_percentage) / 100;
159+
switch (reviewScoreProperty.format) {
160+
case "percentage":
161+
notionReviewObject = {
162+
"number": parseFloat((appInfoReviews.total_positive / appInfoReviews.total_reviews).toFixed(2))
163+
};
164+
break;
165+
case "sentiment":
166+
notionReviewObject = {
167+
"select": {
168+
"name": appInfoReviews.review_score_desc
169+
}
170+
};
171+
break;
172+
case "total":
173+
notionReviewObject = {
174+
"number": appInfoReviews.total_reviews
175+
};
176+
break;
177+
case "positive":
178+
notionReviewObject = {
179+
"number": appInfoReviews.total_positive
180+
};
181+
break;
182+
case "negative":
183+
notionReviewObject = {
184+
"number": appInfoReviews.total_negative
185+
};
186+
break;
187+
case "positive/negative":
188+
notionReviewObject = {
189+
"rich_text": [
190+
{
191+
"text": {
192+
"content": `${appInfoReviews.total_positive} positive / ${appInfoReviews.total_negative} negative`
193+
}
194+
}
195+
]
196+
};
197+
break;
198+
}
160199

161-
outputProperties[reviewScoreProperty.notionProperty] = {
162-
"number": steamReviewScore
163-
};
200+
outputProperties[reviewScoreProperty.notionProperty] = notionReviewObject;
164201

165202
return outputProperties;
166203
}

js/steamAPI.js

+15-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ export async function getSteamAppInfoDirect(appId, retryCount = 0) {
1717
return data[appId].data;
1818
}
1919
return null;
20-
}
21-
);
20+
});
2221

2322
// If the request failed, we try again
2423
if (!result && retryCount < 3) {
@@ -52,6 +51,20 @@ export async function getSteamAppInfoSteamUser(appIds) {
5251
});
5352
}
5453

54+
// Gets the current review score data for a game from the Steam reviews API
55+
export async function getSteamReviewScoreDirect(appId) {
56+
const result = await fetch(`https://store.steampowered.com/appreviews/${appId}?json=1&language=all`)
57+
.then(response => response.json())
58+
.then(data => {
59+
if (data?.success) {
60+
return data.query_summary;
61+
}
62+
return null;
63+
});
64+
65+
return result;
66+
}
67+
5568
export async function getSteamTagNames(storeTags, tagLanguage) {
5669
const tagIds = Object.keys(storeTags).map(function (key) {
5770
return storeTags[key];

js/utils.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const CONFIG = getConfig();
88
export const localDatabase = await loadLocalDatabase();
99
export const storeAPIRequired = isStoreAPIRequired();
1010
export const steamUserAPIRequired = isSteamUserAPIRequired();
11+
export const reviewAPIRequired = isReviewAPIRequired();
1112

1213
// ---------- Config ----------
1314

@@ -51,7 +52,7 @@ async function loadLocalDatabase() {
5152
// Reset the local database if the user wants to
5253
if (CONFIG.forceReset) {
5354
// Set a timer of 10 seconds to give the user time to cancel the reset
54-
console.log("Resetting local database in 10 seconds. Kill the process to cancel.");
55+
console.log("Resetting local database in 10 seconds. Kill the process to cancel (e.g. using Ctrl+C).");
5556
await new Promise(resolve => setTimeout(resolve, 10000));
5657
console.log("Resetting local database...\n");
5758
await db.clear();
@@ -91,10 +92,15 @@ function isStoreAPIRequired() {
9192
function isSteamUserAPIRequired() {
9293
return (
9394
CONFIG.gameProperties.gameName?.enabled ||
94-
CONFIG.gameProperties.reviewScore?.enabled ||
9595
CONFIG.gameProperties.tags?.enabled ||
9696
CONFIG.gameProperties.gameIcon?.enabled ||
9797
CONFIG.gameProperties.coverImage?.enabled ||
9898
CONFIG.gameProperties.steamDeckCompatibility?.enabled
9999
);
100+
}
101+
102+
function isReviewAPIRequired() {
103+
return (
104+
CONFIG.gameProperties.reviewScore?.enabled
105+
);
100106
}

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"name": "notion-steam-api-integration",
33
"type": "module",
4-
"version": "1.5.1",
5-
"description": "Notion integration to fetch data from the Steam API and populate a databse given Steam App ID's.",
4+
"version": "1.6.0",
5+
"description": "Notion integration to fetch data from the Steam API and populate a database given Steam App ID's.",
66
"main": "index.js",
77
"dependencies": {
88
"@notionhq/client": "^1.0.1",

0 commit comments

Comments
 (0)