Browse Source

added EVENT properties for each event identifier

This should make documenting events much better
pull/41/head
Rossen Georgiev 9 years ago
parent
commit
49e4d35d3b
  1. 1
      docs/api/steam.client.rst
  2. 39
      steam/client/__init__.py
  3. 63
      steam/client/mixins/friends.py
  4. 47
      steam/core/cm.py

1
docs/api/steam.client.rst

@ -3,6 +3,7 @@ client
.. automodule:: steam.client
:members: SteamClient
:member-order: alphabetical
:undoc-members:
:inherited-members:
:show-inheritance:

39
steam/client/__init__.py

@ -1,23 +1,6 @@
"""
Implementation of Steam client based on ``gevent``
Events
^^^^^^
| ``connected`` - when successful connection with a CM server is established
| ``disconnected`` - when connection is lost
| ``reconnect`` - when connect attempt is delayed, `delay` argument gives the delay in seconds
| ``channel_secured`` - after channel encryption is complete, client can attempt to login now
| ``error`` - after login failure
| ``auth_code_required`` - either email code or 2FA code is needed for login
| ``logged_on`` - after successful login, client can send messages
| ``new_login_key`` - after new login key has been received and acknowledged
| :class:`.EMsg` - all messages are emitted with their :class:`.EMsg` number
.. note::
Mixins can emitter additional events. See their docs pages for details.
.. note::
Additional features are located in separate submodules. All functionality from :mod:`.builtins` is inherited by default.
@ -47,6 +30,16 @@ from steam.util import ip_from_int
class SteamClient(CMClient, BuiltinBase):
EVENT_LOGGED_ON = 'logged_on'
"""After successful login
"""
EVENT_AUTH_CODE_REQUIRED = 'auth_code_required'
"""When either email or 2FA code is needed for login
"""
EVENT_NEW_LOGIN_KEY = 'new_login_key'
"""After a new login key is accepted
"""
_cm_servers_timestamp = None # used to decide when to update CM list on disk
_reconnect_backoff_c = 0
current_jobid = 0
@ -60,8 +53,8 @@ class SteamClient(CMClient, BuiltinBase):
self._LOG = logging.getLogger("SteamClient")
# register listners
self.on(None, self._handle_jobs)
self.on("disconnected", self._handle_disconnect)
self.on("reconnect", self._handle_disconnect)
self.on(self.EVENT_DISCONNECTED, self._handle_disconnect)
self.on(self.EVENT_RECONNECT, self._handle_disconnect)
self.on(EMsg.ClientNewLoginKey, self._handle_login_key)
self.on(EMsg.ClientUpdateMachineAuth, self._handle_update_machine_auth)
@ -159,7 +152,7 @@ class SteamClient(CMClient, BuiltinBase):
self._reconnect_backoff_c = 0
self.logged_on = True
self.set_persona(EPersonaState.Online)
self.emit("logged_on")
self.emit(self.EVENT_LOGGED_ON)
return
# CM kills the connection on error anyway
@ -183,7 +176,7 @@ class SteamClient(CMClient, BuiltinBase):
else:
code_mismatch = (result == EResult.InvalidLoginAuthCode)
self.emit("auth_code_required", is_2fa, code_mismatch)
self.emit(self.EVENT_AUTH_CODE_REQUIRED, is_2fa, code_mismatch)
def _handle_login_key(self, message):
resp = MsgProto(EMsg.ClientNewLoginKeyAccepted)
@ -193,7 +186,7 @@ class SteamClient(CMClient, BuiltinBase):
self.send(resp)
gevent.idle()
self.login_key = message.body.login_key
self.emit("new_login_key")
self.emit(self.EVENT_NEW_LOGIN_KEY)
def _handle_update_machine_auth(self, message):
ok = self.store_sentry(self.username, message.body.bytes)
@ -432,7 +425,7 @@ class SteamClient(CMClient, BuiltinBase):
.. code:: python
@steamclient.on('auth_code_required')
@steamclient.on(steamclient.EVENT_AUTH_CODE_REQUIRED)
def auth_code_prompt(is_2fa, code_mismatch):
if is_2fa:
code = raw_input("Enter 2FA Code: ")

63
steam/client/mixins/friends.py

@ -19,28 +19,45 @@ class SteamFriendlist(EventEmitter):
You can iterate over it, check if it contains a particular steam id, or get :class:`SteamUser` for a steamid.
.. note::
persona state is not update immediatelly for new user entries
persona state is not updated immediatelly for new user entries
Event: ``ready`` - friendlist is ready to use
Event: ``friend_invite`` - new or existing friend invite
"""
EVENT_READY = 'ready'
"""Friend list is ready for use
"""
EVENT_FRIEND_INVITE = 'friend_invite'
"""New or existing friend invite
:param user: steam user instance
:type user: :class:`SteamUser`
Event: ``friend_new`` - emitted upon accepting a new friend (or being accepted)
:type user: :class:`.SteamUser`
"""
EVENT_FRIEND_NEW = 'friend_new'
"""Friendship established (after being accepted, or accepting)
:param user: steam user instance
:type user: :class:`SteamUser`
:type user: :class:`.SteamUser`
"""
EVENT_FRIEND_REMOVED = 'friend_removed'
"""No longer a friend (removed by either side)
:param user: steam user instance
:type user: :class:`.SteamUser`
"""
EVENT_FRIEND_ADD_RESULT = 'friend_add_result'
"""Result response after adding a friend
Event: ``friend_removed`` - no longer a friend (removed by either side)
:param eresult: result
:param type: :class:`.EResult`
:param steam_id: steam id
:param type: :class:`.SteamID`
"""
EVENT_PERSONA_STATE_UPDATED = 'persona_state_updated'
"""Upon persona state changes for a user
:param user: steam user instance
:type user: :class:`SteamUser`
:type user: :class:`.SteamUser`
"""
ready = False #: indicates whether friend list is available
def __init__(self, client, logger_name='SteamFriendList'):
@ -51,7 +68,7 @@ class SteamFriendlist(EventEmitter):
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)
self._steam.on('disconnected', self._handle_disconnect)
self._steam.on(self._steam.EVENT_DISCONNECTED, self._handle_disconnect)
def emit(self, event, *args):
if event is not None:
@ -65,7 +82,7 @@ class SteamFriendlist(EventEmitter):
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)
self.emit(self.EVENT_FRIEND_ADD_RESULT, eresult, steam_id)
def _handle_friends_list(self, message):
incremental = message.body.bincremental
@ -88,15 +105,15 @@ class SteamFriendlist(EventEmitter):
pstate_check.add(steamid)
if rel == EFriendRelationship.RequestRecipient:
self.emit('friend_invite', suser)
self.emit(self.EVENT_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))
self.emit(self.EVENT_FRIEND_REMOVED, self._fr.pop(steamid))
elif oldrel in (2,4) and rel == EFriendRelationship.Friend:
self.emit('friend_new', self._fr[steamid])
self.emit(self.EVENT_FRIEND_NEW, self._fr[steamid])
# request persona state for any new entries
if pstate_check:
@ -107,7 +124,7 @@ class SteamFriendlist(EventEmitter):
if not self.ready:
self.ready = True
self.emit('ready')
self.emit(self.EVENT_READY)
def _handle_persona_state(self, message):
for friend in message.body.friends:
@ -147,7 +164,7 @@ class SteamFriendlist(EventEmitter):
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`
:type steamid_or_accountname_or_email: :class:`int`, :class:`.SteamID`, :class:`SteamUser`, :class:`str`
.. note::
Adding by email doesn't not work. It's only mentioned for the sake of completeness.
@ -166,7 +183,7 @@ class SteamFriendlist(EventEmitter):
Remove a friend
:param steamid: their steamid
:type steamid: :class:`int`, :class:`steam.steamid.SteamID`, :class:`SteamUser`
:type steamid: :class:`int`, :class:`.SteamID`, :class:`SteamUser`
"""
m = MsgProto(EMsg.ClientRemoveFriend)
m.body.friendid = steamid
@ -193,7 +210,7 @@ class SteamUser(intBase):
def steamid(self):
"""SteamID instance
:rtype: :class:`steam.steamid.SteamID`
:rtype: :class:`.SteamID`
"""
return SteamID(int(self))
@ -201,7 +218,7 @@ class SteamUser(intBase):
def relationship(self):
"""Current relationship with the steam user
:rtype: :class:`steam.enums.common.EFriendRelationship`
:rtype: :class:`.EFriendRelationship`
"""
return self._data['relationship']
@ -239,7 +256,7 @@ class SteamUser(intBase):
def state(self):
"""State of the steam user
:rtype: :class:`steam.enums.common.EPersonaState`
:rtype: :class:`.EPersonaState`
"""
state = self.get_ps('persona_state')
if state:

47
steam/core/cm.py

@ -30,11 +30,36 @@ class CMClient(EventEmitter):
their :class:`steam.enums.emsg.EMsg` as event identifier
"""
TCP = 0 #: TCP protocol enum
UDP = 1 #: UDP protocol enum
EVENT_CONNECTED = 'connected'
"""Connection establed to CM server
"""
EVENT_DISCONNECTED = 'disconnected'
"""Connection closed
"""
EVENT_RECONNECT = 'reconnect'
"""Delayed connect
:param delay: delay in seconds
:type delay: int
"""
EVENT_CHANNEL_SECURED = 'channel_secured'
"""After successful completion of encryption handshake
"""
EVENT_ERROR = 'error'
"""When login is denied
:param eresult: reason
:type eresult: :class:`.EResult`
"""
EVENT_EMSG = 0
"""All incoming messages are emitted with their :class:`.EMsg` number.
"""
PROTOCOL_TCP = 0 #: TCP protocol enum
PROTOCOL_UDP = 1 #: UDP protocol enum
verbose_debug = False #: print message connects in debug
cm_servers = None #: a instance of :class:`steam.core.cm.CMServerList`
cm_servers = None #: a instance of :class:`.CMServerList`
current_server_addr = None #: (ip, port) tuple
_seen_logon = False
_connecting = False
@ -44,7 +69,7 @@ class CMClient(EventEmitter):
channel_key = None #: channel encryption key
channel_hmac = None #: HMAC secret
steam_id = SteamID() #: :class:`steam.steamid.SteamID` of the current user
steam_id = SteamID() #: :class:`.SteamID` of the current user
session_id = None #: session id when logged in
webapi_authenticate_user_nonce = None #: nonce for the getting a web session
@ -52,11 +77,11 @@ class CMClient(EventEmitter):
_heartbeat_loop = None
_LOG = None
def __init__(self, protocol=0):
def __init__(self, protocol=PROTOCOL_TCP):
self._LOG = logging.getLogger("CMClient")
self.cm_servers = CMServerList()
if protocol == CMClient.TCP:
if protocol == CMClient.PROTOCOL_TCP:
self.connection = TCPConnection()
else:
raise ValueError("Only TCP is supported")
@ -91,7 +116,7 @@ class CMClient(EventEmitter):
if delay:
self._LOG.debug("Delayed connect: %d seconds" % delay)
self.emit('reconnect', delay)
self.emit(self.EVENT_RECONNECT, delay)
gevent.sleep(delay)
self._LOG.debug("Connect initiated.")
@ -114,7 +139,7 @@ class CMClient(EventEmitter):
self.current_server_addr = server_addr
self.connected = True
self.emit("connected")
self.emit(self.EVENT_CONNECTED)
self._recv_loop = gevent.spawn(self._recv_messages)
self._connecting = False
return True
@ -151,7 +176,7 @@ class CMClient(EventEmitter):
self._reset_attributes()
self.emit('disconnected')
self.emit(self.EVENT_DISCONNECTED)
def _reset_attributes(self):
for name in ['connected',
@ -298,7 +323,7 @@ class CMClient(EventEmitter):
self._LOG.debug("Channel secured (legacy mode)")
self.channel_secured = True
self.emit('channel_secured')
self.emit(self.EVENT_CHANNEL_SECURED)
def __handle_multi(self, msg):
self._LOG.debug("Multi: Unpacking")
@ -357,7 +382,7 @@ class CMClient(EventEmitter):
interval = msg.body.out_of_game_heartbeat_seconds
self._heartbeat_loop = gevent.spawn(self.__heartbeat, interval)
else:
self.emit("error", EResult(result))
self.emit(self.EVENT_ERROR, EResult(result))
self.disconnect()
def _handle_cm_list(self, msg):

Loading…
Cancel
Save