Browse Source

added support for mixins + friends mixin

pull/34/head
Rossen Georgiev 9 years ago
parent
commit
70fa89cc09
  1. 7
      docs/api/steam.client.mixins.friends.rst
  2. 13
      docs/api/steam.client.mixins.rst
  3. 1
      docs/api/steam.client.rst
  4. 9
      steam/__init__.py
  5. 4
      steam/client/features/misc.py
  6. 4
      steam/client/features/user.py
  7. 4
      steam/client/features/web.py
  8. 50
      steam/client/mixins/__init__.py
  9. 232
      steam/client/mixins/friends.py
  10. 101
      steam/enums/common.py

7
docs/api/steam.client.mixins.friends.rst

@ -0,0 +1,7 @@
friends
=======
.. automodule:: steam.client.mixins.friends
:members:
:undoc-members:
:show-inheritance:

13
docs/api/steam.client.mixins.rst

@ -0,0 +1,13 @@
steam.client.mixins
===================
List of mixins:
.. toctree::
steam.client.mixins.friends
----
.. automodule:: steam.client.mixins

1
docs/api/steam.client.rst

@ -3,6 +3,7 @@ steam.client
.. toctree::
steam.client.features
steam.client.mixins
steam.client.gc
.. automodule:: steam.client

9
steam/__init__.py

@ -12,4 +12,13 @@ from steam.webapi import WebAPI
class SteamClient(object):
def __new__(cls, *args, **kwargs):
from steam.client import SteamClient as SC
bases = cls.__bases__
if bases != (object, ):
if bases[0] != SteamClient:
raise ValueError("SteamClient needs to be the first base for custom classes")
SC = type("SteamClient", (SC,) + bases[1:], {})
return SC(*args, **kwargs)

4
steam/client/features/misc.py

@ -5,8 +5,8 @@ from steam.core.msg import MsgProto
from steam.enums.emsg import EMsg
class Misc(object):
def __init__(self):
super(Misc, self).__init__()
def __init__(self, *args, **kwargs):
super(Misc, self).__init__(*args, **kwargs)
def games_played(self, app_ids):
"""

4
steam/client/features/user.py

@ -3,8 +3,8 @@ from steam.enums.emsg import EMsg
from steam.core.msg import MsgProto
class User(object):
def __init__(self):
super(User, self).__init__()
def __init__(self, *args, **kwargs):
super(User, self).__init__(*args, **kwargs)
def set_persona(self, state=None, name=None):
"""

4
steam/client/features/web.py

