class MessagesManager(ClientEventsHandler): __slots__ = ('__msgFilters', '__chatSessions', '__receivedTags', '__pending') def __init__(self): super(MessagesManager, self).__init__() self.__msgFilters = None self.__chatSessions = _ChatSessions() self.__isInited = False self.__receivedTags = set() self.__pending = [] self.__cooldown = XmppCooldownManager(MESSAGE_LIMIT.COOLDOWN) self.channelsStorage.onRestoredFromCache += self.__cs_onChannelsRestoredFromCache return @storage_getter('channels') def channelsStorage(self): return None def clear(self): self.channelsStorage.onRestoredFromCache -= self.__cs_onChannelsRestoredFromCache self.__clearData() super(MessagesManager, self).clear() def registerHandlers(self): register = self.client().registerHandler register(_EVENT.CONNECTED, self.__handleConnected) register(_EVENT.DISCONNECTED, self.__handleDisconnected) register(_EVENT.MESSAGE, self.__handleMessage) events = g_messengerEvents.users events.onUsersListReceived += self.__me_onUsersListReceived events.onUserActionReceived += self.__me_onUserActionReceived events.onUserStatusUpdated += self.__me_onUserStatusUpdated def unregisterHandlers(self): unregister = self.client().unregisterHandler unregister(_EVENT.CONNECTED, self.__handleConnected) unregister(_EVENT.DISCONNECTED, self.__handleDisconnected) unregister(_EVENT.MESSAGE, self.__handleMessage) events = g_messengerEvents.users events.onUsersListReceived -= self.__me_onUsersListReceived events.onUserActionReceived -= self.__me_onUserActionReceived events.onUserStatusUpdated -= self.__me_onUserStatusUpdated def setFilters(self, msgFilters): self.__msgFilters = msgFilters def isInCooldown(self): return self.__cooldown.isInProcess(CLIENT_ACTION_ID.SEND_MESSAGE) @xmpp_query(QUERY_SIGN.DATABASE_ID, QUERY_SIGN.ACCOUNT_NAME) def startChatSession(self, dbID, name): self.__chatSessions.startSession(makeContactJID(dbID), name) return (True, None) def stopChatSession(self, jid): self.__chatSessions.stopSession(ContactBareJID(jid)) def sendChatMessage(self, jid, body): if self.__cooldown.isInProcess(CLIENT_ACTION_ID.SEND_MESSAGE): g_messengerEvents.onErrorReceived(ChatCoolDownError(CLIENT_ACTION_ID.SEND_MESSAGE, MESSAGE_LIMIT.COOLDOWN)) return self.__chatSessions.sendMessage(ContactBareJID(jid), body) self.__cooldown.process(CLIENT_ACTION_ID.SEND_MESSAGE) def __clearData(self): self.__chatSessions.clear() self.__receivedTags.clear() self.__pending = [] def __setChannelsState(self, isJoined): channels = self.channelsStorage.getChannelsByCriteria(find_criteria.XMPPChannelFindCriteria()) for channel in channels: channel.setJoined(isJoined) def __handleConnected(self): self.__setChannelsState(True) def __handleDisconnected(self, reason, description): self.__clearData() self.__setChannelsState(False) def __handleMessage(self, _, msgType, body, jid, pyGlooxTag): if msgType == MESSAGE_TYPE.CHAT: state, info, sentAt = ChatMessageHandler().handleTag(pyGlooxTag) if info: dbID = info['dbID'] name = info['name'] else: LOG_ERROR('Can not find sender info', pyGlooxTag.getXml()) return if body: body = self.__msgFilters.chainIn(dbID, body) if _REQUIRED_USER_TAGS.issubset(self.__receivedTags): self.__chatSessions.onMessageReceived(jid, body, state, dbID, name, sentAt) else: self.__pending.insert(0, (msgType, (jid, body, state, dbID, name, sentAt))) def __me_onUsersListReceived(self, tags): self.__receivedTags.update(tags) if _REQUIRED_USER_TAGS.issubset(self.__receivedTags): while self.__pending: msgType, data = self.__pending.pop() if msgType == MESSAGE_TYPE.CHAT: self.__chatSessions.onMessageReceived(*data) def __me_onUserActionReceived(self, actionID, user): if actionID == USER_ACTION_ID.IGNORED_ADDED: self.__chatSessions.stopSession(makeContactJID(user.getID())) def __me_onUserStatusUpdated(self, contact): self.__chatSessions.setContactPresence(contact) def __cs_onChannelsRestoredFromCache(self, stateGenerator): if not stateGenerator or not g_settings.server.XMPP.isEnabled(): return for jid, state in stateGenerator(PROTO_TYPE.XMPP): self.__chatSessions.restoreSession(jid, state)
class ContactsManager(ClientEventsHandler): __slots__ = ('__seq', '__tasks', '__cooldown', '__presence', '__voip', '__rqRestrictions', '__subsBatch', '__subsRestrictions') def __init__(self): super(ContactsManager, self).__init__() self.__seq = SeqTaskQueue() self.__seq.suspend() self.__tasks = ContactTaskQueue([block_tasks.SyncBlockItemTask()]) self.__cooldown = XmppCooldownManager() self.__subsBatch = sub_helper.InboundSubscriptionsBatch() self.__subsRestrictions = sub_helper.SubscriptionsRestrictions() self.__presence = _UserPresence() self.__presence.addListeners() self.__voip = VoipHandler() self.__voip.addListeners() g_messengerEvents.onPluginConnectFailed += self.__me_onPluginConnectFailed self.usersStorage.onRestoredFromCache += self.__us_onRestoredFromCache g_settings.onUserPreferencesUpdated += self.__ms_onUserPreferencesUpdated @storage_getter('users') def usersStorage(self): return None @storage_getter('playerCtx') def playerCtx(self): return None def isInited(self): return self.__seq.isInited() def clear(self): g_settings.onUserPreferencesUpdated -= self.__ms_onUserPreferencesUpdated g_messengerEvents.onPluginConnectFailed -= self.__me_onPluginConnectFailed self.usersStorage.onRestoredFromCache -= self.__us_onRestoredFromCache self.__presence.removeListeners() self.__voip.removeListeners() self.__subsBatch.clear() self.__clearTemporaryFlags() super(ContactsManager, self).clear() def switch(self, scope): self.__presence.switch(scope) self.__clearTemporaryFlags() @notations.contacts(PROTO_TYPE.XMPP, log=False) def registerHandlers(self): register = self.client().registerHandler register(_EVENT.CONNECTED, self.__handleConnected) register(_EVENT.DISCONNECTED, self.__handleDisconnected) register(_EVENT.IQ, self.__handleIQ) register(_EVENT.ROSTER_QUERY, self.__handleRosterQuery) register(_EVENT.ROSTER_RESULT, self.__handleRosterResult) register(_EVENT.ROSTER_ITEM_SET, self.__handleRosterItemSet) register(_EVENT.ROSTER_ITEM_REMOVED, self.__handleRosterItemRemoved) register(_EVENT.PRESENCE, self.__handlePresence) register(_EVENT.SUBSCRIPTION_REQUEST, self.__handleSubscriptionRequest) @notations.contacts(PROTO_TYPE.XMPP, log=False) def unregisterHandlers(self): unregister = self.client().unregisterHandler unregister(_EVENT.CONNECTED, self.__handleConnected) unregister(_EVENT.DISCONNECTED, self.__handleDisconnected) unregister(_EVENT.IQ, self.__handleIQ) unregister(_EVENT.ROSTER_QUERY, self.__handleRosterQuery) unregister(_EVENT.ROSTER_RESULT, self.__handleRosterResult) unregister(_EVENT.ROSTER_ITEM_SET, self.__handleRosterItemSet) unregister(_EVENT.ROSTER_ITEM_REMOVED, self.__handleRosterItemRemoved) unregister(_EVENT.PRESENCE, self.__handlePresence) unregister(_EVENT.SUBSCRIPTION_REQUEST, self.__handleSubscriptionRequest) self.__tasks.clear() @xmpp_query(QUERY_SIGN.DATABASE_ID, QUERY_SIGN.ACCOUNT_NAME, QUERY_SIGN.OPT_GROUP_NAME) def addFriend(self, dbID, name, group=None): error = self._checkCooldown(CLIENT_ACTION_ID.ADD_FRIEND) if error: return (False, error) else: if group: if not self.usersStorage.isGroupExists(group): return (False, ClientContactError( CONTACT_ERROR_ID.GROUP_NOT_FOUND, group)) groups = {group} else: groups = None contact = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) tasks, itemType = [], XMPP_ITEM_TYPE.EMPTY_ITEM if contact: if contact.isCurrentPlayer(): return (False, ClientActionError(CLIENT_ACTION_ID.ADD_FRIEND, CLIENT_ERROR_ID.GENERIC)) jid = contact.getJID() itemType = contact.getItemType() if itemType in XMPP_ITEM_TYPE.ROSTER_ITEMS: return (False, ClientContactError( CONTACT_ERROR_ID.ROSTER_ITEM_EXISTS, contact.getFullName())) subTo = contact.getSubscription()[0] else: jid = makeContactJID(dbID) subTo = _SUB.OFF result, error = self.__subsRestrictions.canAddFriends() if not result: return (False, error) if itemType == XMPP_ITEM_TYPE.TMP_BLOCK_ITEM: tasks.append(block_tasks.RemoveTmpBlockItemTask(jid, name)) tasks.append(roster_tasks.AddRosterItemTask(jid, name, groups)) elif itemType == XMPP_ITEM_TYPE.ROSTER_TMP_BLOCK_ITEM: tasks.append(block_tasks.RemoveTmpBlockItemTask(jid, name)) elif itemType == XMPP_ITEM_TYPE.BLOCK_ITEM: tasks.append(block_tasks.RemoveBlockItemTask(jid, name)) tasks.append(roster_tasks.AddRosterItemTask(jid, name, groups)) elif itemType == XMPP_ITEM_TYPE.ROSTER_BLOCK_ITEM: tasks.append(block_tasks.RemoveBlockItemTask(jid, name)) task, exclude = None, set() rosterGroups = contact.getItem().getRosterGroups() for rosterGroup in rosterGroups: if self.usersStorage.isGroupEmpty(rosterGroup): exclude.add(rosterGroup) if groups: if groups != exclude: task = roster_tasks.ChangeRosterItemGroupsTask( jid, name, groups, exclude) elif rosterGroups: task = roster_tasks.ChangeRosterItemGroupsTask( jid, name, set(), exclude) if task: tasks.append(task) elif itemType in XMPP_ITEM_TYPE.SUB_PENDING_ITEMS: if itemType == XMPP_ITEM_TYPE.SUB_PENDING_TMP_BLOCK_ITEM: tasks.append(block_tasks.RemoveTmpBlockItemTask(jid, name)) tasks.append(sub_tasks.ApproveSubscriptionTask(jid, name)) if groups: tasks.append( roster_tasks.ChangeRosterItemGroupsTask( jid, name, groups)) else: tasks.append(roster_tasks.AddRosterItemTask(jid, name, groups)) if subTo == _SUB.OFF: tasks.append(sub_tasks.AskSubscriptionTask(jid)) self.__cooldown.process(CLIENT_ACTION_ID.ADD_FRIEND) return self.__addTasks(CLIENT_ACTION_ID.ADD_FRIEND, jid, *tasks) @xmpp_query(QUERY_SIGN.DATABASE_ID) def removeFriend(self, dbID): error = self._checkCooldown(CLIENT_ACTION_ID.REMOVE_FRIEND) if error: return (False, error) contact = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) if not contact: return (False, ClientContactError( CONTACT_ERROR_ID.CONTACT_ITEM_NOT_FOUND)) if contact.getItemType() not in XMPP_ITEM_TYPE.ROSTER_ITEMS: return (False, ClientContactError(CONTACT_ERROR_ID.ROSTER_ITEM_NOT_FOUND, contact.getFullName())) jid = contact.getJID() self.__cooldown.process(CLIENT_ACTION_ID.REMOVE_FRIEND) tasks = [ roster_tasks.RemoveRosterItemTask(jid, contact.getName(), groups=contact.getGroups()) ] if note_tasks.canNoteAutoDelete(contact): tasks.append(note_tasks.RemoveNoteTask(jid)) return self.__addTasks(CLIENT_ACTION_ID.REMOVE_FRIEND, jid, *tasks) @xmpp_query(QUERY_SIGN.DATABASE_ID, QUERY_SIGN.OPT_GROUP_NAME, QUERY_SIGN.OPT_GROUP_NAME) def moveFriendToGroup(self, dbID, include=None, exclude=None): error = self._checkCooldown(CLIENT_ACTION_ID.CHANGE_GROUP) if error: return (False, error) else: contact = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) if not contact: return (False, ClientContactError( CONTACT_ERROR_ID.CONTACT_ITEM_NOT_FOUND)) if contact.getItemType() not in XMPP_ITEM_TYPE.ROSTER_ITEMS: return (False, ClientContactError( CONTACT_ERROR_ID.ROSTER_ITEM_NOT_FOUND, contact.getFullName())) groups = contact.getGroups() if include: if not self.usersStorage.isGroupExists(include): return (False, ClientContactError( CONTACT_ERROR_ID.GROUP_NOT_FOUND, include)) groups.add(include) if exclude: if not self.usersStorage.isGroupExists(exclude): return (False, ClientContactError( CONTACT_ERROR_ID.GROUP_NOT_FOUND, exclude)) groups.discard(exclude) if contact.getGroups() == groups: return (True, None) jid = contact.getJID() self.__cooldown.process(CLIENT_ACTION_ID.CHANGE_GROUP) return self.__addTasks( CLIENT_ACTION_ID.CHANGE_GROUP, jid, roster_tasks.ChangeRosterItemGroupsTask( jid, contact.getName(), groups, {exclude} if exclude else None)) @xmpp_query(QUERY_SIGN.GROUP_NAME) def addGroup(self, name): error = self._checkCooldown(CLIENT_ACTION_ID.ADD_GROUP) if error: return (False, error) elif self.usersStorage.isGroupExists(name): return (False, ClientContactError(CONTACT_ERROR_ID.GROUP_EXISTS, name)) elif len(self.usersStorage.getGroups() ) >= CONTACT_LIMIT.GROUPS_MAX_COUNT: return (False, ClientIntLimitError(LIMIT_ERROR_ID.MAX_GROUP, CONTACT_LIMIT.GROUPS_MAX_COUNT)) else: self.usersStorage.addEmptyGroup(name) g_messengerEvents.users.onEmptyGroupsChanged({name}, None) self.__cooldown.process(CLIENT_ACTION_ID.ADD_GROUP) return (True, None) @xmpp_query(QUERY_SIGN.GROUP_NAME, QUERY_SIGN.GROUP_NAME) def renameGroup(self, oldName, newName): error = self._checkCooldown(CLIENT_ACTION_ID.CHANGE_GROUP) if error: return (False, error) elif self.usersStorage.isGroupExists(newName): return (False, ClientContactError(CONTACT_ERROR_ID.GROUP_EXISTS)) elif newName == oldName: return (False, ClientActionError(CLIENT_ACTION_ID.CHANGE_GROUP, CLIENT_ERROR_ID.GENERIC)) elif self.usersStorage.isGroupEmpty(oldName): self.usersStorage.changeEmptyGroup(oldName, newName) g_messengerEvents.users.onEmptyGroupsChanged({newName}, {oldName}) return (True, None) else: task = self.__makeChangeGroupsChain(oldName, newName) self.__cooldown.process(CLIENT_ACTION_ID.CHANGE_GROUP) return self.__addTasks(CLIENT_ACTION_ID.CHANGE_GROUP, task.getJID(), task) @xmpp_query(QUERY_SIGN.GROUP_NAME) def removeGroup(self, name, isForced=False): error = self._checkCooldown(CLIENT_ACTION_ID.CHANGE_GROUP) if error: return (False, error) elif not self.usersStorage.isGroupExists(name): return (False, ClientContactError(CONTACT_ERROR_ID.GROUP_NOT_FOUND, name)) elif self.usersStorage.isGroupEmpty(name): self.usersStorage.changeEmptyGroup(name) g_messengerEvents.users.onEmptyGroupsChanged(None, {name}) return (True, None) else: if isForced: task = self.__makeRemoveItemsByGroupChain(name) else: task = self.__makeChangeGroupsChain(name) self.__cooldown.process(CLIENT_ACTION_ID.CHANGE_GROUP) return self.__addTasks(CLIENT_ACTION_ID.CHANGE_GROUP, task.getJID(), task) @xmpp_query(QUERY_SIGN.DATABASE_ID) def requestFriendship(self, dbID): error = self._checkCooldown(CLIENT_ACTION_ID.RQ_FRIENDSHIP) if error: return (False, error) contact = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) if not contact or contact.isCurrentPlayer(): return (False, ClientContactError( CONTACT_ERROR_ID.CONTACT_ITEM_NOT_FOUND)) itemType = contact.getItemType() if itemType == XMPP_ITEM_TYPE.BLOCK_ITEM: return (False, ClientContactError(CONTACT_ERROR_ID.BLOCK_ITEM_EXISTS, contact.getFullName())) if itemType not in XMPP_ITEM_TYPE.ROSTER_ITEMS: return (False, ClientContactError(CONTACT_ERROR_ID.ROSTER_ITEM_NOT_FOUND, contact.getFullName())) jid = contact.getJID() self.__cooldown.process(CLIENT_ACTION_ID.RQ_FRIENDSHIP) return self.__addTasks(CLIENT_ACTION_ID.RQ_FRIENDSHIP, jid, sub_tasks.AskSubscriptionTask(jid)) def canApproveFriendship(self, contact): return (False, ClientError(CLIENT_ERROR_ID.NOT_CONNECTED) ) if not self.client() or not self.client().isConnected( ) else self.__subsRestrictions.canApproveFriendship(contact) def canCancelFriendship(self, contact): return (False, ClientError(CLIENT_ERROR_ID.NOT_CONNECTED) ) if not self.client() or not self.client().isConnected( ) else self.__subsRestrictions.canCancelFriendship(contact) @xmpp_query(QUERY_SIGN.DATABASE_ID) def approveFriendship(self, dbID): contact = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) result, error = self.canApproveFriendship(contact) if not result: return (result, error) if contact.getItemType() in XMPP_ITEM_TYPE.ROSTER_ITEMS: jid = contact.getJID() tasks = [sub_tasks.ApproveSubscriptionTask(jid)] if contact.getSubscription()[0] == _SUB.OFF: tasks.append(sub_tasks.AskSubscriptionTask(jid)) else: jid = makeContactJID(dbID) tasks = (sub_tasks.ApproveSubscriptionTask(jid), sub_tasks.AskSubscriptionTask(jid)) return self.__addTasks(CLIENT_ACTION_ID.APPROVE_FRIENDSHIP, jid, *tasks) @xmpp_query(QUERY_SIGN.DATABASE_ID) def cancelFriendship(self, dbID): contact = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) result, error = self.canCancelFriendship(contact) if not result: return (result, error) jid = contact.getJID() tasks = [sub_tasks.CancelSubscriptionTask(jid)] if note_tasks.canNoteAutoDelete(contact): tasks.append(note_tasks.RemoveNoteTask(jid)) return self.__addTasks(CLIENT_ACTION_ID.CANCEL_FRIENDSHIP, jid, *tasks) def getFriendshipRqs(self): return self.usersStorage.getList(RqFriendshipCriteria()) @xmpp_query(QUERY_SIGN.DATABASE_ID, QUERY_SIGN.ACCOUNT_NAME) def addIgnored(self, dbID, name): error = self._checkCooldown(CLIENT_ACTION_ID.ADD_IGNORED) if error: return (False, error) tasks, itemType = [], XMPP_ITEM_TYPE.EMPTY_ITEM contact = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) if contact: if contact.isCurrentPlayer(): return (False, ClientActionError(CLIENT_ACTION_ID.ADD_IGNORED, CLIENT_ERROR_ID.GENERIC)) itemType = contact.getItemType() if itemType in XMPP_ITEM_TYPE.BLOCK_ITEMS: return (False, ClientContactError(CONTACT_ERROR_ID.BLOCK_ITEM_EXISTS, contact.getFullName())) length = self.usersStorage.getCount( ItemsFindCriteria(XMPP_ITEM_TYPE.PERSISTENT_BLOCKING_LIST)) if length >= CONTACT_LIMIT.BLOCK_MAX_COUNT: return (False, ClientIntLimitError(LIMIT_ERROR_ID.MAX_BLOCK_ITEMS, CONTACT_LIMIT.BLOCK_MAX_COUNT)) if contact: jid = contact.getJID() if itemType in XMPP_ITEM_TYPE.SUB_PENDING_ITEMS: tasks.append(sub_tasks.CancelSubscriptionTask(jid)) else: jid = makeContactJID(dbID) tasks.append(block_tasks.AddBlockItemTask(jid, name)) if itemType in XMPP_ITEM_TYPE.ROSTER_ITEMS: groups = contact.getGroups() if groups: tasks.append(roster_tasks.EmptyGroupsTask(jid, groups=groups)) self.__cooldown.process(CLIENT_ACTION_ID.ADD_IGNORED) return self.__addTasks(CLIENT_ACTION_ID.ADD_IGNORED, jid, *tasks) def addTmpIgnored(self, dbID, name): error = self._checkCooldown(CLIENT_ACTION_ID.ADD_IGNORED) if error: return (False, error) tasks, itemType = [], XMPP_ITEM_TYPE.EMPTY_ITEM contact = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) if contact: if contact.isCurrentPlayer(): return (False, ClientActionError(CLIENT_ACTION_ID.ADD_IGNORED, CLIENT_ERROR_ID.GENERIC)) itemType = contact.getItemType() if itemType in XMPP_ITEM_TYPE.BLOCK_ITEMS: return (False, ClientContactError(CONTACT_ERROR_ID.BLOCK_ITEM_EXISTS, contact.getFullName())) if contact: jid = contact.getJID() else: jid = makeContactJID(dbID) tasks.append(block_tasks.AddTmpBlockItemTask(jid, name)) self.__cooldown.process(CLIENT_ACTION_ID.ADD_IGNORED) return self.__addTasks(CLIENT_ACTION_ID.ADD_IGNORED, jid, *tasks) def removeIgnored(self, dbID): error = self._checkCooldown(CLIENT_ACTION_ID.REMOVE_IGNORED) if error: return (False, error) contact = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) if not contact: return (False, ClientContactError( CONTACT_ERROR_ID.CONTACT_ITEM_NOT_FOUND)) itemType = contact.getItemType() if itemType not in XMPP_ITEM_TYPE.PERSISTENT_BLOCKING_LIST: return (False, ClientContactError(CONTACT_ERROR_ID.BLOCK_ITEM_NOT_FOUND, contact.getFullName())) jid = contact.getJID() tasks = [block_tasks.RemoveBlockItemTask(jid, contact.getName())] if itemType == XMPP_ITEM_TYPE.ROSTER_BLOCK_ITEM: tasks.append( roster_tasks.RemoveRosterItemTask( jid, contact.getName(), groups=contact.getItem().getRosterGroups())) if note_tasks.canNoteAutoDelete(contact): tasks.append(note_tasks.RemoveNoteTask(jid)) self.__cooldown.process(CLIENT_ACTION_ID.REMOVE_IGNORED) return self.__addTasks(CLIENT_ACTION_ID.REMOVE_IGNORED, jid, *tasks) @xmpp_query(QUERY_SIGN.DATABASE_ID) def removeTmpIgnored(self, dbID): return self.__removeTmpIgnored(dbID) @local_query(QUERY_SIGN.DATABASE_ID, QUERY_SIGN.ACCOUNT_NAME) def setMuted(self, dbID, name): error = self._checkCooldown(CLIENT_ACTION_ID.SET_MUTE) if error: return (False, error) else: contact = self.usersStorage.getUser(dbID) if not contact: contact = entities.XMPPUserEntity(dbID, name=name, tags={USER_TAG.MUTED}) self.usersStorage.setUser(contact) else: contact.addTags({USER_TAG.MUTED}) g_messengerEvents.users.onUserActionReceived( USER_ACTION_ID.MUTE_SET, contact) self.__cooldown.process(CLIENT_ACTION_ID.SET_MUTE) return (True, None) @local_query(QUERY_SIGN.DATABASE_ID) def unsetMuted(self, dbID): error = self._checkCooldown(CLIENT_ACTION_ID.UNSET_MUTE) if error: return (False, error) else: contact = self.usersStorage.getUser(dbID) if not contact: return (False, ClientContactError( CONTACT_ERROR_ID.CONTACT_ITEM_NOT_FOUND)) if not contact.isMuted(): return (False, ClientContactError( CONTACT_ERROR_ID.MUTED_ITEM_NOT_FOUND, contact.getFullName())) contact.removeTags({USER_TAG.MUTED}) g_messengerEvents.users.onUserActionReceived( USER_ACTION_ID.MUTE_UNSET, contact) self.__cooldown.process(CLIENT_ACTION_ID.UNSET_MUTE) return (True, None) @xmpp_query(QUERY_SIGN.DATABASE_ID, QUERY_SIGN.NOTE_TEXT) def setNote(self, dbID, note): error = self._checkCooldown(CLIENT_ACTION_ID.SET_NOTE) if error: return (False, error) contact = self.usersStorage.getUser(dbID) if not contact or not contact.getTags(): return (False, ClientContactError( CONTACT_ERROR_ID.CONTACT_ITEM_NOT_FOUND)) jid = makeContactJID(dbID) self.__cooldown.process(CLIENT_ACTION_ID.SET_NOTE) return self.__addTasks(CLIENT_ACTION_ID.SET_NOTE, jid, note_tasks.SetNoteTask(jid, note)) @xmpp_query(QUERY_SIGN.DATABASE_ID) def removeNote(self, dbID): error = self._checkCooldown(CLIENT_ACTION_ID.REMOVE_NOTE) if error: return (False, error) contact = self.usersStorage.getUser(dbID) if not contact or not contact.getTags(): return (False, ClientContactError( CONTACT_ERROR_ID.CONTACT_ITEM_NOT_FOUND)) if not contact.getNote(): return (False, ClientContactError(CONTACT_ERROR_ID.NOTE_NOT_FOUND, name=contact.getFullName())) jid = makeContactJID(dbID) self.__cooldown.process(CLIENT_ACTION_ID.REMOVE_NOTE) return self.__addTasks(CLIENT_ACTION_ID.SET_NOTE, jid, note_tasks.RemoveNoteTask(jid)) def getUserScope(self): return self.__presence.getUserScope() def _checkCooldown(self, actionID): error = None if self.__cooldown.isInProcess(actionID): error = ChatCoolDownError(actionID, self.__cooldown.getDefaultCoolDown()) return error def _processCooldown(self, actionID): self.__cooldown.process(actionID) def __makeChangeGroupsChain(self, exclude, include=None): chain = [] for contact in self.usersStorage.getList(GroupFindCriteria(exclude)): jid = contact.getJID() groups = contact.getGroups() groups.discard(exclude) if include: groups.add(include) chain.append((jid, contact.getName(), groups)) return roster_tasks.ChangeRosterItemsGroupsChain(chain) def __makeRemoveItemsByGroupChain(self, name): chain = [] for contact in self.usersStorage.getList(GroupFindCriteria(name)): groups = contact.getGroups() groups.discard(name) chain.append((contact.getJID(), contact.getName(), groups)) return roster_tasks.RemoveRosterItemsGroupsChain(chain) def __addTasks(self, actionID, jid, *tasks): if self.__tasks.addTasks(jid, *tasks): self.__tasks.runFirstTask(jid) else: return (False, ClientActionError(actionID, CLIENT_ERROR_ID.LOCKED)) return (True, None) def __removeTmpIgnored(self, dbID, forced=False): if not forced: error = self._checkCooldown(CLIENT_ACTION_ID.REMOVE_IGNORED) if error: return (False, error) contact = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) if not contact: return (False, ClientContactError( CONTACT_ERROR_ID.CONTACT_ITEM_NOT_FOUND)) itemType = contact.getItemType() if itemType not in XMPP_ITEM_TYPE.TMP_BLOCKING_LIST: return (False, ClientContactError(CONTACT_ERROR_ID.BLOCK_ITEM_NOT_FOUND, contact.getFullName())) jid = contact.getJID() tasks = [block_tasks.RemoveTmpBlockItemTask(jid, contact.getName())] if not forced: self.__cooldown.process(CLIENT_ACTION_ID.REMOVE_IGNORED) return self.__addTasks(CLIENT_ACTION_ID.REMOVE_IGNORED, jid, *tasks) def __clearTemporaryFlags(self): tmpIgnored = self.usersStorage.getList(TemporaryIgnoredFindCriteria()) for usr in tmpIgnored: self.__removeTmpIgnored(usr.getID(), forced=True) def __handleConnected(self): self.__tasks.suspend() self.__seq.onInited += self.__onSeqsInited self.__seq.init(roster_tasks.RosterResultTask(), block_tasks.BlockListResultTask(), note_tasks.NotesListTask()) def __handleDisconnected(self, reason, description): if reason == DISCONNECT_REASON.BY_REQUEST: self.__seq.suspend() self.__seq.fini() self.__tasks.clear() self.__subsBatch.clear() for contact in self.usersStorage.getList( ProtoFindCriteria(PROTO_TYPE.XMPP)): resources = contact.getItem().getResources() if not resources.isEmpty(): resources.clear() g_messengerEvents.users.onUserStatusUpdated(contact) def __handleIQ(self, iqID, iqType, pyGlooxTag): if not self.__seq.handleIQ(iqID, iqType, pyGlooxTag): self.__tasks.handleIQ(iqID, iqType, pyGlooxTag) def __handleRosterQuery(self, iqID, jid, context): self.__tasks.setIQ(iqID, jid, context) def __handleRosterResult(self, generator): g_logOutput.debug(_LOG.ROSTER, 'Roster result is received') self.__seq.sync(0, generator()) def __handleRosterItemSet(self, jid, name, groups, sub, clanInfo): g_logOutput.debug(_LOG.ROSTER, 'Roster push is received', jid, name, groups, sub, clanInfo) self.__tasks.sync(jid, name, groups, sub, clanInfo, defaultTask=roster_tasks.SyncSubscriptionTask) def __handleRosterItemRemoved(self, jid): self.__tasks.sync(jid, defaultTask=roster_tasks.RemoveRosterItemTask) def __handlePresence(self, jid, resource): jid = ContactJID(jid) dbID = jid.getDatabaseID() if not dbID: return else: user = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) if resource.presence == PRESENCE.UNAVAILABLE: if user and not user.isCurrentPlayer(): user.update(jid=jid, resource=None, clanInfo=resource.getClanInfo()) g_logOutput.debug(_LOG.RESOURCE, 'Resource is removed', user.getName(), jid.getResource(), resource) elif resource.presence != PRESENCE.UNKNOWN: if not user: user = entities.XMPPUserEntity(dbID) self.usersStorage.setUser(user) if user.isCurrentPlayer(): self.playerCtx.setBanInfo(resource.getBanInfo()) else: user.update(jid=jid, resource=resource, clanInfo=resource.getClanInfo()) g_logOutput.debug(_LOG.RESOURCE, 'Resource is set', user.getName(), jid.getResource(), resource) if user: g_messengerEvents.users.onUserStatusUpdated(user) return def __handleSubscriptionRequest(self, subs): self.__subsBatch.addSubs(subs) if not self.__seq.isInited(): return self.__subsRestrictions.setToUseCachedCounts(True) self.__subsBatch.process(self.__tasks) self.__subsRestrictions.setToUseCachedCounts(False) def __onSeqsInited(self): g_logOutput.debug(_LOG.GENERIC, 'Starts to process contacts tasks') self.__presence.sendPresence(True) self.__tasks.release() self.__tasks.onSeqTaskRequested += self.__onSeqTaskRequested self.__subsRestrictions.setToUseCachedCounts(True) self.__subsBatch.process(self.__tasks) self.__subsRestrictions.setToUseCachedCounts(False) g_messengerEvents.users.onUsersListReceived({ USER_TAG.FRIEND, USER_TAG.IGNORED, USER_TAG.IGNORED_TMP, USER_TAG.MUTED }) def __onSeqTaskRequested(self, task): self.__seq.addMultiRq(task) def __me_onPluginConnectFailed(self, protoType, _, tries): if protoType != PROTO_TYPE.XMPP: return scope = self.__presence.getUserScope() if scope == MESSENGER_SCOPE.BATTLE: threshold = _MAX_TRIES_FAILED_IN_BATTLE else: threshold = _MAX_TRIES_FAILED_IN_LOBBY if tries == threshold: g_messengerEvents.users.onUsersListReceived({ USER_TAG.CACHED, USER_TAG.FRIEND, USER_TAG.IGNORED, USER_TAG.IGNORED_TMP, USER_TAG.MUTED }) def __us_onRestoredFromCache(self, stateGenerator): if not g_settings.server.XMPP.isEnabled(): return if stateGenerator: setUser = self.usersStorage.setUser isInited = self.__seq.isInited() for dbID, state in stateGenerator(PROTO_TYPE.XMPP): contact = entities.XMPPUserEntity(dbID) result = contact.setPersistentState(state) if result and (not isInited or contact.getItemType() in XMPP_ITEM_TYPE.SUB_PENDING_ITEMS): setUser(contact) g_messengerEvents.users.onUsersListReceived({ USER_TAG.CACHED, USER_TAG.FRIEND, USER_TAG.IGNORED, USER_TAG.IGNORED_TMP, USER_TAG.MUTED }) def __ms_onUserPreferencesUpdated(self): self.__seq.release()
class ContactsManager(ClientEventsHandler): __slots__ = ('__seq', '__tasks', '__cooldown', '__presence', '__voip') def __init__(self): super(ContactsManager, self).__init__() self.__seq = SeqTaskQueue() self.__tasks = ContactTaskQueue() self.__cooldown = XmppCooldownManager() self.__presence = _UserPresence() self.__presence.addListeners() self.__voip = _VoipHandler() self.__voip.addListeners() g_messengerEvents.onPluginConnectFailed += self.__me_onPluginConnectFailed self.usersStorage.onRestoredFromCache += self.__us_onRestoredFromCache @storage_getter('users') def usersStorage(self): return None @storage_getter('playerCtx') def playerCtx(self): return None def isInited(self): return self.__seq.isInited() def clear(self): g_messengerEvents.onPluginConnectFailed -= self.__me_onPluginConnectFailed self.usersStorage.onRestoredFromCache -= self.__us_onRestoredFromCache self.__presence.removeListeners() self.__voip.removeListeners() super(ContactsManager, self).clear() def switch(self, scope): self.__presence.switch(scope) @notations.contacts(PROTO_TYPE.XMPP, log=False) def registerHandlers(self): register = self.client().registerHandler register(_EVENT.CONNECTED, self.__handleConnected) register(_EVENT.DISCONNECTED, self.__handleDisconnected) register(_EVENT.IQ, self.__handleIQ) register(_EVENT.ROSTER_QUERY, self.__handleRosterQuery) register(_EVENT.ROSTER_RESULT, self.__handleRosterResult) register(_EVENT.ROSTER_ITEM_SET, self.__handleRosterItemSet) register(_EVENT.ROSTER_ITEM_REMOVED, self.__handleRosterItemRemoved) register(_EVENT.ROSTER_RESOURCE_ADDED, self.__handleRosterResourceAdded) register(_EVENT.ROSTER_RESOURCE_REMOVED, self.__handleRosterResourceRemoved) register(_EVENT.SUBSCRIPTION_REQUEST, self.__handleSubscriptionRequest) register(_EVENT.IGR, self.__handlePresenceWithIGR) @notations.contacts(PROTO_TYPE.XMPP, log=False) def unregisterHandlers(self): unregister = self.client().unregisterHandler unregister(_EVENT.CONNECTED, self.__handleConnected) unregister(_EVENT.DISCONNECTED, self.__handleDisconnected) unregister(_EVENT.IQ, self.__handleIQ) unregister(_EVENT.ROSTER_QUERY, self.__handleRosterQuery) unregister(_EVENT.ROSTER_RESULT, self.__handleRosterResult) unregister(_EVENT.ROSTER_ITEM_SET, self.__handleRosterItemSet) unregister(_EVENT.ROSTER_ITEM_REMOVED, self.__handleRosterItemRemoved) unregister(_EVENT.ROSTER_RESOURCE_ADDED, self.__handleRosterResourceAdded) unregister(_EVENT.ROSTER_RESOURCE_REMOVED, self.__handleRosterResourceRemoved) unregister(_EVENT.SUBSCRIPTION_REQUEST, self.__handleSubscriptionRequest) unregister(_EVENT.IGR, self.__handlePresenceWithIGR) self.__tasks.clear() @xmpp_query(QUERY_SIGN.DATABASE_ID, QUERY_SIGN.ACCOUNT_NAME, QUERY_SIGN.OPT_GROUP_NAME) def addFriend(self, dbID, name, group = None): error = self.__checkCooldown(CLIENT_ACTION_ID.ADD_FRIEND) if error: return (False, error) else: if group: if not self.usersStorage.isGroupExists(group): return (False, ClientContactError(CONTACT_ERROR_ID.GROUP_NOT_FOUND, group)) groups = {group} else: groups = None contact = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) tasks, itemType = [], XMPP_ITEM_TYPE.EMPTY_ITEM if contact: if contact.isCurrentPlayer(): return (False, ClientActionError(CLIENT_ACTION_ID.ADD_FRIEND, CLIENT_ERROR_ID.GENERIC)) jid = contact.getJID() itemType = contact.getItemType() if itemType == XMPP_ITEM_TYPE.ROSTER_ITEM: return (False, ClientContactError(CONTACT_ERROR_ID.ROSTER_ITEM_EXISTS, contact.getFullName())) subTo = contact.getSubscription()[0] else: jid = makeContactJID(dbID) subTo = _SUB.OFF error = self.__checkRosterSize() if error: return (False, error) if itemType == XMPP_ITEM_TYPE.BLOCK_ITEM: tasks.append(block_tasks.RemoveBlockItemTask(jid, name)) tasks.append(roster_tasks.AddRosterItemTask(jid, name, groups)) elif itemType == XMPP_ITEM_TYPE.ROSTER_BLOCK_ITEM: tasks.append(block_tasks.RemoveBlockItemTask(jid, name)) task, exclude = None, set() rosterGroups = contact.getItem().getRosterGroups() for group in rosterGroups: if self.usersStorage.isGroupEmpty(group): exclude.add(group) if groups: if groups != exclude: task = roster_tasks.ChangeRosterItemGroupsTask(jid, name, groups, exclude) elif rosterGroups: task = roster_tasks.ChangeRosterItemGroupsTask(jid, name, set(), exclude) if task: tasks.append(task) elif itemType == XMPP_ITEM_TYPE.SUB_PENDING: tasks.append(sub_tasks.ApproveSubscriptionTask(jid, name)) if groups: tasks.append(roster_tasks.ChangeRosterItemGroupsTask(jid, name, groups)) else: tasks.append(roster_tasks.AddRosterItemTask(jid, name, groups)) if subTo == _SUB.OFF: tasks.append(sub_tasks.AskSubscriptionTask(jid)) self.__cooldown.process(CLIENT_ACTION_ID.ADD_FRIEND) return self.__addTasks(CLIENT_ACTION_ID.ADD_FRIEND, jid, *tasks) @xmpp_query(QUERY_SIGN.DATABASE_ID) def removeFriend(self, dbID): error = self.__checkCooldown(CLIENT_ACTION_ID.REMOVE_FRIEND) if error: return (False, error) contact = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) if not contact: return (False, ClientContactError(CONTACT_ERROR_ID.CONTACT_ITEM_NOT_FOUND)) if contact.getItemType() != XMPP_ITEM_TYPE.ROSTER_ITEM: return (False, ClientContactError(CONTACT_ERROR_ID.ROSTER_ITEM_NOT_FOUND, contact.getFullName())) jid = contact.getJID() self.__cooldown.process(CLIENT_ACTION_ID.REMOVE_FRIEND) return self.__addTasks(CLIENT_ACTION_ID.REMOVE_FRIEND, jid, roster_tasks.RemoveRosterItemTask(jid, contact.getName(), groups=contact.getGroups())) @xmpp_query(QUERY_SIGN.DATABASE_ID, QUERY_SIGN.OPT_GROUP_NAME, QUERY_SIGN.OPT_GROUP_NAME) def moveFriendToGroup(self, dbID, include = None, exclude = None): error = self.__checkCooldown(CLIENT_ACTION_ID.CHANGE_GROUP) if error: return (False, error) else: contact = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) if not contact: return (False, ClientContactError(CONTACT_ERROR_ID.CONTACT_ITEM_NOT_FOUND)) if contact.getItemType() != XMPP_ITEM_TYPE.ROSTER_ITEM: return (False, ClientContactError(CONTACT_ERROR_ID.ROSTER_ITEM_NOT_FOUND, contact.getFullName())) groups = contact.getGroups() if include: if not self.usersStorage.isGroupExists(include): return (False, ClientContactError(CONTACT_ERROR_ID.GROUP_NOT_FOUND, include)) groups.add(include) if exclude: if not self.usersStorage.isGroupExists(exclude): return (False, ClientContactError(CONTACT_ERROR_ID.GROUP_NOT_FOUND, exclude)) groups.discard(exclude) jid = contact.getJID() self.__cooldown.process(CLIENT_ACTION_ID.CHANGE_GROUP) return self.__addTasks(CLIENT_ACTION_ID.CHANGE_GROUP, jid, roster_tasks.ChangeRosterItemGroupsTask(jid, contact.getName(), groups, {exclude} if exclude else None)) @xmpp_query(QUERY_SIGN.GROUP_NAME) def addGroup(self, name): error = self.__checkCooldown(CLIENT_ACTION_ID.ADD_GROUP) if error: return (False, error) elif self.usersStorage.isGroupExists(name): return (False, ClientContactError(CONTACT_ERROR_ID.GROUP_EXISTS, name)) elif len(self.usersStorage.getGroups()) >= CONTACT_LIMIT.GROUPS_MAX_COUNT: return (False, ClientIntLimitError(LIMIT_ERROR_ID.MAX_GROUP, CONTACT_LIMIT.GROUPS_MAX_COUNT)) else: self.usersStorage.addEmptyGroup(name) g_messengerEvents.users.onEmptyGroupsChanged({name}, None) self.__cooldown.process(CLIENT_ACTION_ID.ADD_GROUP) return (True, None) @xmpp_query(QUERY_SIGN.GROUP_NAME, QUERY_SIGN.GROUP_NAME) def renameGroup(self, oldName, newName): error = self.__checkCooldown(CLIENT_ACTION_ID.CHANGE_GROUP) if error: return (False, error) elif self.usersStorage.isGroupExists(newName): return (False, ClientContactError(CONTACT_ERROR_ID.GROUP_EXISTS)) elif newName == oldName: return (False, ClientActionError(CLIENT_ACTION_ID.CHANGE_GROUP, CLIENT_ERROR_ID.GENERIC)) elif self.usersStorage.isGroupEmpty(oldName): self.usersStorage.changeEmptyGroup(oldName, newName) g_messengerEvents.users.onEmptyGroupsChanged({newName}, {oldName}) return (True, None) else: task = self.__makeChangeGroupsChain(oldName, newName) self.__cooldown.process(CLIENT_ACTION_ID.CHANGE_GROUP) return self.__addTasks(CLIENT_ACTION_ID.CHANGE_GROUP, task.getJID(), task) @xmpp_query(QUERY_SIGN.GROUP_NAME) def removeGroup(self, name, isForced = False): error = self.__checkCooldown(CLIENT_ACTION_ID.CHANGE_GROUP) if error: return (False, error) elif not self.usersStorage.isGroupExists(name): return (False, ClientContactError(CONTACT_ERROR_ID.GROUP_NOT_FOUND, name)) elif self.usersStorage.isGroupEmpty(name): self.usersStorage.changeEmptyGroup(name) g_messengerEvents.users.onEmptyGroupsChanged(None, {name}) return (True, None) else: if isForced: task = self.__makeRemoveItemsByGroupChain(name) else: task = self.__makeChangeGroupsChain(name) self.__cooldown.process(CLIENT_ACTION_ID.CHANGE_GROUP) return self.__addTasks(CLIENT_ACTION_ID.CHANGE_GROUP, task.getJID(), task) @xmpp_query(QUERY_SIGN.DATABASE_ID) def requestFriendship(self, dbID): error = self.__checkCooldown(CLIENT_ACTION_ID.RQ_FRIENDSHIP) if error: return (False, error) contact = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) if not contact or contact.isCurrentPlayer(): return (False, ClientContactError(CONTACT_ERROR_ID.CONTACT_ITEM_NOT_FOUND)) itemType = contact.getItemType() if itemType == XMPP_ITEM_TYPE.BLOCK_ITEM: return (False, ClientContactError(CONTACT_ERROR_ID.BLOCK_ITEM_EXISTS, contact.getFullName())) if itemType != XMPP_ITEM_TYPE.ROSTER_ITEM: return (False, ClientContactError(CONTACT_ERROR_ID.ROSTER_ITEM_NOT_FOUND, contact.getFullName())) jid = contact.getJID() self.__cooldown.process(CLIENT_ACTION_ID.RQ_FRIENDSHIP) return self.__addTasks(CLIENT_ACTION_ID.RQ_FRIENDSHIP, jid, sub_tasks.AskSubscriptionTask(jid)) def canApproveFriendship(self, contact): if not self.client() or not self.client().isConnected(): return (False, ClientError(CLIENT_ERROR_ID.NOT_CONNECTED)) elif not contact: return (False, ClientContactError(CONTACT_ERROR_ID.CONTACT_ITEM_NOT_FOUND)) tags = contact.getTags() if USER_TAG.SUB_APPROVED in tags: return (False, ClientContactError(CONTACT_ERROR_ID.FRIENDSHIP_APPROVED, contact.getFullName())) if contact.getItemType() == XMPP_ITEM_TYPE.ROSTER_ITEM: if USER_TAG.SUB_FROM in contact.getTags(): return (False, ClientContactError(CONTACT_ERROR_ID.FRIENDSHIP_APPROVED, contact.getFullName())) else: return (True, None) if contact.getItemType() == XMPP_ITEM_TYPE.SUB_PENDING: if USER_TAG.SUB_IN_PROCESS in tags: return (False, ClientContactError(CONTACT_ERROR_ID.FRIENDSHIP_RQ_PROCESS, contact.getFullName())) if USER_TAG.SUB_CANCELED in tags: return (False, ClientContactError(CONTACT_ERROR_ID.FRIENDSHIP_CANCELED, contact.getFullName())) error = self.__checkRosterSize() if error: return (False, error) return (True, None) else: return (False, ClientContactError(CONTACT_ERROR_ID.CONTACT_ITEM_NOT_FOUND)) def canCancelFriendship(self, contact): if not self.client() or not self.client().isConnected(): return (False, ClientError(CLIENT_ERROR_ID.NOT_CONNECTED)) elif not contact: return (False, ClientContactError(CONTACT_ERROR_ID.CONTACT_ITEM_NOT_FOUND)) tags = contact.getTags() if USER_TAG.SUB_APPROVED in tags: return (False, ClientContactError(CONTACT_ERROR_ID.FRIENDSHIP_APPROVED, contact.getFullName())) elif USER_TAG.SUB_FROM in tags: return (False, ClientContactError(CONTACT_ERROR_ID.FRIENDSHIP_APPROVED, contact.getFullName())) elif USER_TAG.SUB_IN_PROCESS in tags: return (False, ClientContactError(CONTACT_ERROR_ID.FRIENDSHIP_RQ_PROCESS, contact.getFullName())) elif USER_TAG.SUB_CANCELED in tags: return (False, ClientContactError(CONTACT_ERROR_ID.FRIENDSHIP_CANCELED, contact.getFullName())) elif contact.getItemType() == XMPP_ITEM_TYPE.ROSTER_ITEM: return (False, ClientContactError(CONTACT_ERROR_ID.ROSTER_ITEM_EXISTS, contact.getFullName())) else: return (True, None) @xmpp_query(QUERY_SIGN.DATABASE_ID) def approveFriendship(self, dbID): contact = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) result, error = self.canApproveFriendship(contact) if not result: return (result, error) if contact.getItemType() == XMPP_ITEM_TYPE.ROSTER_ITEM: jid = contact.getJID() tasks = [sub_tasks.ApproveSubscriptionTask(jid)] if contact.getSubscription()[0] == _SUB.OFF: tasks.append(sub_tasks.AskSubscriptionTask(jid)) else: jid = makeContactJID(dbID) tasks = (sub_tasks.ApproveSubscriptionTask(jid), sub_tasks.AskSubscriptionTask(jid)) return self.__addTasks(CLIENT_ACTION_ID.APPROVE_FRIENDSHIP, jid, *tasks) @xmpp_query(QUERY_SIGN.DATABASE_ID) def cancelFriendship(self, dbID): contact = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) result, error = self.canCancelFriendship(contact) if not result: return (result, error) jid = contact.getJID() task = sub_tasks.CancelSubscriptionTask(jid) return self.__addTasks(CLIENT_ACTION_ID.CANCEL_FRIENDSHIP, jid, task) def getFriendshipRqs(self): return self.usersStorage.getList(RqFriendshipCriteria()) @xmpp_query(QUERY_SIGN.DATABASE_ID, QUERY_SIGN.ACCOUNT_NAME) def addIgnored(self, dbID, name): error = self.__checkCooldown(CLIENT_ACTION_ID.ADD_IGNORED) if error: return (False, error) tasks, itemType = [], XMPP_ITEM_TYPE.EMPTY_ITEM contact = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) if contact: if contact.isCurrentPlayer(): return (False, ClientActionError(CLIENT_ACTION_ID.ADD_FRIEND, CLIENT_ERROR_ID.GENERIC)) itemType = contact.getItemType() if itemType == XMPP_ITEM_TYPE.BLOCK_ITEM: return (False, ClientContactError(CONTACT_ERROR_ID.BLOCK_ITEM_EXISTS, contact.getFullName())) length = self.usersStorage.getCount(ItemsFindCriteria(XMPP_ITEM_TYPE.BLOCKING_LIST)) if length >= CONTACT_LIMIT.BLOCK_MAX_COUNT: return (False, ClientIntLimitError(LIMIT_ERROR_ID.MAX_BLOCK_ITEMS, CONTACT_LIMIT.BLOCK_MAX_COUNT)) if contact: jid = contact.getJID() if itemType == XMPP_ITEM_TYPE.SUB_PENDING: tasks.append(sub_tasks.CancelSubscriptionTask(jid)) else: jid = makeContactJID(dbID) tasks.append(block_tasks.AddBlockItemTask(jid, name)) if itemType == XMPP_ITEM_TYPE.ROSTER_ITEM: groups = contact.getGroups() if groups: tasks.append(roster_tasks.EmptyGroupsTask(jid, groups=groups)) self.__cooldown.process(CLIENT_ACTION_ID.ADD_IGNORED) return self.__addTasks(CLIENT_ACTION_ID.ADD_IGNORED, jid, *tasks) @xmpp_query(QUERY_SIGN.DATABASE_ID) def removeIgnored(self, dbID): error = self.__checkCooldown(CLIENT_ACTION_ID.REMOVE_IGNORED) if error: return (False, error) contact = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) if not contact: return (False, ClientContactError(CONTACT_ERROR_ID.CONTACT_ITEM_NOT_FOUND)) itemType = contact.getItemType() if itemType not in XMPP_ITEM_TYPE.BLOCKING_LIST: return (False, ClientContactError(CONTACT_ERROR_ID.BLOCK_ITEM_NOT_FOUND, contact.getFullName())) jid = contact.getJID() tasks = [block_tasks.RemoveBlockItemTask(jid, contact.getName())] if itemType == XMPP_ITEM_TYPE.ROSTER_BLOCK_ITEM: tasks.append(roster_tasks.RemoveRosterItemTask(jid, contact.getName())) self.__cooldown.process(CLIENT_ACTION_ID.REMOVE_IGNORED) return self.__addTasks(CLIENT_ACTION_ID.REMOVE_IGNORED, jid, *tasks) @local_query(QUERY_SIGN.DATABASE_ID, QUERY_SIGN.ACCOUNT_NAME) def setMuted(self, dbID, name): error = self.__checkCooldown(CLIENT_ACTION_ID.SET_MUTE) if error: return (False, error) else: contact = self.usersStorage.getUser(dbID) if not contact: contact = entities.XMPPUserEntity(dbID, name=name, tags={USER_TAG.MUTED}) self.usersStorage.setUser(contact) else: contact.addTags({USER_TAG.MUTED}) g_messengerEvents.users.onUserActionReceived(USER_ACTION_ID.MUTE_SET, contact) self.__cooldown.process(CLIENT_ACTION_ID.SET_MUTE) return (True, None) @local_query(QUERY_SIGN.DATABASE_ID) def unsetMuted(self, dbID): error = self.__checkCooldown(CLIENT_ACTION_ID.UNSET_MUTE) if error: return (False, error) else: contact = self.usersStorage.getUser(dbID) if not contact: return (False, ClientContactError(CONTACT_ERROR_ID.CONTACT_ITEM_NOT_FOUND)) if not contact.isMuted(): return (False, ClientContactError(CONTACT_ERROR_ID.MUTED_ITEM_NOT_FOUND, contact.getFullName())) contact.removeTags({USER_TAG.MUTED}) g_messengerEvents.users.onUserActionReceived(USER_ACTION_ID.MUTE_SET, contact) self.__cooldown.process(CLIENT_ACTION_ID.UNSET_MUTE) return (True, None) @xmpp_query(QUERY_SIGN.DATABASE_ID, QUERY_SIGN.NOTE_TEXT) def setNote(self, dbID, note): error = self.__checkCooldown(CLIENT_ACTION_ID.SET_NOTE) if error: return (False, error) contact = self.usersStorage.getUser(dbID) if not contact or not contact.getTags(): return (False, ClientContactError(CONTACT_ERROR_ID.CONTACT_ITEM_NOT_FOUND)) jid = makeContactJID(dbID) self.__cooldown.process(CLIENT_ACTION_ID.SET_NOTE) return self.__addTasks(CLIENT_ACTION_ID.SET_NOTE, jid, note_tasks.SetNoteTask(jid, note)) @xmpp_query(QUERY_SIGN.DATABASE_ID) def removeNote(self, dbID): error = self.__checkCooldown(CLIENT_ACTION_ID.REMOVE_NOTE) if error: return (False, error) contact = self.usersStorage.getUser(dbID) if not contact or not contact.getTags(): return (False, ClientContactError(CONTACT_ERROR_ID.CONTACT_ITEM_NOT_FOUND)) if not contact.getNote(): return (False, ClientContactError(CONTACT_ERROR_ID.NOTE_NOT_FOUND, name=contact.getFullName())) jid = makeContactJID(dbID) self.__cooldown.process(CLIENT_ACTION_ID.REMOVE_NOTE) return self.__addTasks(CLIENT_ACTION_ID.SET_NOTE, jid, note_tasks.RemoveNoteTask(jid)) def __makeChangeGroupsChain(self, exclude, include = None): chain = [] for contact in self.usersStorage.getList(GroupFindCriteria(exclude)): jid = contact.getJID() groups = contact.getGroups() groups.discard(exclude) if include: groups.add(include) chain.append((jid, contact.getName(), groups)) return roster_tasks.ChangeRosterItemsGroupsChain(chain) def __makeRemoveItemsByGroupChain(self, name): chain = [] for contact in self.usersStorage.getList(GroupFindCriteria(name)): groups = contact.getGroups() groups.discard(name) chain.append((contact.getJID(), contact.getName(), groups)) return roster_tasks.RemoveRosterItemsGroupsChain(chain) def __addTasks(self, actionID, jid, *tasks): if self.__tasks.addTasks(jid, *tasks): self.__tasks.runFirstTask(jid) else: return (False, ClientActionError(actionID, CLIENT_ERROR_ID.LOCKED)) return (True, None) def __checkCooldown(self, actionID): error = None if self.__cooldown.isInProcess(actionID): error = ChatCoolDownError(actionID, self.__cooldown.getDefaultCoolDown()) return error def __checkRosterSize(self): length = self.usersStorage.getCount(ItemsFindCriteria((XMPP_ITEM_TYPE.ROSTER_ITEM,))) if length >= CONTACT_LIMIT.ROSTER_MAX_COUNT: return ClientIntLimitError(LIMIT_ERROR_ID.MAX_ROSTER_ITEMS, CONTACT_LIMIT.ROSTER_MAX_COUNT) else: return None def __handleConnected(self): self.__tasks.suspend() self.__seq.onInited += self.__onSeqsInited tasks = [roster_tasks.RosterResultTask(), block_tasks.BlockListResultTask()] if GUI_SETTINGS.isXmppNotesEnabled: tasks.append(note_tasks.NotesListTask()) self.__seq.init(*tasks) def __handleDisconnected(self, reason, description): self.__seq.fini() self.__tasks.clear() for contact in self.usersStorage.getList(ProtoFindCriteria(PROTO_TYPE.XMPP)): resources = contact.getItem().getResources() if not resources.isEmpty(): resources.clear() g_messengerEvents.users.onUserStatusUpdated(contact) def __handleIQ(self, iqID, iqType, pyGlooxTag): if not self.__seq.handleIQ(iqID, iqType, pyGlooxTag): self.__tasks.handleIQ(iqID, iqType, pyGlooxTag) def __handleRosterQuery(self, iqID, jid, context): self.__tasks.setIQ(iqID, jid, context) def __handleRosterResult(self, generator): g_logOutput.debug(CLIENT_LOG_AREA.ROSTER, 'Roster result is received') self.__seq.sync(0, generator()) def __handleRosterItemSet(self, jid, name, groups, to, from_): g_logOutput.debug(CLIENT_LOG_AREA.ROSTER, 'Roster push is received', jid, name, groups, to, from_) self.__tasks.sync(jid, name, groups, to, from_, defaultTask=roster_tasks.SyncSubscriptionTask) def __handleRosterItemRemoved(self, jid): self.__tasks.sync(jid) def __handleRosterResourceAdded(self, jid, resource): dbID = jid.getDatabaseID() user = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) if not user: user = entities.XMPPUserEntity(dbID) self.usersStorage.setUser(user) if not user.isCurrentPlayer(): user.update(jid=jid, resource=resource) g_logOutput.debug(CLIENT_LOG_AREA.RESOURCE, 'Resource is set', user.getName(), jid.getResource(), resource) g_messengerEvents.users.onUserStatusUpdated(user) def __handleRosterResourceRemoved(self, jid): user = self.usersStorage.getUser(jid.getDatabaseID(), PROTO_TYPE.XMPP) if user and not user.isCurrentPlayer(): user.update(jid=jid, resource=None) g_logOutput.debug(CLIENT_LOG_AREA.RESOURCE, 'Resource is removed', jid.getResource(), user.getName()) g_messengerEvents.users.onUserStatusUpdated(user) return def __handleSubscriptionRequest(self, jid, name, _): self.__addTasks(USER_ACTION_ID.SUBSCRIPTION_CHANGED, jid, sub_tasks.InboundSubscriptionTask(jid, name)) def __handlePresenceWithIGR(self, jid, resource): dbID = jid.getDatabaseID() user = self.usersStorage.getUser(dbID, PROTO_TYPE.XMPP) if user and user.isCurrentPlayer(): return if not user: user = entities.XMPPUserEntity(dbID) self.usersStorage.setUser(user) user.update(jid=jid, resource=resource) g_logOutput.debug(CLIENT_LOG_AREA.RESOURCE, 'Resource with IGR is set', user.getName(), jid.getResource(), resource) g_messengerEvents.users.onUserActionReceived(USER_ACTION_ID.IGR_CHANGED, user) def __onSeqsInited(self): self.__presence.sendPresence(True) self.__tasks.release() g_messengerEvents.users.onUsersListReceived({USER_TAG.FRIEND, USER_TAG.IGNORED, USER_TAG.MUTED}) def __me_onPluginConnectFailed(self, protoType, _, tries): if protoType != PROTO_TYPE.XMPP or tries != _MAX_CONNECTION_FAILED_COUNT: return g_messengerEvents.users.onUsersListReceived({USER_TAG.CACHED, USER_TAG.FRIEND, USER_TAG.IGNORED, USER_TAG.MUTED}) def __us_onRestoredFromCache(self, stateGenerator): if not g_settings.server.XMPP.isEnabled(): return if stateGenerator and not self.__seq.isInited(): setUser = self.usersStorage.setUser for dbID, state in stateGenerator(PROTO_TYPE.XMPP): contact = entities.XMPPUserEntity(dbID) if contact.setPersistentState(state): setUser(contact) g_messengerEvents.users.onUsersListReceived({USER_TAG.CACHED, USER_TAG.FRIEND, USER_TAG.IGNORED, USER_TAG.MUTED})
class SearchUsersProcessor(SearchProcessor, ClientEventsHandler): """ Search users by name using the XMPP """ def __init__(self): super(SearchUsersProcessor, self).__init__() self.__sentRequestID = None self.__limits = limits.FindUserSearchLimits() self.__cooldown = XmppCooldownManager(self.__limits.getRequestCooldown()) self.proto.client.registerHandler(GLOOX_EVENT.IQ, self.__onIQReceived) return def __del__(self): super(SearchUsersProcessor, self).__init__() self.proto.client.unregisterHandler(GLOOX_EVENT.IQ, self.__onIQReceived) @proto_getter(PROTO_TYPE.XMPP) def proto(self): return None @storage_getter('users') def usersStorage(self): return None def find(self, token, **kwargs): """ Process find request :param token: search token (username prefix) :param kwargs: args :return: None """ error = self.__checkCooldown(CLIENT_ACTION_ID.FIND_USERS_BY_PREFIX) if error: self._onSearchFailed(error.getMessage()) return token = token.strip() isCorrect, reason = checkAccountName(token) if not isCorrect: self._onSearchFailed(reason) return client = self.proto.client if client.isConnected(): query = NicknamePrefixSearchQuery(token, limit=self.getSearchResultLimit()) self._lastRequestID = client.sendIQ(query) self.__cooldown.process(CLIENT_ACTION_ID.FIND_USERS_BY_PREFIX) else: error = ClientActionError(CLIENT_ACTION_ID.FIND_USERS_BY_PREFIX, CLIENT_ERROR_ID.NOT_CONNECTED) self._onSearchFailed(error.getMessage()) def getSearchCoolDown(self): """ Get cooldown between requests :return: cooldown in seconds """ return self.__limits.getRequestCooldown() def getSearchResultLimit(self): """ Get limit for search query size :return: limit for search query size """ return self.__limits.getMaxResultSize() def __onIQReceived(self, iqID, iqType, pyGlooxTag): """ Process iq response form xmpp server :param iqID: iq id sequence number :param iqType: iq type (get/set) :param pyGlooxTag: xmpp parser wrapper :return: None """ if self._lastRequestID != iqID: return if iqType == IQ_TYPE.ERROR: error = createServerIQError(pyGlooxTag) if error: reason = error.getMessage() else: reason = '' LOG_WARNING('Search error is not resolved on the client', pyGlooxTag.getXml()) self._onSearchFailed(reason) else: users = self.__OnSuccesResponse(pyGlooxTag) self._onSearchTokenComplete(iqID, users) def __OnSuccesResponse(self, pyGlooxTag): """ Parse response object, and return user list :param pyGlooxTag: xmpp parser wrapper :return: """ result = NicknamePrefixSearchHandler().handleTag(pyGlooxTag) users = [] for userInfo in result: user = self.usersStorage.getUser(userInfo.dbId) if user: userInfo = user else: userInfo = XMPPUserEntity(userInfo.dbId, name=userInfo.nickname, clanInfo=userInfo.clanInfo) userInfo.addTags((USER_TAG.SEARCH, USER_TAG.TEMP)) self.usersStorage.addUser(userInfo) users.append(userInfo) return users def __checkCooldown(self, actionID): """ Check if cooldown was set for action :param actionID: action id :return: None, if cooldown was not set, else return ChatCoolDownError object """ error = None if self.__cooldown.isInProcess(actionID): error = ChatCoolDownError(actionID, self.__cooldown.getDefaultCoolDown()) return error
class MessagesManager(ClientEventsHandler): __slots__ = ('__msgFilters', '__limits', '__chatSessions', '__muc', '__receivedTags', '__pending', '__cooldown') def __init__(self): super(MessagesManager, self).__init__() self.__msgFilters = None self.__limits = _MessageLimits() self.__chatSessions = ChatSessionsProvider(self.__limits) self.__muc = MUCProvider() self.__receivedTags = set() self.__pending = [] self.__cooldown = XmppCooldownManager(self.__limits.getBroadcastCoolDown()) self.channelsStorage.onRestoredFromCache += self.__cs_onChannelsRestoredFromCache return @storage_getter('channels') def channelsStorage(self): return None def clear(self): self.channelsStorage.onRestoredFromCache -= self.__cs_onChannelsRestoredFromCache self.__clearData() super(MessagesManager, self).clear() def registerHandlers(self): register = self.client().registerHandler register(_EVENT.DISCONNECTED, self.__handleDisconnected) register(_EVENT.LOGIN, self.__handleLogin) register(_EVENT.PRESENCE, self.__handlePresence) register(_EVENT.PRESENCE_ERROR, self.__handlePresenceError) register(_EVENT.IQ, self.__handleIQ) register(_EVENT.MESSAGE, self.__handleMessage) events = g_messengerEvents.users events.onUsersListReceived += self.__me_onUsersListReceived events.onUserActionReceived += self.__me_onUserActionReceived events.onUserStatusUpdated += self.__me_onUserStatusUpdated def unregisterHandlers(self): unregister = self.client().unregisterHandler unregister(_EVENT.DISCONNECTED, self.__handleDisconnected) unregister(_EVENT.LOGIN, self.__handleLogin) unregister(_EVENT.PRESENCE, self.__handlePresence) unregister(_EVENT.PRESENCE_ERROR, self.__handlePresenceError) unregister(_EVENT.IQ, self.__handleIQ) unregister(_EVENT.MESSAGE, self.__handleMessage) events = g_messengerEvents.users events.onUsersListReceived -= self.__me_onUsersListReceived events.onUserActionReceived -= self.__me_onUserActionReceived events.onUserStatusUpdated -= self.__me_onUserStatusUpdated def setFilters(self, msgFilters): self.__msgFilters = msgFilters def isInCooldown(self): return self.__cooldown.isInProcess(CLIENT_ACTION_ID.SEND_MESSAGE) @xmpp_query(QUERY_SIGN.DATABASE_ID, QUERY_SIGN.ACCOUNT_NAME) def startChatSession(self, dbID, name): self.__chatSessions.startSession(jid_entity.makeContactJID(dbID), name) return (True, None) def stopChatSession(self, jid): self.__chatSessions.stopSession(jid.getBareJID()) def sendChatMessage(self, jid, body): if self.__cooldown.isInProcess(CLIENT_ACTION_ID.SEND_MESSAGE): g_messengerEvents.onErrorReceived(ChatCoolDownError(CLIENT_ACTION_ID.SEND_MESSAGE, self.__limits.getBroadcastCoolDown())) return body = self.__msgFilters.chainOut(body, self.__limits) if not body: return self.__chatSessions.sendMessage(jid.getBareJID(), body, self.__msgFilters) self.__cooldown.process(CLIENT_ACTION_ID.SEND_MESSAGE) def requestChatSessionHistory(self, jid): contactId = int(jid.getBareJID().getNode()) self.__msgFilters.reset(contactId) self.__chatSessions.requestHistory(jid.getBareJID()) @xmpp_query() def createUserRoom(self, name, password = ''): return self.__muc.createRoom(name, password=password) @xmpp_query() def joinToMUC(self, jid, password = ''): return self.__muc.joinToRoom(jid.getBareJID(), password=password) def leaveFromMUC(self, jid): self.__muc.leaveFromRoom(jid.getBareJID()) def sendGroupChatMessage(self, jid, body): if self.__cooldown.isInProcess(CLIENT_ACTION_ID.SEND_MESSAGE): g_messengerEvents.onErrorReceived(ChatCoolDownError(CLIENT_ACTION_ID.SEND_MESSAGE, self.__limits.getBroadcastCoolDown())) return body = self.__msgFilters.chainOut(body, self.__limits) if not body: return self.__muc.sendMessage(jid_entity.ContactBareJID(jid), body, self.__msgFilters) self.__cooldown.process(CLIENT_ACTION_ID.SEND_MESSAGE) def __clearData(self): self.__chatSessions.clear() self.__muc.clear() self.__receivedTags.clear() self.__pending = [] def __setJoined(self, isJoined): channels = self.channelsStorage.getChannelsByCriteria(find_criteria.XMPPChannelFindCriteria()) for channel in channels: channel.setJoined(isJoined) def __handleLogin(self): self.__chatSessions.release() self.__muc.release() def __handleDisconnected(self, reason, description): self.__chatSessions.suspend() self.__muc.suspend() def __handlePresence(self, jid, resource): self.__muc.handlePresence(jid, resource) def __handlePresenceError(self, jid, pyGlooxTag): self.__muc.handlePresenceError(jid, pyGlooxTag) def __handleIQ(self, iqID, iqType, pyGlooxTag): self.__chatSessions.handleIQ(iqID, iqType, pyGlooxTag) self.__muc.handleIQ(iqID, iqType, pyGlooxTag) def __handleMessage(self, _, msgType, body, jid, pyGlooxTag): if msgType not in MESSAGE_TYPE_TO_ATTR: return message = chat_ext.MessageHandler(MESSAGE_TYPE_TO_ATTR[msgType]).handleTag(pyGlooxTag) if body: message.body = self.__msgFilters.chainIn(message.accountDBID, body) if not _REQUIRED_USER_TAGS.issubset(self.__receivedTags): self.__pending.insert(0, (msgType, (jid, message))) return if self.__muc.hasChannel(jid.getBareJID()): self.__muc.addMessage(jid, message) elif msgType == MESSAGE_TYPE.CHAT or msgType == MESSAGE_TYPE.NORMAL and message.isHistory(): self.__chatSessions.addMessage(jid, message) def __me_onUsersListReceived(self, tags): self.__receivedTags.update(tags) if _REQUIRED_USER_TAGS.issubset(self.__receivedTags): while self.__pending: msgType, data = self.__pending.pop() if msgType == MESSAGE_TYPE.CHAT: self.__chatSessions.addMessage(*data) def __me_onUserActionReceived(self, actionID, contact): self.__chatSessions.setUserAction(actionID, contact) self.__muc.setUserAction(actionID, contact) def __me_onUserStatusUpdated(self, contact): self.__chatSessions.setContactPresence(contact) def __cs_onChannelsRestoredFromCache(self, stateGenerator): if not stateGenerator or not g_settings.server.XMPP.isEnabled(): return for jid, state in stateGenerator(PROTO_TYPE.XMPP): if not self.__chatSessions.restore(jid, state): self.__muc.restore(jid, state)
class MessagesManager(ClientEventsHandler): __slots__ = ('__msgFilters', '__limits', '__chatSessions', '__muc', '__receivedTags', '__pending', '__cooldown') def __init__(self): super(MessagesManager, self).__init__() self.__msgFilters = None self.__limits = MessageLimits() self.__chatSessions = ChatSessionsProvider(self.__limits) self.__muc = MUCProvider() self.__receivedTags = set() self.__pending = [] self.__cooldown = XmppCooldownManager(self.__limits.getBroadcastCoolDown()) self.channelsStorage.onRestoredFromCache += self.__cs_onChannelsRestoredFromCache return @storage_getter('channels') def channelsStorage(self): return None def clear(self): self.channelsStorage.onRestoredFromCache -= self.__cs_onChannelsRestoredFromCache self.__clearData() super(MessagesManager, self).clear() def registerHandlers(self): register = self.client().registerHandler register(_EVENT.DISCONNECTED, self.__handleDisconnected) register(_EVENT.LOGIN, self.__handleLogin) register(_EVENT.PRESENCE, self.__handlePresence) register(_EVENT.PRESENCE_ERROR, self.__handlePresenceError) register(_EVENT.IQ, self.__handleIQ) register(_EVENT.MESSAGE, self.__handleMessage) register(_EVENT.MESSAGE_ERROR, self.__handleMessageError) events = g_messengerEvents.users events.onUsersListReceived += self.__me_onUsersListReceived events.onUserActionReceived += self.__me_onUserActionReceived events.onUserStatusUpdated += self.__me_onUserStatusUpdated def unregisterHandlers(self): unregister = self.client().unregisterHandler unregister(_EVENT.DISCONNECTED, self.__handleDisconnected) unregister(_EVENT.LOGIN, self.__handleLogin) unregister(_EVENT.PRESENCE, self.__handlePresence) unregister(_EVENT.PRESENCE_ERROR, self.__handlePresenceError) unregister(_EVENT.IQ, self.__handleIQ) unregister(_EVENT.MESSAGE, self.__handleMessage) unregister(_EVENT.MESSAGE_ERROR, self.__handleMessageError) events = g_messengerEvents.users events.onUsersListReceived -= self.__me_onUsersListReceived events.onUserActionReceived -= self.__me_onUserActionReceived events.onUserStatusUpdated -= self.__me_onUserStatusUpdated def setFilters(self, msgFilters): self.__msgFilters = msgFilters def isInCooldown(self): return self.__cooldown.isInProcess(CLIENT_ACTION_ID.SEND_MESSAGE) @xmpp_query(QUERY_SIGN.DATABASE_ID, QUERY_SIGN.ACCOUNT_NAME) def startChatSession(self, dbID, name): self.__chatSessions.startSession(jid_entity.makeContactJID(dbID), name) return (True, None) def stopChatSession(self, jid): self.__chatSessions.stopSession(jid.getBareJID()) def sendChatMessage(self, jid, body): if self.__cooldown.isInProcess(CLIENT_ACTION_ID.SEND_MESSAGE): g_messengerEvents.onErrorReceived(ChatCoolDownError(CLIENT_ACTION_ID.SEND_MESSAGE, self.__limits.getBroadcastCoolDown())) return body = self.__msgFilters.chainOut(body, self.__limits) if not body: return self.__chatSessions.sendMessage(jid.getBareJID(), body, self.__msgFilters) self.__cooldown.process(CLIENT_ACTION_ID.SEND_MESSAGE) def requestChatSessionHistory(self, jid): contactId = int(jid.getBareJID().getNode()) self.__msgFilters.reset(contactId) self.__chatSessions.requestHistory(jid.getBareJID()) @xmpp_query() def createUserRoom(self, name, password=''): return self.__muc.createRoom(name, password=password) @xmpp_query() def joinToMUC(self, jid, password='', name=''): return self.__muc.joinToRoom(jid.getBareJID(), password=password, name=name, initResult=ACTION_RESULT.DO_NOTHING) def leaveFromMUC(self, jid): self.__muc.leaveFromRoom(jid.getBareJID()) def sendGroupChatMessage(self, jid, body): if self.__cooldown.isInProcess(CLIENT_ACTION_ID.SEND_MESSAGE): g_messengerEvents.onErrorReceived(ChatCoolDownError(CLIENT_ACTION_ID.SEND_MESSAGE, self.__limits.getBroadcastCoolDown())) return body = self.__msgFilters.chainOut(body, self.__limits) if not body: return self.__muc.sendMessage(jid_entity.ContactBareJID(jid), body, self.__msgFilters) self.__cooldown.process(CLIENT_ACTION_ID.SEND_MESSAGE) def _addSysChannelsToStorage(self): self._addCommonChannelToStorage() def _addCommonChannelToStorage(self): sysChannelConfig = g_settings.server.XMPP.getChannelByType(XMPP_MUC_CHANNEL_TYPE.STANDARD) if sysChannelConfig is not None and sysChannelConfig['enabled']: sysChannelEntity = entities.XmppSystemChannelEntity(mucChannelType=XMPP_MUC_CHANNEL_TYPE.STANDARD, name=sysChannelConfig['userString']) if self.channelsStorage.addChannel(sysChannelEntity): g_messengerEvents.channels.onChannelInited(sysChannelEntity) return def __clearData(self): self.__chatSessions.clear() self.__muc.clear() self.__receivedTags.clear() self.__pending = [] def __setJoined(self, isJoined): channels = self.channelsStorage.getChannelsByCriteria(find_criteria.XMPPChannelFindCriteria()) for channel in channels: channel.setJoined(isJoined) def __handleLogin(self): self.__chatSessions.release() self._addSysChannelsToStorage() def __handleDisconnected(self, reason, description): self.__chatSessions.suspend() self.__muc.suspend() def __handlePresence(self, jid, resource): self.__muc.handlePresence(jid, resource) def __handlePresenceError(self, jid, pyGlooxTag): self.__muc.handlePresenceError(jid, pyGlooxTag) def __handleIQ(self, iqID, iqType, pyGlooxTag): self.__chatSessions.handleIQ(iqID, iqType, pyGlooxTag) self.__muc.handleIQ(iqID, iqType, pyGlooxTag) def __handleMessage(self, _, msgType, body, jid, pyGlooxTag): if msgType not in MESSAGE_TYPE_TO_ATTR: return message = chat_ext.MessageHandler(MESSAGE_TYPE_TO_ATTR[msgType]).handleTag(pyGlooxTag) if body: message.body = self.__msgFilters.chainIn(message.accountDBID, body) if not _REQUIRED_USER_TAGS.issubset(self.__receivedTags): self.__pending.insert(0, (msgType, (jid, message))) return if self.__muc.hasChannel(jid.getBareJID()): self.__muc.addMessage(jid, message) elif msgType == MESSAGE_TYPE.CHAT or msgType == MESSAGE_TYPE.NORMAL and message.isHistory(): self.__chatSessions.addMessage(jid, message) def __handleMessageError(self, _, __, ___, pyGlooxTag): g_messengerEvents.onErrorReceived(createServerActionMessageError(CLIENT_ACTION_ID.SEND_MESSAGE, pyGlooxTag)) def __me_onUsersListReceived(self, tags): self.__receivedTags.update(tags) if _REQUIRED_USER_TAGS.issubset(self.__receivedTags): while self.__pending: msgType, data = self.__pending.pop() if msgType == MESSAGE_TYPE.CHAT: self.__chatSessions.addMessage(*data) def __me_onUserActionReceived(self, actionID, contact): self.__chatSessions.setUserAction(actionID, contact) self.__muc.setUserAction(actionID, contact) def __me_onUserStatusUpdated(self, contact): self.__chatSessions.setContactPresence(contact) def __cs_onChannelsRestoredFromCache(self, stateGenerator): if not stateGenerator or not g_settings.server.XMPP.isEnabled(): return for jid, state in stateGenerator(PROTO_TYPE.XMPP): if not self.__chatSessions.restore(jid, state): self.__muc.restore(jid, state)
class ChatSessionHistoryRequester(ClientHolder): def __init__(self, limits): super(ChatSessionHistoryRequester, self).__init__() self.__cooldown = XmppCooldownManager(limits.getBroadcastCoolDown()) self.__limit = limits.getHistoryMaxLength() self.__iqID = '' self.__pool = [] self.__history = [] self.__callbackID = None self.__state = _HISTORY_RQ_STATE.FREE self.__isSuspend = True return @storage_getter('channels') def channelsStorage(self): return None def release(self): if self.__isSuspend: self.__isSuspend = False self.__doNextRequest() def suspend(self): if not self.__isSuspend: self.__isSuspend = True if self.__callbackID is not None: BigWorld.cancelCallback(self.__callbackID) self.__callbackID = None self.__state = _HISTORY_RQ_STATE.FREE self.__iqID = '' self.__history = [] return def clear(self): if self.__callbackID is not None: BigWorld.cancelCallback(self.__callbackID) self.__callbackID = None self.__iqID = '' self.__pool = [] self.__state = _HISTORY_RQ_STATE.FREE return def request(self, jid): if self.__state == _HISTORY_RQ_STATE.UNAVAILABLE: self.__setChannelHistory(jid) return if jid not in self.__pool: self.__pool.append(jid) if self.__isSuspend: return if self.__state == _HISTORY_RQ_STATE.FREE: self.__doNextRequest() def cancel(self, jid): if jid in self.__pool: if jid == self.__pool[0]: if self.__callbackID is not None: BigWorld.cancelCallback(self.__callbackID) self.__callbackID = None self.__state = _HISTORY_RQ_STATE.FREE self.__pool.pop(0) self.__iqID = '' self.__history = [] self.__doNextRequest() else: self.__pool.remove(jid) return def handleIQ(self, iqID, iqType, tag): if iqID == self.__iqID: if iqType == IQ_TYPE.RESULT: self.__state = _HISTORY_RQ_STATE.RESULT elif iqType == IQ_TYPE.ERROR: self.__state = _HISTORY_RQ_STATE.UNAVAILABLE error = errors.createServerActionIQError(CLIENT_ACTION_ID.RQ_HISTORY, tag) if error: g_messengerEvents.onErrorReceived(error) while self.__pool: self.__setChannelHistory(self.__pool.pop(0)) result = True else: result = False return result def addHistory(self, message): if message.body: self.__history.append(message) if message.isFinalInHistory: self.__setChannelHistory(self.__pool.pop(0)) self.__history = [] self.__state = _HISTORY_RQ_STATE.FREE self.__doNextRequest() def __setChannelHistory(self, jid): channel = self.channelsStorage.getChannel(entities.XMPPChatChannelEntity(jid)) if channel: g_messengerEvents.channels.onHistoryReceived(sorted(self.__history, key=operator.attrgetter('sentAt')), channel) def __doNextRequest(self): if self.__cooldown.isInProcess(CLIENT_ACTION_ID.RQ_HISTORY): self.__state = _HISTORY_RQ_STATE.COOLDOWN self.__callbackID = BigWorld.callback(self.__cooldown.getTime(CLIENT_ACTION_ID.RQ_HISTORY), self.__callback) else: if not self.__pool: return self.__state = _HISTORY_RQ_STATE.WAIT self.__iqID = self.client().sendIQ(chat_ext.GetChatHistoryQuery(self.__pool[0], self.__limit)) self.__cooldown.process(CLIENT_ACTION_ID.RQ_HISTORY, 5.0) def __callback(self): self.__callbackID = None self.__state = _HISTORY_RQ_STATE.FREE self.__doNextRequest() return
class ChatSessionHistoryRequester(ClientHolder): def __init__(self, limits): super(ChatSessionHistoryRequester, self).__init__() self.__cooldown = XmppCooldownManager(limits.getBroadcastCoolDown()) self.__limit = limits.getHistoryMaxLength() self.__iqID = '' self.__pool = [] self.__history = [] self.__callbackID = None self.__state = _HISTORY_RQ_STATE.FREE self.__isSuspend = True return @storage_getter('channels') def channelsStorage(self): return None def release(self): if self.__isSuspend: self.__isSuspend = False self.__doNextRequest() def suspend(self): if not self.__isSuspend: self.__isSuspend = True if self.__callbackID is not None: BigWorld.cancelCallback(self.__callbackID) self.__callbackID = None self.__state = _HISTORY_RQ_STATE.FREE self.__iqID = '' self.__history = [] return def clear(self): if self.__callbackID is not None: BigWorld.cancelCallback(self.__callbackID) self.__callbackID = None self.__iqID = '' self.__pool = [] self.__state = _HISTORY_RQ_STATE.FREE return def request(self, jid): if self.__state == _HISTORY_RQ_STATE.UNAVAILABLE: self.__setChannelHistory(jid) return if jid not in self.__pool: self.__pool.append(jid) if self.__isSuspend: return if self.__state == _HISTORY_RQ_STATE.FREE: self.__doNextRequest() def cancel(self, jid): if jid in self.__pool: if jid == self.__pool[0]: if self.__callbackID is not None: BigWorld.cancelCallback(self.__callbackID) self.__callbackID = None self.__state = _HISTORY_RQ_STATE.FREE self.__pool.pop(0) self.__iqID = '' self.__history = [] self.__doNextRequest() else: self.__pool.remove(jid) return def handleIQ(self, iqID, iqType, tag): if iqID == self.__iqID: if iqType == IQ_TYPE.RESULT: self.__state = _HISTORY_RQ_STATE.RESULT elif iqType == IQ_TYPE.ERROR: self.__state = _HISTORY_RQ_STATE.UNAVAILABLE error = errors.createServerActionIQError(CLIENT_ACTION_ID.RQ_HISTORY, tag) if error: g_messengerEvents.onErrorReceived(error) while self.__pool: self.__setChannelHistory(self.__pool.pop(0)) result = True else: result = False return result def addHistory(self, message): if not self.__state == _HISTORY_RQ_STATE.RESULT: raise AssertionError if message.body: self.__history.append(message) message.isFinalInHistory and self.__setChannelHistory(self.__pool.pop(0)) self.__history = [] self.__state = _HISTORY_RQ_STATE.FREE self.__doNextRequest() def __setChannelHistory(self, jid): channel = self.channelsStorage.getChannel(entities.XMPPChatChannelEntity(jid)) if channel: g_messengerEvents.channels.onHistoryReceived(sorted(self.__history, key=operator.attrgetter('sentAt')), channel) def __doNextRequest(self): if self.__cooldown.isInProcess(CLIENT_ACTION_ID.RQ_HISTORY): self.__state = _HISTORY_RQ_STATE.COOLDOWN self.__callbackID = BigWorld.callback(self.__cooldown.getTime(CLIENT_ACTION_ID.RQ_HISTORY), self.__callback) else: if not self.__pool: return self.__state = _HISTORY_RQ_STATE.WAIT self.__iqID = self.client().sendIQ(chat_ext.GetChatHistoryQuery(self.__pool[0], self.__limit)) self.__cooldown.process(CLIENT_ACTION_ID.RQ_HISTORY, 5.0) def __callback(self): self.__callbackID = None self.__state = _HISTORY_RQ_STATE.FREE self.__doNextRequest() return
class MessagesManager(ClientEventsHandler): __slots__ = ('__msgFilters', '__limits', '__chatSessions', '__receivedTags', '__pending') def __init__(self): super(MessagesManager, self).__init__() self.__msgFilters = None self.__limits = _MessageLimits() self.__chatSessions = _ChatSessions(self.__limits) self.__isInited = False self.__receivedTags = set() self.__pending = [] self.__cooldown = XmppCooldownManager(self.__limits.getBroadcastCoolDown()) self.channelsStorage.onRestoredFromCache += self.__cs_onChannelsRestoredFromCache @storage_getter('channels') def channelsStorage(self): return None def clear(self): self.channelsStorage.onRestoredFromCache -= self.__cs_onChannelsRestoredFromCache self.__clearData() super(MessagesManager, self).clear() def registerHandlers(self): register = self.client().registerHandler register(_EVENT.DISCONNECTED, self.__handleDisconnected) register(_EVENT.IQ, self.__handleIQ) register(_EVENT.MESSAGE, self.__handleMessage) events = g_messengerEvents.users events.onUsersListReceived += self.__me_onUsersListReceived events.onUserActionReceived += self.__me_onUserActionReceived events.onUserStatusUpdated += self.__me_onUserStatusUpdated def unregisterHandlers(self): unregister = self.client().unregisterHandler unregister(_EVENT.DISCONNECTED, self.__handleDisconnected) unregister(_EVENT.IQ, self.__handleIQ) unregister(_EVENT.MESSAGE, self.__handleMessage) events = g_messengerEvents.users events.onUsersListReceived -= self.__me_onUsersListReceived events.onUserActionReceived -= self.__me_onUserActionReceived events.onUserStatusUpdated -= self.__me_onUserStatusUpdated def setFilters(self, msgFilters): self.__msgFilters = msgFilters def isInCooldown(self): return self.__cooldown.isInProcess(CLIENT_ACTION_ID.SEND_MESSAGE) @xmpp_query(QUERY_SIGN.DATABASE_ID, QUERY_SIGN.ACCOUNT_NAME) def startChatSession(self, dbID, name): self.__chatSessions.startSession(makeContactJID(dbID), name) return (True, None) def stopChatSession(self, jid): self.__chatSessions.stopSession(ContactBareJID(jid)) def sendChatMessage(self, jid, body): if self.__cooldown.isInProcess(CLIENT_ACTION_ID.SEND_MESSAGE): g_messengerEvents.onErrorReceived(ChatCoolDownError(CLIENT_ACTION_ID.SEND_MESSAGE, self.__limits.getBroadcastCoolDown())) return body = self.__msgFilters.chainOut(body, self.__limits) if not body: return self.__chatSessions.sendMessage(ContactBareJID(jid), body, self.__msgFilters) self.__cooldown.process(CLIENT_ACTION_ID.SEND_MESSAGE) def requestChatHistory(self, jid): self.__chatSessions.requestHistory(ContactBareJID(jid)) def __clearData(self): self.__chatSessions.clear() self.__receivedTags.clear() self.__pending = [] def __handleDisconnected(self, reason, description): self.__clearData() channels = self.channelsStorage.getChannelsByCriteria(find_criteria.XMPPChannelFindCriteria()) for channel in channels: channel.setJoined(False) def __handleIQ(self, iqID, iqType, pyGlooxTag): self.__chatSessions.handleIQ(iqID, iqType, pyGlooxTag) def __handleMessage(self, _, msgType, body, jid, pyGlooxTag): if msgType not in MESSAGE_TYPE_TO_ATTR: return message = MessageHandler(MESSAGE_TYPE_TO_ATTR[msgType]).handleTag(pyGlooxTag) if not message.accountDBID: g_logOutput.error(CLIENT_LOG_AREA.MESSAGE, 'Can not find sender info', pyGlooxTag.getXml()) return if body: message.body = self.__msgFilters.chainIn(message.accountDBID, body) if not _REQUIRED_USER_TAGS.issubset(self.__receivedTags): self.__pending.insert(0, (msgType, (jid, message))) return if msgType == MESSAGE_TYPE.CHAT or msgType == MESSAGE_TYPE.NORMAL and message.isHistory(): self.__chatSessions.addMessage(jid, message) def __me_onUsersListReceived(self, tags): self.__receivedTags.update(tags) if _REQUIRED_USER_TAGS.issubset(self.__receivedTags): while self.__pending: msgType, data = self.__pending.pop() if msgType == MESSAGE_TYPE.CHAT: self.__chatSessions.addMessage(*data) def __me_onUserActionReceived(self, actionID, user): if actionID == USER_ACTION_ID.IGNORED_ADDED: self.__chatSessions.stopSession(makeContactJID(user.getID())) def __me_onUserStatusUpdated(self, contact): self.__chatSessions.setContactPresence(contact) def __cs_onChannelsRestoredFromCache(self, stateGenerator): if not stateGenerator or not g_settings.server.XMPP.isEnabled(): return for jid, state in stateGenerator(PROTO_TYPE.XMPP): self.__chatSessions.restoreSession(jid, state)
class SearchUsersProcessor(SearchProcessor, ClientEventsHandler): def __init__(self): super(SearchUsersProcessor, self).__init__() self.__sentRequestID = None self.__limits = limits.FindUserSearchLimits() self.__cooldown = XmppCooldownManager( self.__limits.getRequestCooldown()) self.proto.client.registerHandler(GLOOX_EVENT.IQ, self.__onIQReceived) return def __del__(self): super(SearchUsersProcessor, self).__init__() self.proto.client.unregisterHandler(GLOOX_EVENT.IQ, self.__onIQReceived) @proto_getter(PROTO_TYPE.XMPP) def proto(self): return None @storage_getter('users') def usersStorage(self): return None def find(self, token, **kwargs): error = self.__checkCooldown(CLIENT_ACTION_ID.FIND_USERS_BY_PREFIX) if error: self._onSearchFailed(error.getMessage()) return token = token.strip() isCorrect, reason = checkAccountName(token) if not isCorrect: self._onSearchFailed(reason) return client = self.proto.client if client.isConnected(): query = NicknamePrefixSearchQuery( token, limit=self.getSearchResultLimit()) self._lastRequestID = client.sendIQ(query) self.__cooldown.process(CLIENT_ACTION_ID.FIND_USERS_BY_PREFIX) else: error = ClientActionError(CLIENT_ACTION_ID.FIND_USERS_BY_PREFIX, CLIENT_ERROR_ID.NOT_CONNECTED) self._onSearchFailed(error.getMessage()) def getSearchCoolDown(self): return self.__limits.getRequestCooldown() def getSearchResultLimit(self): return self.__limits.getMaxResultSize() def unregisterHandlers(self): raise SoftException( 'This method should not be reached in this context') def registerHandlers(self): raise SoftException( 'This method should not be reached in this context') def __onIQReceived(self, iqID, iqType, pyGlooxTag): if self._lastRequestID != iqID: return if iqType == IQ_TYPE.ERROR: error = createServerIQError(pyGlooxTag) if error: reason = error.getMessage() else: reason = '' LOG_WARNING('Search error is not resolved on the client', pyGlooxTag.getXml()) self._onSearchFailed(reason) else: users = self.__OnSuccesResponse(pyGlooxTag) self._onSearchTokenComplete(iqID, users) def __OnSuccesResponse(self, pyGlooxTag): result = NicknamePrefixSearchHandler().handleTag(pyGlooxTag) users = [] for userInfo in result: user = self.usersStorage.getUser(userInfo.dbId) if user: userInfo = user else: userInfo = XMPPUserEntity(userInfo.dbId, name=userInfo.nickname, clanInfo=userInfo.clanInfo) userInfo.addTags((USER_TAG.SEARCH, USER_TAG.TEMP)) self.usersStorage.addUser(userInfo) users.append(userInfo) return users def __checkCooldown(self, actionID): error = None if self.__cooldown.isInProcess(actionID): error = ChatCoolDownError(actionID, self.__cooldown.getDefaultCoolDown()) return error
class MessagesManager(ClientEventsHandler): __slots__ = ('__msgFilters', '__limits', '__chatSessions', '__receivedTags', '__pending') def __init__(self): super(MessagesManager, self).__init__() self.__msgFilters = None self.__limits = _MessageLimits() self.__chatSessions = _ChatSessions(self.__limits) self.__isInited = False self.__receivedTags = set() self.__pending = [] self.__cooldown = XmppCooldownManager( self.__limits.getBroadcastCoolDown()) self.channelsStorage.onRestoredFromCache += self.__cs_onChannelsRestoredFromCache @storage_getter('channels') def channelsStorage(self): return None def clear(self): self.channelsStorage.onRestoredFromCache -= self.__cs_onChannelsRestoredFromCache self.__clearData() super(MessagesManager, self).clear() def registerHandlers(self): register = self.client().registerHandler register(_EVENT.DISCONNECTED, self.__handleDisconnected) register(_EVENT.IQ, self.__handleIQ) register(_EVENT.MESSAGE, self.__handleMessage) events = g_messengerEvents.users events.onUsersListReceived += self.__me_onUsersListReceived events.onUserActionReceived += self.__me_onUserActionReceived events.onUserStatusUpdated += self.__me_onUserStatusUpdated def unregisterHandlers(self): unregister = self.client().unregisterHandler unregister(_EVENT.DISCONNECTED, self.__handleDisconnected) unregister(_EVENT.IQ, self.__handleIQ) unregister(_EVENT.MESSAGE, self.__handleMessage) events = g_messengerEvents.users events.onUsersListReceived -= self.__me_onUsersListReceived events.onUserActionReceived -= self.__me_onUserActionReceived events.onUserStatusUpdated -= self.__me_onUserStatusUpdated def setFilters(self, msgFilters): self.__msgFilters = msgFilters def isInCooldown(self): return self.__cooldown.isInProcess(CLIENT_ACTION_ID.SEND_MESSAGE) @xmpp_query(QUERY_SIGN.DATABASE_ID, QUERY_SIGN.ACCOUNT_NAME) def startChatSession(self, dbID, name): self.__chatSessions.startSession(makeContactJID(dbID), name) return (True, None) def stopChatSession(self, jid): self.__chatSessions.stopSession(ContactBareJID(jid)) def sendChatMessage(self, jid, body): if self.__cooldown.isInProcess(CLIENT_ACTION_ID.SEND_MESSAGE): g_messengerEvents.onErrorReceived( ChatCoolDownError(CLIENT_ACTION_ID.SEND_MESSAGE, self.__limits.getBroadcastCoolDown())) return body = self.__msgFilters.chainOut(body, self.__limits) if not body: return self.__chatSessions.sendMessage(ContactBareJID(jid), body, self.__msgFilters) self.__cooldown.process(CLIENT_ACTION_ID.SEND_MESSAGE) def requestChatHistory(self, jid): self.__chatSessions.requestHistory(ContactBareJID(jid)) def __clearData(self): self.__chatSessions.clear() self.__receivedTags.clear() self.__pending = [] def __handleDisconnected(self, reason, description): self.__clearData() channels = self.channelsStorage.getChannelsByCriteria( find_criteria.XMPPChannelFindCriteria()) for channel in channels: channel.setJoined(False) def __handleIQ(self, iqID, iqType, pyGlooxTag): self.__chatSessions.handleIQ(iqID, iqType, pyGlooxTag) def __handleMessage(self, _, msgType, body, jid, pyGlooxTag): if msgType not in MESSAGE_TYPE_TO_ATTR: return message = MessageHandler( MESSAGE_TYPE_TO_ATTR[msgType]).handleTag(pyGlooxTag) if not message.accountDBID: g_logOutput.error(CLIENT_LOG_AREA.MESSAGE, 'Can not find sender info', pyGlooxTag.getXml()) return if body: message.body = self.__msgFilters.chainIn(message.accountDBID, body) if not _REQUIRED_USER_TAGS.issubset(self.__receivedTags): self.__pending.insert(0, (msgType, (jid, message))) return if msgType == MESSAGE_TYPE.CHAT or msgType == MESSAGE_TYPE.NORMAL and message.isHistory( ): self.__chatSessions.addMessage(jid, message) def __me_onUsersListReceived(self, tags): self.__receivedTags.update(tags) if _REQUIRED_USER_TAGS.issubset(self.__receivedTags): while self.__pending: msgType, data = self.__pending.pop() if msgType == MESSAGE_TYPE.CHAT: self.__chatSessions.addMessage(*data) def __me_onUserActionReceived(self, actionID, user): if actionID == USER_ACTION_ID.IGNORED_ADDED: self.__chatSessions.stopSession(makeContactJID(user.getID())) def __me_onUserStatusUpdated(self, contact): self.__chatSessions.setContactPresence(contact) def __cs_onChannelsRestoredFromCache(self, stateGenerator): if not stateGenerator or not g_settings.server.XMPP.isEnabled(): return for jid, state in stateGenerator(PROTO_TYPE.XMPP): self.__chatSessions.restoreSession(jid, state)