From defcea5dfed51f4653e629709e760973d40def52 Mon Sep 17 00:00:00 2001 From: Rossen Georgiev Date: Tue, 27 Feb 2018 16:59:46 +0000 Subject: [PATCH] update SteamID * better SteamID validation * handling for chat SteamIDs * improve .as_steam3 representation * more tests Fix #117 --- steam/enums/common.py | 12 ++++++ steam/steamid.py | 98 ++++++++++++++++++++++++++++--------------- tests/test_steamid.py | 59 +++++++++++++++++++++++++- 3 files changed, 135 insertions(+), 34 deletions(-) diff --git a/steam/enums/common.py b/steam/enums/common.py index 486f2dd..2352dc1 100644 --- a/steam/enums/common.py +++ b/steam/enums/common.py @@ -136,6 +136,18 @@ class EType(SteamIntEnum): Max = 11 +class EInstanceFlag(SteamIntEnum): + MMSLobby = 0x20000 + Lobby = 0x40000 + Clan = 0x80000 + + +class EVanityUrlType(SteamIntEnum): + Individual = 1 + Group = 2 + GameGroup = 3 + + class EServerType(SteamIntEnum): Invalid = -1 First = 0 diff --git a/steam/steamid.py b/steam/steamid.py index c8054c1..5c7c8ac 100644 --- a/steam/steamid.py +++ b/steam/steamid.py @@ -3,7 +3,7 @@ import sys import re import requests from steam.enums.base import SteamIntEnum -from steam.enums import EType, EUniverse +from steam.enums import EType, EUniverse, EInstanceFlag from steam.util.web import make_requests_session if sys.version_info < (3,): @@ -21,14 +21,14 @@ class ETypeChar(SteamIntEnum): C = EType.ContentServer g = EType.Clan T = EType.Chat - c = EType.Chat - L = EType.Chat + L = EType.Chat # lobby chat, 'c' for clan chat + c = EType.Chat # clan chat a = EType.AnonUser def __str__(self): return self.name -ETypeChars = ''.join(map(str, list(ETypeChar))) +ETypeChars = ''.join(ETypeChar.__members__.keys()) class SteamID(intBase): @@ -46,8 +46,9 @@ class SteamID(intBase): SteamID('STEAM_1:0:2') # steam2 SteamID('[g:1:4]') # steam3 """ - EType = EType #: reference to EType - EUniverse = EUniverse #: reference to EUniverse + EType = EType #: reference to EType + EUniverse = EUniverse #: reference to EUniverse + EInstanceFlag = EInstanceFlag #: reference to EInstanceFlag def __new__(cls, *args, **kwargs): steam64 = make_steam64(*args, **kwargs) @@ -146,19 +147,28 @@ class SteamID(intBase): :return: steam3 format (e.g ``[U:1:1234]``) :rtype: :class:`str` """ - if self.type is EType.AnonGameServer: - return "[%s:%s:%s:%s]" % ( - str(ETypeChar(self.type)), - int(self.universe), - self.id, - self.instance - ) - else: - return "[%s:%s:%s]" % ( - str(ETypeChar(self.type)), - int(self.universe), - self.id, - ) + typechar = str(ETypeChar(self.type)) + instance = None + + if self.type in (EType.AnonGameServer, EType.Multiseat): + instance = self.instance + elif self.type == EType.Individual: + if self.instance != 1: + instance = self.instance + elif self.type == EType.Chat: + if self.instance & EInstanceFlag.Clan: + typechar = 'c' + elif self.instance & EInstanceFlag.Lobby: + typechar = 'L' + else: + typechar = 'T' + + parts = [typechar, int(self.universe), self.id] + + if instance is not None: + parts.append(instance) + + return '[%s]' % (':'.join(map(str, parts))) @property def community_url(self): @@ -178,12 +188,26 @@ class SteamID(intBase): def is_valid(self): """ + Check whether this SteamID is valid + :rtype: :py:class:`bool` """ - return (self.id > 0 - and self.type is not EType.Invalid - and self.universe is not EUniverse.Invalid - ) + if self.id == 0: + return False + + if self.type == EType.Invalid or self.type >= EType.Max: + return False + + if self.universe == EUniverse.Invalid or self.universe >= EUniverse.Max: + return False + + if self.type == EType.Individual and self.instance > 4: + return False + + if self.type == EType.Clan and self.instance != 0: + return False + + return True def make_steam64(id=0, *args, **kwargs): @@ -306,9 +330,9 @@ def steam3_to_tuple(value): :rtype: :class:`tuple` or :class:`None` """ match = re.match(r"^\[" - r"(?P[%s]):" # type char - r"(?P\d+):" # universe - r"(?P\d+)" # accountid + r"(?P[i%s]):" # type char + r"(?P[0-4]):" # universe + r"(?P\d{1,10})" # accountid r"(:(?P\d+))?" # instance r"\]$" % ETypeChars, value @@ -318,16 +342,24 @@ def steam3_to_tuple(value): steam32 = int(match.group('id')) universe = EUniverse(int(match.group('universe'))) - etype = ETypeChar[match.group('type')] + typechar = match.group('type').replace('i', 'I') + etype = EType(ETypeChar[typechar]) instance = match.group('instance') - if instance is None: - if etype in (EType.Individual, EType.GameServer): - instance = 1 - else: - instance = 0 - else: + if typechar in 'gT': + instance = 0 + elif instance is not None: instance = int(instance) + elif typechar == 'L': + instance = EInstanceFlag.Lobby + elif typechar == 'c': + instance = EInstanceFlag.Clan + elif etype in (EType.Individual, EType.GameServer): + instance = 1 + else: + instance = 0 + + instance = int(instance) return (steam32, etype, universe, instance) diff --git a/tests/test_steamid.py b/tests/test_steamid.py index 2b60641..82c37d8 100644 --- a/tests/test_steamid.py +++ b/tests/test_steamid.py @@ -5,7 +5,7 @@ import vcr import requests from steam import steamid from steam.steamid import SteamID, ETypeChar -from steam.enums import EType, EUniverse +from steam.enums import EType, EUniverse, EInstanceFlag class SteamID_initialization(unittest.TestCase): @@ -181,6 +181,36 @@ class SteamID_properties(unittest.TestCase): # just to cover in coverage repr(SteamID()) + def test_is_valid(self): + # default + self.assertFalse(SteamID().is_valid()) + # id = 0 + self.assertFalse(SteamID(0).is_valid()) + self.assertFalse(SteamID(id=0).is_valid()) + self.assertFalse(SteamID(-50).is_valid()) + self.assertFalse(SteamID(id=-50).is_valid()) + # id > 0 + self.assertTrue(SteamID(5).is_valid()) + # type out of bound + self.assertFalse(SteamID(1, EType.Max).is_valid()) + # universe out of bound + self.assertFalse(SteamID(1, universe=EUniverse.Max).is_valid()) + # individual + self.assertTrue(SteamID(123, EType.Individual, EUniverse.Public, instance=0).is_valid()) + self.assertTrue(SteamID(123, EType.Individual, EUniverse.Public, instance=1).is_valid()) + self.assertTrue(SteamID(123, EType.Individual, EUniverse.Public, instance=2).is_valid()) + self.assertTrue(SteamID(123, EType.Individual, EUniverse.Public, instance=3).is_valid()) + self.assertTrue(SteamID(123, EType.Individual, EUniverse.Public, instance=4).is_valid()) + self.assertFalse(SteamID(123, EType.Individual, EUniverse.Public, instance=5).is_valid()) + self.assertFalse(SteamID(123, EType.Individual, EUniverse.Public, instance=333).is_valid()) + # clan + self.assertTrue(SteamID(1, EType.Clan, EUniverse.Public, instance=0).is_valid()) + self.assertFalse(SteamID(1, EType.Clan, EUniverse.Public, instance=1).is_valid()) + self.assertFalse(SteamID(1, EType.Clan, EUniverse.Public, instance=1234).is_valid()) + + s = SteamID(123, type=EType.Clan, universe=EUniverse.Public, instance=333) + self.assertFalse(s.is_valid()) + def test_rich_comperison(self): for test_value in [SteamID(5), 5]: self.assertFalse(SteamID(10) == test_value) @@ -213,9 +243,13 @@ class SteamID_properties(unittest.TestCase): def test_as_steam3(self): self.assertEqual(SteamID('[U:1:1234]').as_steam3, '[U:1:1234]') + self.assertEqual(SteamID('[U:1:1234:56]').as_steam3, '[U:1:1234:56]') self.assertEqual(SteamID('[g:1:4]').as_steam3, '[g:1:4]') self.assertEqual(SteamID('[A:1:1234:567]').as_steam3, '[A:1:1234:567]') self.assertEqual(SteamID('[G:1:1234:567]').as_steam3, '[G:1:1234]') + self.assertEqual(SteamID('[T:1:1234]').as_steam3, '[T:1:1234]') + self.assertEqual(SteamID('[c:1:1234]').as_steam3, '[c:1:1234]') + self.assertEqual(SteamID('[L:1:1234]').as_steam3, '[L:1:1234]') def test_as_32(self): self.assertEqual(SteamID(76580280500085312).as_32, 123456) @@ -312,6 +346,20 @@ class steamid_functions(unittest.TestCase): def test_arg_steam3(self): self.assertIsNone(steamid.steam3_to_tuple('invalid_format')) + self.assertIsNone(steamid.steam3_to_tuple('')) + self.assertIsNone(steamid.steam3_to_tuple(' ')) + self.assertIsNone(steamid.steam3_to_tuple('[U:5:1234]')) + self.assertIsNone(steamid.steam3_to_tuple('[i:5:1234]')) + + self.assertEqual(steamid.steam3_to_tuple("[i:1:1234]"), + (1234, EType.Invalid, EUniverse.Public, 0) + ) + self.assertEqual(steamid.steam3_to_tuple("[I:1:1234]"), + (1234, EType.Invalid, EUniverse.Public, 0) + ) + self.assertEqual(steamid.steam3_to_tuple("[U:0:1234]"), + (1234, EType.Individual, EUniverse.Invalid, 1) + ) self.assertEqual(steamid.steam3_to_tuple("[U:1:1234]"), (1234, EType.Individual, EUniverse.Public, 1) @@ -328,4 +376,13 @@ class steamid_functions(unittest.TestCase): self.assertEqual(steamid.steam3_to_tuple("[A:1:1234:567]"), (1234, EType.AnonGameServer, EUniverse.Public, 567) ) + self.assertEqual(steamid.steam3_to_tuple("[T:1:1234]"), + (1234, EType.Chat, EUniverse.Public, 0) + ) + self.assertEqual(steamid.steam3_to_tuple("[L:1:1234]"), + (1234, EType.Chat, EUniverse.Public, EInstanceFlag.Lobby) + ) + self.assertEqual(steamid.steam3_to_tuple("[c:1:1234]"), + (1234, EType.Chat, EUniverse.Public, EInstanceFlag.Clan) + )