Browse Source

update SteamID

* better SteamID validation
* handling for chat SteamIDs
* improve .as_steam3 representation
* more tests

Fix #117
0.9
Rossen Georgiev 7 years ago
parent
commit
defcea5dfe
  1. 12
      steam/enums/common.py
  2. 98
      steam/steamid.py
  3. 59
      tests/test_steamid.py

12
steam/enums/common.py

@ -136,6 +136,18 @@ class EType(SteamIntEnum):
Max = 11 Max = 11
class EInstanceFlag(SteamIntEnum):
MMSLobby = 0x20000
Lobby = 0x40000
Clan = 0x80000
class EVanityUrlType(SteamIntEnum):
Individual = 1
Group = 2
GameGroup = 3
class EServerType(SteamIntEnum): class EServerType(SteamIntEnum):
Invalid = -1 Invalid = -1
First = 0 First = 0

98
steam/steamid.py

@ -3,7 +3,7 @@ import sys
import re import re
import requests import requests
from steam.enums.base import SteamIntEnum 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 from steam.util.web import make_requests_session
if sys.version_info < (3,): if sys.version_info < (3,):
@ -21,14 +21,14 @@ class ETypeChar(SteamIntEnum):
C = EType.ContentServer C = EType.ContentServer
g = EType.Clan g = EType.Clan
T = EType.Chat T = EType.Chat
c = EType.Chat L = EType.Chat # lobby chat, 'c' for clan chat
L = EType.Chat c = EType.Chat # clan chat
a = EType.AnonUser a = EType.AnonUser
def __str__(self): def __str__(self):
return self.name return self.name
ETypeChars = ''.join(map(str, list(ETypeChar))) ETypeChars = ''.join(ETypeChar.__members__.keys())
class SteamID(intBase): class SteamID(intBase):
@ -46,8 +46,9 @@ class SteamID(intBase):
SteamID('STEAM_1:0:2') # steam2 SteamID('STEAM_1:0:2') # steam2
SteamID('[g:1:4]') # steam3 SteamID('[g:1:4]') # steam3
""" """
EType = EType #: reference to EType EType = EType #: reference to EType
EUniverse = EUniverse #: reference to EUniverse EUniverse = EUniverse #: reference to EUniverse
EInstanceFlag = EInstanceFlag #: reference to EInstanceFlag
def __new__(cls, *args, **kwargs): def __new__(cls, *args, **kwargs):
steam64 = make_steam64(*args, **kwargs) steam64 = make_steam64(*args, **kwargs)
@ -146,19 +147,28 @@ class SteamID(intBase):
:return: steam3 format (e.g ``[U:1:1234]``) :return: steam3 format (e.g ``[U:1:1234]``)
:rtype: :class:`str` :rtype: :class:`str`
""" """
if self.type is EType.AnonGameServer: typechar = str(ETypeChar(self.type))
return "[%s:%s:%s:%s]" % ( instance = None
str(ETypeChar(self.type)),
int(self.universe), if self.type in (EType.AnonGameServer, EType.Multiseat):
self.id, instance = self.instance
self.instance elif self.type == EType.Individual:
) if self.instance != 1:
else: instance = self.instance
return "[%s:%s:%s]" % ( elif self.type == EType.Chat:
str(ETypeChar(self.type)), if self.instance & EInstanceFlag.Clan:
int(self.universe), typechar = 'c'
self.id, 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 @property
def community_url(self): def community_url(self):
@ -178,12 +188,26 @@ class SteamID(intBase):
def is_valid(self): def is_valid(self):
""" """
Check whether this SteamID is valid
:rtype: :py:class:`bool` :rtype: :py:class:`bool`
""" """
return (self.id > 0 if self.id == 0:
and self.type is not EType.Invalid return False
and self.universe is not EUniverse.Invalid
) 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): def make_steam64(id=0, *args, **kwargs):
@ -306,9 +330,9 @@ def steam3_to_tuple(value):
:rtype: :class:`tuple` or :class:`None` :rtype: :class:`tuple` or :class:`None`
""" """
match = re.match(r"^\[" match = re.match(r"^\["
r"(?P<type>[%s]):" # type char r"(?P<type>[i%s]):" # type char
r"(?P<universe>\d+):" # universe r"(?P<universe>[0-4]):" # universe
r"(?P<id>\d+)" # accountid r"(?P<id>\d{1,10})" # accountid
r"(:(?P<instance>\d+))?" # instance r"(:(?P<instance>\d+))?" # instance
r"\]$" % ETypeChars, r"\]$" % ETypeChars,
value value
@ -318,16 +342,24 @@ def steam3_to_tuple(value):
steam32 = int(match.group('id')) steam32 = int(match.group('id'))
universe = EUniverse(int(match.group('universe'))) 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') instance = match.group('instance')
if instance is None: if typechar in 'gT':
if etype in (EType.Individual, EType.GameServer): instance = 0
instance = 1 elif instance is not None:
else:
instance = 0
else:
instance = int(instance) 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) return (steam32, etype, universe, instance)

59
tests/test_steamid.py

@ -5,7 +5,7 @@ import vcr
import requests import requests
from steam import steamid from steam import steamid
from steam.steamid import SteamID, ETypeChar from steam.steamid import SteamID, ETypeChar
from steam.enums import EType, EUniverse from steam.enums import EType, EUniverse, EInstanceFlag
class SteamID_initialization(unittest.TestCase): class SteamID_initialization(unittest.TestCase):
@ -181,6 +181,36 @@ class SteamID_properties(unittest.TestCase):
# just to cover in coverage # just to cover in coverage
repr(SteamID()) 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): def test_rich_comperison(self):
for test_value in [SteamID(5), 5]: for test_value in [SteamID(5), 5]:
self.assertFalse(SteamID(10) == test_value) self.assertFalse(SteamID(10) == test_value)
@ -213,9 +243,13 @@ class SteamID_properties(unittest.TestCase):
def test_as_steam3(self): def test_as_steam3(self):
self.assertEqual(SteamID('[U:1:1234]').as_steam3, '[U:1:1234]') 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('[g:1:4]').as_steam3, '[g:1:4]')
self.assertEqual(SteamID('[A:1:1234:567]').as_steam3, '[A:1:1234:567]') 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('[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): def test_as_32(self):
self.assertEqual(SteamID(76580280500085312).as_32, 123456) self.assertEqual(SteamID(76580280500085312).as_32, 123456)
@ -312,6 +346,20 @@ class steamid_functions(unittest.TestCase):
def test_arg_steam3(self): def test_arg_steam3(self):
self.assertIsNone(steamid.steam3_to_tuple('invalid_format')) 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]"), self.assertEqual(steamid.steam3_to_tuple("[U:1:1234]"),
(1234, EType.Individual, EUniverse.Public, 1) (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]"), self.assertEqual(steamid.steam3_to_tuple("[A:1:1234:567]"),
(1234, EType.AnonGameServer, EUniverse.Public, 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)
)

Loading…
Cancel
Save