Skip to content

Commit

Permalink
Added additional format options for review scores (#57)
Browse files Browse the repository at this point in the history
  • Loading branch information
NikkelM authored Jul 5, 2024
1 parent 573458a commit 1bdc0c6
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 31 deletions.
28 changes: 19 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ Which game properties should be fetched when a new Steam game is detected, and t
},
"reviewScore": {
"enabled": true,
"format": "percentage",
"notionProperty": "Review Score"
},
"tags": {
Expand All @@ -153,7 +154,7 @@ Which game properties should be fetched when a new Steam game is detected, and t
<details>
<summary><code>gameName</code></summary>

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

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

The release date of the game. The database field in Notion must be of type "Date".
The release date of the game. The database field in Notion must be of type `Date`.

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

The user review score from 0-100. The database field in Notion must be of type "Number".
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".

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

<h4><code>format</code></h4>

How the review score should be formatted.

| Type | Default value | Possible values | Required |
|---|---|---|---|
| `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 |

<h4><code>notionProperty</code></h4>

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

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".
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`.

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

<h4><code>notionProperty</code></h4>

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

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

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

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

The URL to the store page of the game. The database field in Notion must be of type "URL".
The URL to the store page of the game. The database field in Notion must be of type `URL`.

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

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".
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`.

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

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".
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`.

| Type | Default value | Possible values | Required |
|---|---|---|---|
Expand Down
1 change: 1 addition & 0 deletions config.default.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
},
"reviewScore": {
"enabled": true,
"format": "percentage",
"notionProperty": "Review Score"
},
"tags": {
Expand Down
36 changes: 35 additions & 1 deletion config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
},
"reviewScore": {
"enabled": true,
"format": "percentage",
"notionProperty": "Review Score"
},
"tags": {
Expand Down Expand Up @@ -199,10 +200,11 @@
]
},
"reviewScore": {
"description": "The user review score from 0-100. The database field in Notion must be of type \"Number\".",
"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\".",
"type": "object",
"default": {
"enabled": true,
"format": "percentage",
"notionProperty": "Review Score"
},
"additionalProperties": false,
Expand All @@ -212,6 +214,37 @@
"type": "boolean",
"default": true
},
"format": {
"description": "How the review score should be formatted.",
"type": "string",
"default": "percentage",
"oneOf": [
{
"const": "percentage",
"title": "Notion database field type: \"Number\". A percentage value formatted as a float from 0.00-1.00."
},
{
"const": "sentiment",
"title": "Notion database field type: \"Select\". A sentiment value such as \"Overwhelmingly Positive\" or \"Mixed\"."
},
{
"const": "total",
"title": "Notion database field type: \"Number\". The total number of reviews submitted for the game, across all languages."
},
{
"const": "positive",
"title": "Notion database field type: \"Number\". The total number of positive reviews submitted for the game, across all languages."
},
{
"const": "negative",
"title": "Notion database field type: \"Number\". The total number of negative reviews submitted for the game, across all languages."
},
{
"const": "positive/negative",
"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\"."
}
]
},
"notionProperty": {
"description": "The name of the Notion property to set the user review score in.",
"type": "string",
Expand All @@ -220,6 +253,7 @@
},
"required": [
"enabled",
"format",
"notionProperty"
]
},
Expand Down
13 changes: 9 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
// Suppresses the warning about the fetch API being unstable
process.removeAllListeners('warning');

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

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

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

// 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
Expand Down Expand Up @@ -90,7 +90,12 @@ async function updateNotionDatabase() {
? await getSteamAppInfoDirect(steamAppId)
: null;

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

let notionProperties = await getGameProperties(appInfoDirect, appInfoSteamUser[steamAppId], appInfoReviews, steamAppId);

updateNotionPage(pageId, notionProperties);
addGameToLocalDatabase(pageId, steamAppId);
Expand Down
55 changes: 46 additions & 9 deletions js/gameProperties.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getSteamTagNames } from './steamAPI.js';
import { CONFIG } from './utils.js';

