You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

167 lines
5.5 KiB

import logging
from eventemitter import EventEmitter
from steam.steamid import SteamID, intBase
from steam.enums import EResult, EFriendRelationship
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, logger_name="%s.friends" % self.__class__.__name__)
class SteamFriendlist(EventEmitter):
"""SteamFriendlist is an object that keeps state of user's friend list.
It's essentially a :class:`list` of :class:`.SteamUser`.
You can iterate over it, check if it contains a particular ``steam id``,
or get :class:`.SteamUser` for a ``steam id``.
"""
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 = 'friend_new'
"""Friendship established (after being accepted, or accepting)
:param user: steam user instance
: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
:param eresult: result
:param type: :class:`.EResult`
:param steam_id: steam id
:param type: :class:`.SteamID`
"""
ready = False #: indicates whether friend list is ready for use
def __init__(self, client, logger_name='SteamFriendList'):
self._LOG = logging.getLogger(logger_name)
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(self._steam.EVENT_DISCONNECTED, self._handle_disconnect)
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_disconnect(self):
self.ready = False
self._fr.clear()
def _handle_add_friend_result(self, message):
eresult = EResult(message.body.eresult)
steam_id = SteamID(message.body.steam_id_added)
self.emit(self.EVENT_FRIEND_ADD_RESULT, eresult, steam_id)
def _handle_friends_list(self, message):
incremental = message.body.bincremental
if incremental == False:
self._fr.clear()
steamids_to_check = set()
# update internal friends list
for friend in message.body.friends:
steamid = SteamID(friend.ulfriendid)
if steamid.type != steamid.EType.Individual:
continue
suser = self._steam.get_user(steamid, False)
rel = EFriendRelationship(friend.efriendrelationship)
if steamid not in self._fr:
self._fr[suser] = suser
suser.relationship = rel
steamids_to_check.add(steamid)
if rel in (2,4): # RequestRecipient = 2, RequestInitiator = 4
if rel == EFriendRelationship.RequestRecipient:
self.emit(self.EVENT_FRIEND_INVITE, suser)
else:
oldrel, suser.relationship = suser.relationship, rel
if rel == EFriendRelationship.No:
self.emit(self.EVENT_FRIEND_REMOVED, self._fr.pop(suder))
elif oldrel in (2,4) and rel == EFriendRelationship.Friend:
self.emit(self.EVENT_FRIEND_NEW, suser)
# request persona state for any new entries
if steamids_to_check:
self._steam.request_persona_state(steamids_to_check)
if not self.ready:
self.ready = True
self.emit(self.EVENT_READY)
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:`.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_job(m)
def remove(self, steamid):
"""
Remove a friend
:param steamid: their steamid
:type steamid: :class:`int`, :class:`.SteamID`, :class:`SteamUser`
"""
self._steam.send(MsgProto(EMsg.ClientRemoveFriend), {'friendid': steamid})