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})