Contents
Steam Games in my PDI
As a fun personal side project, I built a ranking system to rank my Steam Games in my PDI. For those non-gamers, Steam is a digital distribution platform developed by Valve Corporation. It was launched in 2003 and has become one of the largest platforms for purchasing, downloading, and playing video games.
Most of the games I own (126 in total) are on the Steam platform, so I will get my library of games from Steam. I don’t want to transpose my games from Steam into a manual database manually; I want to do it automatically. As the old saying goes, where there is a will, there is an API. I will fetch all my games into my PDI using an API.
In this blog series, I will share some challenges I faced in building this application integration and how I solved them.
Challenge 1: Getting a list of games I own into ServiceNow.
GetRecentlyPlayedGames API
Steam has an API to retrieve all the games a user owns, provided they allow the information to be shared publicly or with friends (it must be my friend). The API does not require authentication, making it relatively easy to work with. You will only need two things:
- Steam API Key
- Steam ID of the account
Example URL: http://api.steampowered.com/IPlayerService/GetRecentlyPlayedGames/v0001/?key=XXXXXXXXXXXXXXXXX&steamid=76561197960434622&format=json
The API key is attached to your account to retrieve your games regardless of your settings. To try this, request a Steam key here. You can view your Steam ID from your Steam profile page.
This returns something like this:
{ "response": { "game_count": 128, "games": [ { "appid": 10, "playtime_forever": 283, "playtime_windows_forever": 0, "playtime_mac_forever": 0, "playtime_linux_forever": 0, "playtime_deck_forever": 0, "rtime_last_played": 1349593200, "playtime_disconnected": 0 }, { "appid": 20, "playtime_forever": 0, "playtime_windows_forever": 0, "playtime_mac_forever": 0, "playtime_linux_forever": 0, "playtime_deck_forever": 0, "rtime_last_played": 0, "playtime_disconnected": 0 }, // ... and on it goes { "appid": 570, "playtime_forever": 18639, "playtime_windows_forever": 0, "playtime_mac_forever": 0, "playtime_linux_forever": 0, "playtime_deck_forever": 0, "rtime_last_played": 1561862072, "playtime_disconnected": 0 } ] } }
Flow Designer REST Action
Thanks to REST actions, working with REST in Flow Designer is straightforward. I’ve created steamkey and steamprofile inputs for my API key and steam, respectively. I added the include_free_games parameters as they are omitted by default.
The API has returned my entire Steam library, including games I have never played (48 in total!). No documented queries will let me filter this down, so I will have to do it with code.
JavaScript ES6 has some fantastic built-in functions for handling data, such as filter()
, map()
and sort()
. If you are in an Application scope (or Global in Xanadu) you can use these functions to make life easier.
Using this code, I can
- Remove games with less than 2 hours playtime
- Map only the data I need into a new object
- Sort by playtime descending
(function execute(inputs, outputs) { const games = JSON.parse(inputs.response); const result = games.response.games .filter(game => game.playtime_forever > 120) //played for more than 2 hours .map(game => ({ appid: game.appid, weight: game.playtime_forever })) .sort((a, b) => b.weight - a.weight); // Sort in descending order by playtime_forever outputs.apps = result; })(inputs, outputs);
AppDetails API
Now that I have the app IDs of all my games, I can use Steam’s app details API to retrieve further information about them. This API does not require a key and only requires the appid. For example, https://store.steampowered.com/api/appdetails/?appids=720 returns the following:
{ "730": { "success": true, "data": { "type": "game", "name": "Counter-Strike 2", "steam_appid": 730, "required_age": 0, "is_free": true, "dlc": [2678630], "detailed_description": "For over two decades, Counter-Strike has offered an elite competitive experience, one shaped by millions of players from across the globe. And now the next chapter in the CS story is about to begin. This is Counter-Strike 2.\u003Cbr\u003E\u003Cbr\u003EA free upgrade to CS:GO, Counter-Strike 2 marks the largest technical leap in Counter-Strikeās history. Built on the Source 2 engine, Counter-Strike 2 is modernized with realistic physically-based rendering, state of the art networking, and upgraded Community Workshop tools.\u003Cbr\u003E\u003Cbr\u003EIn addition to the classic objective-focused gameplay that Counter-Strike pioneered in 1999, Counter-Strike 2 features:\u003Cbr\u003E\u003Cbr\u003E\u003Cul class=\"bb_ul\"\u003E\u003Cli\u003EAll-new CS Ratings with the updated Premier mode\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003EGlobal and Regional leaderboards\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003EUpgraded and overhauled maps\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003EGame-changing dynamic smoke grenades\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003ETick-rate-independent gameplay\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003ERedesigned visual effects and audio\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003EAll items from CS:GO moving forward to CS2\u003C/li\u003E\u003C/ul\u003E", "about_the_game": "For over two decades, Counter-Strike has offered an elite competitive experience, one shaped by millions of players from across the globe. And now the next chapter in the CS story is about to begin. This is Counter-Strike 2.\u003Cbr\u003E\u003Cbr\u003EA free upgrade to CS:GO, Counter-Strike 2 marks the largest technical leap in Counter-Strikeās history. Built on the Source 2 engine, Counter-Strike 2 is modernized with realistic physically-based rendering, state of the art networking, and upgraded Community Workshop tools.\u003Cbr\u003E\u003Cbr\u003EIn addition to the classic objective-focused gameplay that Counter-Strike pioneered in 1999, Counter-Strike 2 features:\u003Cbr\u003E\u003Cbr\u003E\u003Cul class=\"bb_ul\"\u003E\u003Cli\u003EAll-new CS Ratings with the updated Premier mode\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003EGlobal and Regional leaderboards\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003EUpgraded and overhauled maps\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003EGame-changing dynamic smoke grenades\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003ETick-rate-independent gameplay\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003ERedesigned visual effects and audio\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003EAll items from CS:GO moving forward to CS2\u003C/li\u003E\u003C/ul\u003E", "short_description": "For over two decades, Counter-Strike has offered an elite competitive experience, one shaped by millions of players from across the globe. And now the next chapter in the CS story is about to begin. This is Counter-Strike 2.", "supported_languages": "Czech, Danish, Dutch, English\u003Cstrong\u003E*\u003C/strong\u003E, Finnish, French, German, Hungarian, Italian, Japanese, Korean, Norwegian, Polish, Portuguese - Portugal, Portuguese - Brazil, Romanian, Russian, Simplified Chinese, Spanish - Spain, Swedish, Thai, Traditional Chinese, Turkish, Bulgarian, Ukrainian, Greek, Spanish - Latin America, Vietnamese, Indonesian\u003Cbr\u003E\u003Cstrong\u003E*\u003C/strong\u003Elanguages with full audio support", "header_image": "https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/730/header.jpg?t=1719426374", "capsule_image": "https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/730/capsule_231x87.jpg?t=1719426374", "capsule_imagev5": "https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/730/capsule_184x69.jpg?t=1719426374", "website": "http://counter-strike.net/", "pc_requirements": { "minimum": "\u003Cstrong\u003EMinimum:\u003C/strong\u003E\u003Cbr\u003E\u003Cul class=\"bb_ul\"\u003E\u003Cli\u003E\u003Cstrong\u003EOS:\u003C/strong\u003E WindowsĀ® 10\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003E\u003Cstrong\u003EProcessor:\u003C/strong\u003E 4 hardware CPU threads - IntelĀ® Coreā¢ i5 750 or higher\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003E\u003Cstrong\u003EMemory:\u003C/strong\u003E 8 GB RAM\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003E\u003Cstrong\u003EGraphics:\u003C/strong\u003E Video card must be 1 GB or more and should be a DirectX 11-compatible with support for Shader Model 5.0\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003E\u003Cstrong\u003EDirectX:\u003C/strong\u003E Version 11\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003E\u003Cstrong\u003EStorage:\u003C/strong\u003E 85 GB available space\u003C/li\u003E\u003C/ul\u003E" }, "mac_requirements": { "minimum": "\u003Cstrong\u003EMinimum:\u003C/strong\u003E\u003Cbr\u003E\u003Cul class=\"bb_ul\"\u003E\u003Cli\u003E\u003Cstrong\u003EOS:\u003C/strong\u003E MacOS X 10.11 (El Capitan) or later\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003E\u003Cstrong\u003EProcessor:\u003C/strong\u003E Intel Core Duo Processor (2GHz or better)\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003E\u003Cstrong\u003EMemory:\u003C/strong\u003E 2 GB RAM\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003E\u003Cstrong\u003EGraphics:\u003C/strong\u003E ATI Radeon HD 2400 or better / NVIDIA 8600M or better\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003E\u003Cstrong\u003EStorage:\u003C/strong\u003E 15 GB available space\u003C/li\u003E\u003C/ul\u003E" }, "linux_requirements": { "minimum": "\u003Cstrong\u003EMinimum:\u003C/strong\u003E\u003Cbr\u003E\u003Cul class=\"bb_ul\"\u003E\u003Cli\u003E\u003Cstrong\u003EOS:\u003C/strong\u003E Ubuntu 20.04\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003E\u003Cstrong\u003EProcessor:\u003C/strong\u003E 4 hardware CPU threads - IntelĀ® Coreā¢ i5 750 or higher\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003E\u003Cstrong\u003EMemory:\u003C/strong\u003E 8 GB RAM\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003E\u003Cstrong\u003EGraphics:\u003C/strong\u003E AMD GCN+ or NVIDIA Kepler+ with up-to-date Vulkan drivers. Support for VK_EXT_graphics_pipeline_library highly recommended.\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003E\u003Cstrong\u003EStorage:\u003C/strong\u003E 85 GB available space\u003Cbr\u003E\u003C/li\u003E\u003Cli\u003E\u003Cstrong\u003ESound Card:\u003C/strong\u003E Highly recommended\u003C/li\u003E\u003C/ul\u003E" }, "developers": [ "Valve" ], "publishers": [ "Valve" ], "packages": [329385, 298963, 54029], "package_groups": [ { "name": "default", "title": "Buy Counter-Strike 2", "description": "", "selection_text": "Select a purchase option", "save_text": "", "display_type": 0, "is_recurring_subscription": "false", "subs": [ { "packageid": 298963, "percent_savings_text": " ", "percent_savings": 0, "option_text": "Counter-Strike 2 - Free", "option_description": "", "can_get_free_license": "0", "is_free_license": true, "price_in_cents_with_discount": 0 }, { "packageid": 54029, "percent_savings_text": " ", "percent_savings": 0, "option_text": "Prime Status Upgrade - A$ 23.25", "option_description": "", "can_get_free_license": "0", "is_free_license": false, "price_in_cents_with_discount": 2325 } ] } ], "platforms": { "windows": true, "mac": false, "linux": true }, "categories": [ { "id": 1, "description": "Multi-player" }, // ... and on it goes { "id": 63, "description": "Steam Timeline" } ], "genres": [ { "id": "1", "description": "Action" }, { "id": "37", "description": "Free To Play" } ], "screenshots": [ { "id": 0, "path_thumbnail": "https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/730/ss_796601d9d67faf53486eeb26d0724347cea67ddc.600x338.jpg?t=1719426374", "path_full": "https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/730/ss_796601d9d67faf53486eeb26d0724347cea67ddc.1920x1080.jpg?t=1719426374" }, { "id": 1, "path_thumbnail": "https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/730/ss_d830cfd0550fbb64d80e803e93c929c3abb02056.600x338.jpg?t=1719426374", "path_full": "https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/730/ss_d830cfd0550fbb64d80e803e93c929c3abb02056.1920x1080.jpg?t=1719426374" }, // ... and on it goes { "id": 18, "path_thumbnail": "https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/730/ss_2514675f364079b754b820cbc8b2e7c331d56a26.600x338.jpg?t=1719426374", "path_full": "https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/730/ss_2514675f364079b754b820cbc8b2e7c331d56a26.1920x1080.jpg?t=1719426374" } ], "movies": [ { "id": 256972298, "name": "Launch Trailer", "thumbnail": "https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/256972298/movie.293x165.jpg?t=1696005467", "webm": { "480": "http://video.akamai.steamstatic.com/store_trailers/256972298/movie480_vp9.webm?t=1696005467", "max": "http://video.akamai.steamstatic.com/store_trailers/256972298/movie_max_vp9.webm?t=1696005467" }, "mp4": { "480": "http://video.akamai.steamstatic.com/store_trailers/256972298/movie480.mp4?t=1696005467", "max": "http://video.akamai.steamstatic.com/store_trailers/256972298/movie_max.mp4?t=1696005467" }, "highlight": true }, // ... and on it goes { "id": 256969690, "name": "Responsive Smokes", "thumbnail": "https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/256969690/movie.293x165.jpg?t=1696005491", "webm": { "480": "http://video.akamai.steamstatic.com/store_trailers/256969690/movie480_vp9.webm?t=1696005491", "max": "http://video.akamai.steamstatic.com/store_trailers/256969690/movie_max_vp9.webm?t=1696005491" }, "mp4": { "480": "http://video.akamai.steamstatic.com/store_trailers/256969690/movie480.mp4?t=1696005491", "max": "http://video.akamai.steamstatic.com/store_trailers/256969690/movie_max.mp4?t=1696005491" }, "highlight": true } ], "recommendations": { "total": 4199652 }, "achievements": { "total": 1, "highlighted": [ { "name": "A New Beginning", "path": "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/730/f75dd04fa12445a8ec43be65fa16ff1b8d2bf82e.jpg" } ] }, "release_date": { "coming_soon": false, "date": "22 Aug, 2012" }, "support_info": { "url": "", "email": "" }, "background": "https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/730/page_bg_generated_v6b.jpg?t=1719426374", "background_raw": "https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/730/page.bg.jpg?t=1719426374", "content_descriptors": { "ids": [2, 5], "notes": "Includes intense violence and blood." }, "ratings": { "usk": { "rating": "16", "descriptors": "Gewalt; In-Game KƤufe (+zufƤllige Objekte); Chats" }, "agcom": { "rating": "16", "descriptors": "Violenza" }, "cadpa": { "rating": "12" }, "dejus": { "rating": "16", "descriptors": "ViolĆŖncia" } } } } }
The REST step setup is pretty basic, like last time, using appid
as an input into my action:
The meat of the data I want is nested in the data property, so I will need to create a script action to extract it. I also noticed the GetRecentlyOwnedGames API was retrieving non-games, such as videos and music. I want to throw an error when this occurs.
And the code for reference:
(function execute(inputs, outputs) { var responsePayload = JSON.parse(inputs.response); var gameDataObj = responsePayload[inputs.appid + ""].data; var gameStr = JSON.stringify(gameDataObj); outputs.game = gameDataObj; outputs.gamedata = gameStr; if (gameDataObj.type != "game") { // throw some sort of error to prevent non-game being created throw("Not a game. Ignoring") } })(inputs, outputs);
I can use the data structure return within the data property as an example to parse the data into a JSON object.
Now, I can insert the relevant details into my ServiceNow database using the Create or Update Record step, inserting a record in the Steam Game table I created earlier.
My Games in Service Portal
With some other work behind the scenes, I can now view my Steam Games in my PDI:
Conclusion
Creating homegrown solutions in your PDI is a great way to learn and practice ServiceNow capabilities. It was the first time I had used the JSON Parser step, which reinforced my learning of some of the new ES6 API. Why not give something a go in your PDI? Don’t forget to back up your work to GIT!