@ -9,8 +9,8 @@ from steam.util.web import make_requests_session
class Web(object):
def __init__(self):
super(Web, self).__init__()
def __init__(self, *args, **kwargs):
super(Web, self).__init__(*args, **kwargs)
def get_web_session_cookies(self):
"""

50
steam/client/mixins/__init__.py

@ -0,0 +1,50 @@
"""
All optional features are available as mixins for :class:`steam.client.SteamClient`.
Using this approach the client can remain light yet flexible.
Functionallity can be added though inheritance depending on the use case.
Here is quick example of how to use one of the available mixins.
.. code:: python
from steam import SteamClient
from stema.client.mixins.friends import Friends
class MySteamClient(SteamClient, Friends):
pass
client = MySteamClient()
Making custom mixing is just as simple.
.. warning::
Take care not to override existing methods or properties, otherwise bad things will happen
.. note::
To avoid name collisions of non-public variables and methods, see `Private Variables <https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references>`_
.. code:: python
class MyMixin(object):
def __init__(*args, **kwargs):
super(MyMixin, self).__init__(*args, **kwargs)
self.my_property = 42
def my_method(self)
pass
class MySteamClient(SteamClient, Friends, MyMixin):
pass
client = MySteamClient()
>>> client.my_property
42
"""

232
steam/client/mixins/friends.py

@ -0,0 +1,232 @@
import logging
from eventemitter import EventEmitter
from steam.steamid import SteamID, intBase
from steam.enums import EResult, EFriendRelationship, EPersonaState
from steam.enums.emsg import EMsg
from steam.core.msg import MsgProto
class Friends(object):
def __init__(self, *args, **kwargs):
super(Friends, self).__init__(*args, **kwargs)
#: SteamFriendlist instance
self.friends = SteamFriendlist(self)
class SteamFriendlist(EventEmitter):
_LOG = logging.getLogger('SteamClient.friends')
def __init__(self, client):
self._fr = {}
self._steam = client
self._steam.on(EMsg.ClientAddFriendResponse, self._handle_add_friend_result)
self._steam.on(EMsg.ClientFriendsList, self._handle_friends_list)
self._steam.on(EMsg.ClientPersonaState, self._handle_persona_state)
def emit(self, event, *args):
if event is not None:
self._LOG.debug("Emit event: %s" % repr(event))
EventEmitter.emit(self, event, *args)
def _handle_add_friend_result(self, message):
eresult = EResult(message.body.eresult)
steam_id = SteamID(message.body.steam_id_added)
self.emit("friend_add_result", eresult, steam_id)
def _handle_friends_list(self, message):
incremental = message.body.bincremental
if incremental == False:
self._fr.clear()
pstate_check = set()
# update internal friends list
for friend in message.body.friends:
steamid = friend.ulfriendid
rel = EFriendRelationship(friend.efriendrelationship)
if steamid not in self._fr:
suser = SteamUser(steamid, rel)
self._fr[suser] = suser
if rel in (2,4):
if incremental == False:
pstate_check.add(steamid)
if rel == EFriendRelationship.RequestRecipient:
self.emit('friend_invite', suser)
else:
oldrel = self._fr[steamid]._data['relationship']
self._fr[steamid]._data['relationship'] = rel
if rel == EFriendRelationship.No:
self.emit('friend_removed', self._fr.pop(steamid))
elif oldrel in (2,4) and rel == EFriendRelationship.Friend:
self.emit('friend_new', self._fr[steamid])
# request persona state for any new entries
if pstate_check:
m = MsgProto(EMsg.ClientRequestFriendData)
m.body.persona_state_requested = 4294967295 # request all possible flags
m.body.friends.extend(pstate_check)
self._steam.send(m)
def _handle_persona_state(self, message):
for friend in message.body.friends:
steamid = friend.friendid
if steamid == self._steam.steam_id:
continue
if steamid in self._fr:
self._fr[steamid]._data['pstate'] = friend
def __repr__(self):
return "<%s %d users>" % (
self.__class__.__name__,
len(self._fr),
)
def __len__(self):
return len(self._fr)
def __iter__(self):
return iter(self._fr)
def __list__(self):
return list(self._fr)
def __getitem__(self, key):
return self._fr[key]
def __contains__(self, friend):
return friend in self._fr
def add(self, steamid_or_accountname_or_email):
"""
Add/Accept a steam user to be your friend.
When someone sends you an invite, use this method to accept it.
:param steamid_or_accountname_or_email: steamid, account name, or email
:type steamid_or_accountname_or_email: :class:`int`, :class:`steam.steamid.SteamID`, :class:`SteamUser`, :class:`str`
.. note::
Adding by email doesn't not work. It's only mentioned for the sake of completeness.
"""
m = MsgProto(EMsg.ClientAddFriend)
if isinstance(steamid_or_accountname_or_email, (intBase, int)):
m.body.steamid_to_add = steamid_or_accountname_or_email
else:
m.body.accountname_or_email_to_add = steamid_or_accountname_or_email
self._steam.send(m)
def remove(self, steamid):
"""
Remove a friend
:param steamid: their steamid
:type steamid: :class:`int`, :class:`steam.steamid.SteamID`, :class:`SteamUser`
"""
m = MsgProto(EMsg.ClientRemoveFriend)
m.body.friendid = steamid
self._steam.send(m)
class SteamUser(intBase):
def __new__(cls, steam64, *args, **kwargs):
return super(SteamUser, cls).__new__(cls, steam64)
def __init__(self, steam64, rel):
self._data = {
'relationship': EFriendRelationship(rel)
}
@property
def steamid(self):
"""SteamID instance
:rtype: :class:`steam.steamid.SteamID`
"""
return SteamID(int(self))
@property
def relationship(self):
"""Current relationship with the steam user
:rtype: :class:`steam.enums.common.EFriendRelationship`
"""
return self._data['relationship']
def get_persona_state_value(self, field_name):
"""Get a value for field in ``CMsgClientPersonaState.Friend``
:param field_name: see example list below
:type field_name: :class:`str`
:return: value for the field, or ``None`` if not available
:rtype: :class:`None`, :class:`int`, :class:`str`, :class:`bytes`, :class:`bool`
Examples:
- game_played_app_id
- last_logoff
- last_logon
- game_name
- avatar_hash
- facebook_id
...
"""
pstate = self._data.get('pstate', None)
if pstate is None or not pstate.HasField(field_name):
return None
return getattr(pstate, field_name)
@property
def name(self):
"""Name of the steam user, or ``None`` if it's not available
:rtype: :class:`str`, :class:`None`
"""
return self.get_persona_state_value('player_name')
@property
def state(self):
"""State of the steam user
:rtype: :class:`steam.enums.common.EPersonaState`
"""
state = self.get_persona_state_value('persona_state')
if state:
return EPersonaState(state)
return EPersonaState.Offline
def get_avatar_url(self, size=2):
"""Get url to the steam avatar
:param size: possible values are ``0``, ``1``, or ``2`` corresponding to small, medium, large
:type size: :class:`int`
:return: url to avatar
:rtype: :class:`str`
"""
ahash = self.get_persona_state_value('avatar_hash')
if ahash is None:
return None
sizes = {
0: '',
1: '_medium',
2: '_full',
}
url = "http://cdn.akamai.steamstatic.com/steamcommunity/public/images/avatars/%s/%s%s.jpg"
ahash = binascii.hexlify(persona_state_value.avatar_hash).decode('ascii')
return url % (ahash[:2], ahash, sizes[size])
def __repr__(self):
return "<%s (%s) %s %s>" % (
self.__class__.__name__,
int(self) if self.name is None else repr(self.name),
self.relationship.name,
self.state.name,
)

101
steam/enums/common.py

@ -1,14 +1,5 @@
from steam.enums.base import SteamIntEnum
__all__ = [
'EResult',
'EUniverse',
'EType',
'EServerType',
'EOSType',
'EPersonaState',
]
class EResult(SteamIntEnum):
Invalid = 0
@ -269,3 +260,95 @@ class EPersonaState(SteamIntEnum):
LookingToTrade = 5
LookingToPlay = 6
Max = 7
class EFriendRelationship(SteamIntEnum):
No = 0
Blocked = 1
RequestRecipient = 2
Friend = 3
RequestInitiator = 4
Ignored = 5
IgnoredFriend = 6
SuggestedFriend = 7
Max = 8
class EAccountFlags(SteamIntEnum):
NormalUser = 0
PersonaNameSet = 1
Unbannable = 2
PasswordSet = 4
Support = 8
Admin = 16
Supervisor = 32
AppEditor = 64
HWIDSet = 128
PersonalQASet = 256
VacBeta = 512
Debug = 1024
Disabled = 2048
LimitedUser = 4096
LimitedUserForce = 8192
EmailValidated = 16384
MarketingTreatment = 32768
OGGInviteOptOut = 65536
ForcePasswordChange = 131072
ForceEmailVerification = 262144
LogonExtraSecurity = 524288
LogonExtraSecurityDisabled = 1048576
Steam2MigrationComplete = 2097152
NeedLogs = 4194304
Lockdown = 8388608
MasterAppEditor = 16777216
BannedFromWebAPI = 33554432
ClansOnlyFromFriends = 67108864
GlobalModerator = 134217728
class EFriendFlags(SteamIntEnum):
No = 0
Blocked = 1
FriendshipRequested = 2
Immediate = 4
ClanMember = 8
OnGameServer = 16
RequestingFriendship = 128
RequestingInfo = 256
Ignored = 512
IgnoredFriend = 1024
Suggested = 2048
FlagAll = 65535
class EPersonaStateFlag(SteamIntEnum):
HasRichPresence = 1
InJoinableGame = 2
OnlineUsingWeb = 256
OnlineUsingMobile = 512
OnlineUsingBigPicture = 1024
class EClientPersonaStateFlag(SteamIntEnum):
Status = 1
PlayerName = 2
QueryPort = 4
SourceID = 8
Presence = 16
Metadata = 32
LastSeen = 64
ClanInfo = 128
GameExtraInfo = 256
GameDataBlob = 512
ClanTag = 1024
Facebook = 2048
# Do not remove
from sys import modules
from enum import EnumMeta
__all__ = map(lambda y: y.__name__,
filter(lambda x: x.__class__ is EnumMeta, modules[__name__].__dict__.values()),
)
del modules, EnumMeta

Loading…
Cancel
Save