During my day job I tend to get into situations where I have to make PowerShell connect to systems and applications that are atypical. A lot times, the easiest way to integrate PowerShell and these systems is the humble invoke-webrequest. It’s not a big stretch to bring that home to the ‘fun’ projects. In that vein – let’s make PowerShell talk to Steam – because ‘Why Not?’.
Steam has a pretty well documented web api, and as such, this is a pretty easy integration to get get going. I will mostly be using invoke-webrequest, since the API we are dealing is the Steam Web API. The API is mainly documented here. Another GREAT resource is this site. I won’t dig into the client side API in this post – that post is coming soon.
Let’s start by doing something simple. Let’s get a list of the api interfaces. A simple invoke-webrequest can get the data. By default, the Steam Web Api will return data in a json format, but for our demonstration I am going to get it in an XML format.
[xml]$interfaces = Invoke-WebRequest http://api.steampowered.com/ISteamWebAPIUtil/GetSupportedAPIList/v1/?format=xml -UseBasicParsing
I am using the ‘-UseBasicParsing’ switch in order to not have to initialize the IE engine and avoid performing IE initial setup. You will notice that I use the [xml] prefix to tell PowerShell to expect an XML return, and in the url I specify the xml format. Other valid formats to use in the url include json (default if nothing is specified), and vdf. If we examine the $interfaces variable, we can eventually dig down to this:
PS C:\Users\Draith> $interfaces.apilist.interfaces.interface name methods ---- ------- IGCVersion_205790 methods IGCVersion_440 methods IGCVersion_570 methods IGCVersion_730 methods IPortal2Leaderboards_620 methods ISteamApps methods ISteamBitPay methods ISteamDirectory methods ISteamEnvoy methods ISteamNews methods ISteamPayPalPaymentsHub methods ISteamRemoteStorage methods ISteamUserAuth methods ISteamUserOAuth methods ISteamUserStats methods ISteamWebAPIUtil methods ISteamWebUserPresenceOAuth methods ITFSystem_440 methods IPlayerService methods IAccountRecoveryService methods
Lots of interfaces here, but one immediately catches the eye – ISteamApps. How do we access that one? Well, we would need to look at the methods associated with that interface.
PS C:\Users\Draith> $interfaces.apilist.interfaces.interface|Where-Object {$_.name -eq 'ISteamApps'}|Select-Object -ExpandProperty methods method ------ {GetAppList, GetAppList, GetServersAtAddress, UpToDateCheck}
There we go – looks like 2 GetAppList methods, and 2 others. If we were to dig a bit deeper, we would see that the GetAppList methods are version 1 and version 2. Let’s use version 2 for this example. Now that we know the Interface (ISteamApps) and the method (GetAppList), we can craft the web request as documented in the Steam Web Api.
[xml]$apps = invoke-webrequest -uri 'http://api.steampowered.com/ISteamApps/GetAppList/v0002/?format=xml' -UseBasicParsing
This is a very similar request to the first one we used to get the interfaces, only we are now supplying the interface and the method. Notice the v0002 portion – it’s the version. Again we told the request to use the XML format.
PS C:\Users\Draith> $apps.applist.apps.app appid name ----- ---- 5 Dedicated Server 7 Steam Client 8 winui2 10 Counter-Strike 20 Team Fortress Classic 30 Day of Defeat 40 Deathmatch Classic 50 Half-Life: Opposing Force
<and many many many more>
Wow – we just pulled back a list of every game published to Steam. I am not sure what the status has to be (Alpha, Beta, Pre-Order, etc..), but you can see the list is very extensive – almost 29000 games when I wrote this post. We stored this data in the $app variable so we only have to pull it once (really don’t want to tick off Gabe by pulling this multiple times).
Now that we have this, what good is it? Well, for a good portion of the Web Api’s, we will need to know an AppID. If we wanted to get the AppID for a specific game, we can do something like this:
PS C:\Users\Draith> $apps.applist.apps.app|Where-Object {$_.name -like '*kerbal*'} appid name ----- ---- 220200 Kerbal Space Program 231410 Kerbal Space Program Demo
There we go – Kerbal Space Program (KSP) has AppID of 220200 for the main (non-demo) game. Great! Why don’t get see if we can get the latest news on KSP? Remember our $interfaces variable? One of the interfaces was ISteamNews. Looking at the methods on ISteamNews we find this:
PS C:\Users\Draith>$interfaces.apilist.interfaces.interface|Where-Object {$_.name -eq 'ISteamNews'}|Select-Object -ExpandProperty methods method ------ {GetNewsForApp, GetNewsForApp}
Look familiar? As you can guess, there is a v0001 and v0002 version of the same method. Crafting the URL is pretty easy at this point, except when we try like we did previously, we get this:
PS C:\Users\Draith> invoke-webrequest -uri 'http://api.steampowered.com/ISteamNews/GetNewsForApp/v0002/?format=xml' -UseBasicParsing invoke-webrequest : Bad RequestBad RequestPlease verify that all required parameters are being sent At line:1 char:1 + invoke-webrequest -uri 'http://api.steampowered.com/ISteamNews/GetNew ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
Apparently there is a parameter that is missing. How do we find that? Well, we have to dig in a bit:
PS C:\Users\Draith> $interfaces.apilist.interfaces.interface|Where-Object {$_.name -eq 'ISteamNews'}|Select-Object -ExpandProperty methods|Select-Object -ExpandProperty method|Where-Object {$_.version -eq 2}|Select-Object -ExpandProperty parameters|Select-Object -ExpandProperty parameter name type optional description ---- ---- -------- ----------- appid uint32 false AppID to retrieve news for maxlength uint32 true Maximum length for the content to return, if this is 0 the full content is returned, if it's less then a blurb is generated to fit. enddate uint32 true Retrieve posts earlier than this date (unix epoch timestamp) count uint32 true # of posts to retrieve (default 20) feeds string true Comma-seperated list of feed names to return news for
There we go – a list of the parameters that the interface expects. The only mandatory one (optional = false) is the appid. Let’s try our web request again, but supply the appid this time:
[xml]$kspnews = invoke-webrequest -uri 'http://api.steampowered.com/ISteamNews/GetNewsforapp/v0002/?appid=220200&format=xml' -UseBasicParsing
Notice how we have the interface set(ISteamNews) and the method (GetNewsForApp), and the version (v0002), as well as supplied the appid (220200). That leaves us with something like this:
PS C:\Users\Draith> $kspnews.appnews.newsitems.newsitem gid : 252584970553375805 title : What No Man's Sky could learn about exploration from Kerbal Space Program url : http://store.steampowered.com/news/externalpost/pcgamer/252584970553375805 is_external_url : true author : contents : To get a jump start on No Man s Sky s space sandbox, some of our staff are playing the game on PS4 this week. Read more of our different opinions on the game as we continue to play. My journey in No Man's Sky has so far been like going to a thri
<And much much more>
Great – what do we actually have? If we go a gettype() on $kspnews.appnews.newsitems.newsitem we find it’s an array. That makes it fairly simple to manipulate via the pipeline:
PS C:\Users\Draith> $kspnews.appnews.newsitems.newsitem|foreach{$_.title} What No Man's Sky could learn about exploration from Kerbal Space Program PC Gamer UK Podcast 019: Top 100 picks Kerbal Space Program patch 1.1.3 is now available! Kerbal Space Program Lead Dev Quits For Planets New Kerbal Space Program lead developer calls it quits Patch 1.1.2 is now available! Kerbal Space Program patch 1.1.1 is now available! Kerbal Space Program Launches Patch 1.1 Kerbal Space Program update 1.1 ???Turbo Charged??? is now available! Midweek Madness - Kerbal Space Program, 40% Off The Games That Got Away In 2015 Best Simulation 2015 Kerbal Space Program Weekend Deal - Kerbal Space Program, 40% Off The RPS Advent Calendar, Dec 13th: Kerbal Space Program SteamController Support for KSP! KSP 1.0.5 is Live Kerbal Space Program update 1.0.5 adds sea landings, gives Val a face plate We won the Golden Joystick for best indie game.. Daily Deal - Kerbal Space Program, 40% Off Two Unity Awards! Thank you for voting
It’s a small leap to pull back the details for a particular news article. For example, if we wanted to see the details on the 1.1.2 patch, we can do this:
PS C:\Users\Draith> $kspnews.appnews.newsitems.newsitem|Where-Object{$_.title -eq 'Patch 1.1.2 is now available!'}|Select-Object -ExpandProperty contents [img]http://i.imgur.com/plC31xC.png[/img] Hello everyone! We noticed a number of issues persisted through the 1.1.1 patch earlier this week. We???re releasing patch 1.1.2 to address these issues before we head off to a long overdue vacation for the next couple of weeks. Patch 1.1.2 addresses issues with the user interface and landing legs, amongst others. Check out the full changelog on [url=http://forum.kerbalspaceprogram.com/index.php?/developerarticles.html/kerbal-space-program-patch-112-is-now-live-r191/]our forums[/url]. The Patch will start downloading through your Steam client automatically.
Pretty nifty – you can manipulate the content from there as normal html.
Getting the news is one thing, but what if you wanted to get a bit more data – something a little more exciting like the number of players actually _playing_ the game at the moment. Again – a simple invoke-webrequest can pull that – this time using the ISteamUserStats interface:
PS C:\Users\Draith> (Invoke-WebRequest -uri 'http://api.steampowered.com/ISteamUserStats/GetNumberOfCurrentPlayers/v0001/?appid=220200&format=json' -UseBasicParsing|convertfrom-json).response|Select-Object player_count player_count ------------ 2548
There you go! We just pulled back the live player count for a steam game using PowerShell! This invoke is slightly different than the previous ones – here we are pulling the data in a json format, piping that to a ConvertFrom-Json cmdlet, and only selecting the response property. From that we pull the player_count.
One more example using invoke-webrequest – lets pull the Achievement percentage for a particular game. In this case, we can’t use KSP because that particular game doesn’t track achievements. Instead, we will use Team Fortress 2 (appid 440). Note that this interface/method uses the parameter gameid, not appid. Don’t ask why, because I have no idea.
PS C:\Users\Draith> $achievements = (Invoke-WebRequest -uri 'http://api.steampowered.com/ISteamUserStats/GetGlobalAchievementPercentagesForApp/v0001/?gameid=440&format=json' -UseBasicParsing|convertfrom-json).achievementpercentages.achievements PS C:\Users\Draith> $achievements.achievement name percent ---- ------- TF_SCOUT_LONG_DISTANCE_RUNNER 54.461830139160156 TF_HEAVY_DAMAGE_TAKEN 46.819679260253906 TF_GET_CONSECUTIVEKILLS_NODEATHS 44.32537841796875 TF_PYRO_CAMP_POSITION 36.193519592285156 TF_KILL_NEMESIS 34.089653015136719
There are plenty of interfaces and interfaces you can pull – GetGlobalStatsForGame, UpToDateCheck (check to see if an app version is up to date), GetServerInfo (check Web API server status), GetWorldStatus (Specific for TF2), etc… Using the appropriate UserID, and the Steam Web API Key, you can even use ISteamUser to get Friends lists, get player profile information, find players achievements for a particular game, and more! Steam has done a great job supplying this API, and with PowerShell, you can access this data with little more than a simple invoke-webrequest.