-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(plugins/steam): add plugin (#1400) [skip ci]
- Loading branch information
1 parent
7870932
commit b85fa23
Showing
14 changed files
with
2,825 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,37 @@ | ||
appid | ||
apikey | ||
apiname | ||
appdetails | ||
appids | ||
appinfo | ||
deno | ||
gpgarmor | ||
github | ||
githubassets | ||
https | ||
IPlayer | ||
ISteam | ||
leetcode | ||
Nie | ||
npx | ||
personaname | ||
pgn | ||
playerstats | ||
rtime | ||
scm | ||
shas | ||
splatoon | ||
Splatnet | ||
ssh | ||
statink | ||
STATINK | ||
steamcommunity | ||
steamid | ||
steamids | ||
steampowered | ||
timecreated | ||
ubuntu | ||
unlocktime | ||
userid | ||
yargsparser | ||
webtoken |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<!--header--> | ||
<!--/header--> | ||
|
||
## ➡️ Available options | ||
|
||
<!--options--> | ||
<!--/options--> | ||
|
||
## 🗝️ Obtaining a *Steam Web API* token | ||
|
||
Go to [steamcommunity.com/dev/apikey](https://steamcommunity.com/dev/apikey) to obtain a Steam Web API token: | ||
|
||
![Token](/.github/readme/imgs/plugin_steam_webtoken.png) | ||
|
||
To retrieve your Steam ID, access your user account on [store.steampowered.com/account](https://store.steampowered.com/account) and copy the identifier located behind the header: | ||
|
||
![User ID](/.github/readme/imgs/plugin_steam_userid.png) | ||
|
||
## ℹ️ Examples workflows | ||
|
||
<!--examples--> | ||
<!--/examples--> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
- name: Recently played games | ||
uses: lowlighter/metrics@latest | ||
with: | ||
filename: metrics.plugin.steam.svg | ||
token: NOT_NEEDED | ||
base: "" | ||
plugin_steam_token: ${{ secrets.STEAM_TOKEN }} | ||
plugin_steam: yes | ||
plugin_steam_user: 0 | ||
plugin_steam_sections: recently-played | ||
plugin_steam_achievements_limit: 0 | ||
prod: | ||
# ⚠️ Using mocked data for privacy reasons | ||
with: | ||
plugin_steam_token: MOCKED_TOKEN | ||
use_mocked_data: yes | ||
|
||
- name: Profile and detailed game history | ||
uses: lowlighter/metrics@latest | ||
with: | ||
filename: metrics.plugin.steam.full.svg | ||
token: NOT_NEEDED | ||
base: "" | ||
plugin_steam_token: ${{ secrets.STEAM_TOKEN }} | ||
plugin_steam: yes | ||
plugin_steam_user: 0 | ||
prod: | ||
# ⚠️ Using mocked data for privacy reasons | ||
with: | ||
plugin_steam_token: MOCKED_TOKEN | ||
use_mocked_data: yes |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
//Setup | ||
export default async function({login, q, imports, data, account}, {token, enabled = false, extras = false} = {}) { | ||
//Plugin execution | ||
try { | ||
//Check if plugin is enabled and requirements are met | ||
if ((!q.steam) || (!imports.metadata.plugins.steam.enabled(enabled, {extras}))) | ||
return null | ||
|
||
//Load inputs | ||
let {user, sections, "games.ignored": _games_ignored, "games.limit": _games_limit, "recent.games.limit": _recent_games_limit, "achievements.limit": _achievements_limit, "playtime.threshold": _playtime_threshold} = imports.metadata.plugins.steam.inputs({data, account, q}) | ||
|
||
const urls = { | ||
games: { | ||
owned: `https://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?key=${token}&steamid=${user}&format=json&include_appinfo=1`, | ||
schema: `https://api.steampowered.com/ISteamUserStats/GetSchemaForGame/v0002/?key=${token}&format=json`, | ||
details: "https://store.steampowered.com/api/appdetails?", | ||
}, | ||
player: { | ||
summary: `https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=${token}&steamids=${user}&format=json`, | ||
level: `https://api.steampowered.com/IPlayerService/GetSteamLevel/v1/?key=${token}&steamid=${user}&format=json`, | ||
achievement: `https://api.steampowered.com/ISteamUserStats/GetPlayerAchievements/v0001/?key=${token}&steamid=${user}&format=json&l=en`, | ||
}, | ||
} | ||
const result = {sections, player: null, games: {count: 0, playtime: 0, achievements: 0}} | ||
|
||
//Fetch owned games | ||
console.debug(`metrics/compute/${login}/plugins > steam > fetching owned games`) | ||
let {data: {response: {game_count: count, games}}} = await imports.axios.get(urls.games.owned) | ||
result.games.count = count | ||
result.games.playtime = games.reduce((total, {playtime_forever: playtime}) => (total += playtime), 0) / 60 | ||
|
||
//Fetch game achievements and order games by section | ||
for (const section of ["most-played", "recently-played"]) { | ||
if (!sections.includes(section)) | ||
continue | ||
result.games[section] = await Promise.all( | ||
games | ||
.map(({appid: id, name, img_icon_url: icon, playtime_forever: playtime, rtime_last_played: played}) => ({id, name, icon: `http://media.steampowered.com/steamcommunity/public/images/apps/${id}/${icon}.jpg`, playtime: playtime / 60, played})) | ||
.filter(({playtime}) => (playtime >= _playtime_threshold)) | ||
.filter(({id}) => (!_games_ignored.includes(`${id}`))) | ||
.sort((a, b) => ({"most-played": (b.playtime - a.playtime), "recently-played": (b.played - a.played)}[section])) | ||
.slice(0, ({"most-played": _games_limit, "recently-played": _recent_games_limit}[section]) || Infinity) | ||
.map(async game => { | ||
const schema = {} | ||
try { | ||
console.debug(`metrics/compute/${login}/plugins > steam > fetching schema for "${game.name}" (${game.id})`) | ||
const {data: {game: {availableGameStats: {achievements = []} = {}}}} = await imports.axios.get(`${urls.games.schema}&appid=${game.id}`) | ||
Object.assign(schema, Object.fromEntries(achievements.map(({name, icon}) => [name, {icon}]))) | ||
} | ||
catch (error) { | ||
console.debug(`metrics/compute/${login}/plugins > steam > failed to get schema for "${game.name}" (${game.id}) > ${error}`) | ||
} | ||
const about = {} | ||
try { | ||
console.debug(`metrics/compute/${login}/plugins > steam > fetching details for "${game.name}" (${game.id})`) | ||
const {data: {[game.id]: {data}}} = await imports.axios.get(`${urls.games.details}&appids=${game.id}`) | ||
about.description = data.short_description ?? "" | ||
about.genres = data.genres?.map(({description}) => description) ?? [] | ||
} | ||
catch (error) { | ||
console.debug(`metrics/compute/${login}/plugins > steam > failed to get details for "${game.name}" (${game.id}) > ${error}`) | ||
} | ||
|
||
let achievements = [] | ||
const rate = {total: Object.keys(schema).length, achieved: 0} | ||
try { | ||
console.debug(`metrics/compute/${login}/plugins > steam > fetching player achievements "${game.name}" (${game.id})`) | ||
let {data: {playerstats: {achievements: list = []}}} = await imports.axios.get(`${urls.player.achievement}&appid=${game.id}`) | ||
achievements = await Promise.all(list.map(async ({apiname: id, achieved, unlocktime: unlocked, name, description}) => ({icon: await imports.imgb64(schema[id]?.icon ?? null, {width: 32, height: 32}), achieved: !!achieved, unlocked, name, description, id}))) | ||
achievements = achievements.sort((a, b) => (b.unlocked - a.unlocked)) | ||
rate.achieved = achievements.filter(({achieved}) => achieved).length | ||
achievements = achievements.slice(0, _achievements_limit) | ||
} | ||
catch (error) { | ||
console.debug(`metrics/compute/${login}/plugins > steam > failed to get player achievements for "${game.name}" (${game.id}) > ${error}`) | ||
} | ||
return {...game, ...about, icon: await imports.imgb64(game.icon, {width: 64, height: 64}), achievements, rate} | ||
}), | ||
) | ||
} | ||
|
||
//Fetch player info | ||
if (sections.includes("player")) { | ||
console.debug(`metrics/compute/${login}/plugins > steam > fetching profile info`) | ||
let {data: {response: {players: [info]}}} = await imports.axios.get(urls.player.summary) | ||
console.debug(`metrics/compute/${login}/plugins > steam > fetching profile level`) | ||
const {data: {response: {player_level: level}}} = await imports.axios.get(urls.player.level) | ||
result.player = { | ||
level, | ||
avatar: await imports.imgb64(info.avatar, {width: 64, height: 64}), | ||
created: info.timecreated, | ||
name: info.personaname, | ||
} | ||
} | ||
|
||
//Results | ||
console.log(JSON.stringify(result)) | ||
return result | ||
} | ||
//Handle errors | ||
catch (error) { | ||
throw imports.format.error(error) | ||
} | ||
} |
Oops, something went wrong.