export async function getGameProperties(appInfoDirect, appInfoSteamUser, steamAppId) {
export async function getGameProperties(appInfoDirect, appInfoSteamUser, appInfoReviews, steamAppId) {
let outputProperties = {};
let cover;
let icon;
Expand All @@ -16,7 +16,7 @@ export async function getGameProperties(appInfoDirect, appInfoSteamUser, steamAp
outputProperties = getGameReleaseDate(propertyValue, appInfoDirect, outputProperties);
break;
case "reviewScore":
outputProperties = getGameReviewScore(propertyValue, appInfoSteamUser, outputProperties);
outputProperties = getGameReviewScore(propertyValue, appInfoReviews, outputProperties);
break;
case "tags":
outputProperties = await getGameTags(propertyValue, appInfoSteamUser, outputProperties);
Expand Down Expand Up @@ -152,15 +152,52 @@ function getGameReleaseDate(releaseDateProperty, appInfoDirect, outputProperties
return outputProperties;
}

function getGameReviewScore(reviewScoreProperty, appInfoSteamUser, outputProperties) {
if (!reviewScoreProperty.enabled || !appInfoSteamUser.review_percentage) { return outputProperties; }
function getGameReviewScore(reviewScoreProperty, appInfoReviews, outputProperties) {
if (!reviewScoreProperty.enabled || !appInfoReviews) { return outputProperties; }
let notionReviewObject;

// The reviewScore is not available through the Steam store API, so we have to use the SteamUser API instead
const steamReviewScore = parseInt(appInfoSteamUser.review_percentage) / 100;
switch (reviewScoreProperty.format) {
case "percentage":
notionReviewObject = {
"number": parseFloat((appInfoReviews.total_positive / appInfoReviews.total_reviews).toFixed(2))
};
break;
case "sentiment":
notionReviewObject = {
"select": {
"name": appInfoReviews.review_score_desc
}
};
break;
case "total":
notionReviewObject = {
"number": appInfoReviews.total_reviews
};
break;
case "positive":
notionReviewObject = {
"number": appInfoReviews.total_positive
};
break;
case "negative":
notionReviewObject = {
"number": appInfoReviews.total_negative
};
break;
case "positive/negative":
notionReviewObject = {
"rich_text": [
{
"text": {
"content": `${appInfoReviews.total_positive} positive / ${appInfoReviews.total_negative} negative`
}
}
]
};
break;
}

outputProperties[reviewScoreProperty.notionProperty] = {
"number": steamReviewScore
};
outputProperties[reviewScoreProperty.notionProperty] = notionReviewObject;

return outputProperties;
}
Expand Down
17 changes: 15 additions & 2 deletions js/steamAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ export async function getSteamAppInfoDirect(appId, retryCount = 0) {
return data[appId].data;
}
return null;
}
);
});

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

// Gets the current review score data for a game from the Steam reviews API
export async function getSteamReviewScoreDirect(appId) {
const result = await fetch(`https://store.steampowered.com/appreviews/${appId}?json=1&language=all`)
.then(response => response.json())
.then(data => {
if (data?.success) {
return data.query_summary;
}
return null;
});

return result;
}

export async function getSteamTagNames(storeTags, tagLanguage) {
const tagIds = Object.keys(storeTags).map(function (key) {
return storeTags[key];
Expand Down
10 changes: 8 additions & 2 deletions js/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const CONFIG = getConfig();
export const localDatabase = await loadLocalDatabase();
export const storeAPIRequired = isStoreAPIRequired();
export const steamUserAPIRequired = isSteamUserAPIRequired();
export const reviewAPIRequired = isReviewAPIRequired();

// ---------- Config ----------

Expand Down Expand Up @@ -51,7 +52,7 @@ async function loadLocalDatabase() {
// Reset the local database if the user wants to
if (CONFIG.forceReset) {
// Set a timer of 10 seconds to give the user time to cancel the reset
console.log("Resetting local database in 10 seconds. Kill the process to cancel.");
console.log("Resetting local database in 10 seconds. Kill the process to cancel (e.g. using Ctrl+C).");
await new Promise(resolve => setTimeout(resolve, 10000));
console.log("Resetting local database...\n");
await db.clear();
Expand Down Expand Up @@ -91,10 +92,15 @@ function isStoreAPIRequired() {
function isSteamUserAPIRequired() {
return (
CONFIG.gameProperties.gameName?.enabled ||
CONFIG.gameProperties.reviewScore?.enabled ||
CONFIG.gameProperties.tags?.enabled ||
CONFIG.gameProperties.gameIcon?.enabled ||
CONFIG.gameProperties.coverImage?.enabled ||
CONFIG.gameProperties.steamDeckCompatibility?.enabled
);
}

function isReviewAPIRequired() {
return (
CONFIG.gameProperties.reviewScore?.enabled
);
}
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "notion-steam-api-integration",
"type": "module",
"version": "1.5.1",
"description": "Notion integration to fetch data from the Steam API and populate a databse given Steam App ID's.",
"version": "1.6.0",
"description": "Notion integration to fetch data from the Steam API and populate a database given Steam App ID's.",
"main": "index.js",
"dependencies": {
"@notionhq/client": "^1.0.1",
Expand Down

0 comments on commit 1bdc0c6

Please sign in to comment.