/scripts/srcupdatecheck
#! | 146 lines | 124 code | 22 blank | 0 comment | 0 complexity | 3d6c234a39941a815bd9ecbf05012bac MD5 | raw file
1#!/usr/bin/python
2# coding=utf8
3
4# srcupdatecheck v13 - part of
5# NemRun v1.8.6 - john@pointysoftware.net, 07/25/2012
6
7# Copyright 2012 john@pointysoftware.net
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17
18# You should have received a copy of the GNU General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20
21import time
22import sys
23import xml.dom.minidom
24import re
25
26gSteamAPI = 'https://api.steampowered.com'
27
28# This supports Python 2.4+ and 3.0+, which requires a few tricks,
29# and different libraries
30
31if (sys.hexversion < 0x03000000):
32 gPy3k = False
33 import urllib
34 import urllib2
35else:
36 gPy3k = True
37 import urllib.request
38 import urllib.parse
39
40if not len(sys.argv) == 2:
41 print("Usage: ./srcupdatecheck /path/to/steam.inf")
42 sys.exit(-1)
43
44#
45# Steam API Call
46#
47
48def SteamAPICall(path, rawargs = {}):
49 args = rawargs
50 args['format'] = 'xml'
51 if gPy3k:
52 args = '?%s' % (urllib.parse.urlencode(args))
53 else:
54 args = '?%s' % (urllib.urlencode(args))
55
56 url = "%s/%s/%s" % (gSteamAPI, path, args)
57 try:
58 if gPy3k:
59 raw = urllib.request.urlopen(url).read().decode()
60 else:
61 raw = urllib2.urlopen(url).read()
62 except Exception:
63 print("!! API Call failed\n\tURL:\t'%s'" % (url))
64 return False
65
66 try:
67 dom = xml.dom.minidom.parseString(raw)
68 except Exception:
69 print("!! API Call - Failed to parse XML result\n\tURL:\t'%s'\n=== Raw ===\n%s\n===========" % (url, raw))
70 return False
71
72 response = dom.getElementsByTagName('response')
73 if not len(response):
74 return False
75
76 ret = {}
77 for c in response[0].childNodes:
78 if c.nodeType == xml.dom.minidom.Node.ELEMENT_NODE:
79 if not len(c.childNodes):
80 ret[c.nodeName] = ""
81 elif c.childNodes[0].data.lower() == "true":
82 ret[c.nodeName] = True
83 elif c.childNodes[0].data.lower() == "false":
84 ret[c.nodeName] = False
85 else:
86 ret[c.nodeName] = c.childNodes[0].data
87 print("\t -- API Call[%s: %s]\n\t\t%s" % (path, rawargs, ret))
88 return ret
89
90# 1 Up to date, 0 not, -1 call failed
91# Note that the json returns 'version_is_listable', but valve never uses this,
92# optional updates don't bump their version # :(
93def RunCheck(no, appid, ver):
94 call = SteamAPICall('ISteamApps/UpToDateCheck/v0001', { 'appid': appid, 'version': ver })
95 if not call or call['success'] != True:
96 print("[%u] !! API Call did not succeed\n\tRaw:\t%s" % (no, call))
97 return -1
98 if call['up_to_date']:
99 print("[%u] API returned up to date!" % (no))
100 return 1
101 else:
102 print("[%u] API returned out of date - Version %s vs %s" % (no, ver.replace('.', ''), call['required_version']))
103
104#
105# Read PatchVersion from provided steam.inf
106#
107try:
108 steaminf = open(sys.argv[1])
109except IOError:
110 print("File \"%s\" does not exist!" % sys.argv[1])
111 sys.exit(-1)
112
113infblob = steaminf.read()
114verre = re.search('PatchVersion=([^\r\n]+)', infblob)
115appre = re.search('appID=([^\r\n]+)', infblob)
116prodre = re.search('ProductName=([^\r\n]+)', infblob)
117
118if not (verre and appre and prodre):
119 print("Invalid steam.inf file.")
120 sys.exit(-1)
121
122ver = verre.group(1)
123game = prodre.group(1)
124appid = appre.group(1)
125
126print("Found patch version: %s, game: %s, appid: %s" % (ver,game,appid))
127
128# According to Tony, this API call can sometimes return out of date incorrectly (yay!)
129# So we'll keep our stupid try-multiple-times logic
130lastattempt = -1
131attempt = 1
132while True:
133 ret = RunCheck(attempt, appid, ver);
134 if (ret != -1):
135 if (ret == lastattempt):
136 if (ret == 1):
137 print("Confirmed up to date [%u requests]" % (attempt))
138 sys.exit(0)
139 else:
140 print("Confirmed out of date [%u requests]" % (attempt))
141 sys.exit(7)
142 else:
143 # In the case where we're chain-failing for some reason, don't hammer the API
144 time.sleep(5)
145 lastattempt = ret
146 attempt += 1