diff --git a/README.rst b/README.rst index a189a38..4afad08 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,5 @@ +|pypi| |license| + Module for interacting with various Steam_ features WebAPI @@ -68,6 +70,7 @@ SteamID >>> SteamID(12345) # accountid >>> SteamID('12345') + >>> SteamID('STEAM_1:1:6172') # steam2 SteamID(id=12345, type='Individual', universe='Public', instance=1) >>> SteamID(103582791429521412) # steam64 @@ -75,7 +78,8 @@ SteamID >>> SteamID('[g:1:4]') # steam3 SteamID(id=4, type='Clan', universe='Public', instance=0) - # vanity urls are resolved by making an HTTP request + # vanity urls are resolved by fetching the community profile page (this is unstable) + # use the WebAPI to reliably resolve vanity urls >>> SteamID('https://steamcommunity.com/id/drunkenf00l') >>> SteamID('http://steamcommunity.com/profiles/76561197968459473') # no request is made SteamID(id=8193745, type='Individual', universe='Public', instance=1) @@ -89,12 +93,12 @@ SteamID 103582791429521412 >>> str(group) '103582791429521412' - >>> group.as_steam2 - 'STEAM_0:0:2' + >>> group.as_steam2 # only works for 'Individual' accounts + 'STEAM_1:0:2' >>> group.as_steam3 '[g:1:4]' >>> group.community_url - 'http://steamcommunity.com/gid/103582791429521412' + 'https://steamcommunity.com/gid/103582791429521412' @@ -102,3 +106,11 @@ SteamID .. _Steam Web API: https://developer.valvesoftware.com/wiki/Steam_Web_API .. _API Key: http://steamcommunity.com/dev/apikey .. _list of the currently available API interfaces: https://github.com/ValvePython/steam/wiki/web-api + +.. |pypi| image:: https://img.shields.io/pypi/v/steam.svg?style=flat&label=latest%20version + :target: https://pypi.python.org/pypi/steam + :alt: Latest version released on PyPi + +.. |license| image:: https://img.shields.io/pypi/l/steam.svg?style=flat&label=license + :target: https://pypi.python.org/pypi/steam + :alt: MIT License diff --git a/steam/__init__.py b/steam/__init__.py index cbe80b7..f1621d1 100644 --- a/steam/__init__.py +++ b/steam/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.4" +__version__ = "0.5" __author__ = "Rossen Georgiev" from .steamid import SteamID diff --git a/steam/steamid.py b/steam/steamid.py index 9887b70..3e139d0 100644 --- a/steam/steamid.py +++ b/steam/steamid.py @@ -33,10 +33,14 @@ class SteamID(object): SteamID(id=12345, type='Invalid', universe='Invalid', instance=0) SteamID(103582791429521412) # steam64 SteamID('103582791429521412') + SteamID('STEAM_1:0:2') # steam2 SteamID('[g:1:4]') # steam3 - SteamID('https://steamcommunity.com/id/drunkenf00l') # vanity url SteamID('http://steamcommunity.com/profiles/76561197968459473') + # WARNING: vainty url resolving is fragile + # it might break if community profile page changes + # you should use the WebAPI to resolve them reliably + SteamID('https://steamcommunity.com/id/drunkenf00l') """ largs = len(args) @@ -89,6 +93,20 @@ class SteamID(object): # textual input e.g. [g:1:4] else: + # try steam2 + match = re.match(r"^STEAM_(?P\d+)" + r":(?P[0-1])" + r":(?P\d+)$", value + ) + + if match: + self.id = (int(match.group('id')) << 1) | int(match.group('reminder')) + self.universe = EUniverse(int(match.group('universe'))) + self.type = EType(1) + self.instance = 1 + return + + # try steam3 typeChars = ''.join(self.ETypeChar.values()) match = re.match(r"^\[" r"(?P[%s]):" # type char @@ -97,23 +115,24 @@ class SteamID(object): r"(:(?P\d+))?" # instance r"\]$" % typeChars, value ) - if not match: - raise ValueError("Expected a number or textual SteamID" - " (e.g. [g:1:4]), got %s" % repr(value) - ) - - self.id = int(match.group('id')) - self.universe = EUniverse(int(match.group('universe'))) - inverseETypeChar = dict((b, a) for (a, b) in self.ETypeChar.items()) - self.type = EType(inverseETypeChar[match.group('type')]) - self.instance = match.group('instance') - if self.instance is None: - if self.type in (EType.Individual, EType.GameServer): - self.instance = 1 + if match: + self.id = int(match.group('id')) + self.universe = EUniverse(int(match.group('universe'))) + inverseETypeChar = dict((b, a) for (a, b) in self.ETypeChar.items()) + self.type = EType(inverseETypeChar[match.group('type')]) + self.instance = match.group('instance') + if self.instance is None: + if self.type in (EType.Individual, EType.GameServer): + self.instance = 1 + else: + self.instance = 0 else: - self.instance = 0 - else: - self.instance = int(self.instance) + self.instance = int(self.instance) + return + + raise ValueError("Expected a number or textual SteamID" + " (e.g. [g:1:4], STEAM_0:1:1234), got %s" % repr(value) + ) elif lkwargs > 0: if 'id' not in kwargs: @@ -152,11 +171,12 @@ class SteamID(object): ) def __str__(self): - return self.as_64 + return str(self.as_64) @property def as_steam2(self): - return "STEAM_0:%s:%s" % ( + return "STEAM_%s:%s:%s" % ( + self.universe.value, self.id % 2, self.id >> 1, ) @@ -188,6 +208,6 @@ class SteamID(object): EType.Clan: "gid/%s", } if self.type in suffix: - url = "http://steamcommunity.com/%s" % suffix[self.type] + url = "https://steamcommunity.com/%s" % suffix[self.type] return url % self.as_64 - return '' + return None diff --git a/steam/webapi.py b/steam/webapi.py index 3a95db2..ef29d01 100644 --- a/steam/webapi.py +++ b/steam/webapi.py @@ -42,6 +42,7 @@ class WebAPI(object): populates the name space under the instance """ result = self._api_request( + None, "GET", "ISteamWebAPIUtil/GetSupportedAPIList/v1/", params={'format': 'json'}, @@ -79,7 +80,7 @@ class WebAPI(object): def _url_base(self): return "%s://api.steampowered.com/" % ('https' if self.https else 'http') - def _api_request(self, method, path, **kwargs): + def _api_request(self, caller, method, path, **kwargs): if method not in ('GET', 'POST'): raise NotImplemented("HTTP method: %s" % repr(self.method)) if 'params' not in kwargs: @@ -104,6 +105,9 @@ class WebAPI(object): f = getattr(requests, method.lower()) resp = f(self._url_base + path, stream=True, **kwargs) + if caller is not None: + caller.last_response = resp + if not resp.ok: raise requests.exceptions.HTTPError("%s %s" % (resp.status_code, resp.reason)) @@ -175,6 +179,7 @@ class WebAPIMethod(object): """ def __init__(self, method_dict, parent=None): + self.last_response = None self._parent = parent self._dict = method_dict @@ -240,6 +245,7 @@ class WebAPIMethod(object): # make the request return self._api_request( + self, self.method, "%s/%s/v%s/" % (self._parent.name, self.name, self.version), params=params, @@ -270,7 +276,7 @@ class WebAPIMethod(object): return self._parent.https def doc(self): - doc = "%(name)s (v%(version)04d)\n" % self._dict + doc = "%(httpmethod)s %(name)s (v%(version)04d)\n" % self._dict if 'description' in self._dict: doc += "\n %(description)s\n" % self._dict