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
示例#2
0
 def __init__(self, limits):
     super(_ChatHistoryRequester, 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
 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
 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
示例#5
0
 def __init__(self, limits):
     super(_ChatHistoryRequester, 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
示例#6
0
 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
示例#7
0
文件: messages.py 项目: webiumsk/WoT
 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
示例#8
0
文件: __init__.py 项目: webiumsk/WoT
 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
示例#9
0
 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
示例#10
0
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)
示例#11
0
文件: messages.py 项目: webiumsk/WoT
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 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 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
示例#14
0
文件: __init__.py 项目: webiumsk/WoT
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})
示例#15
0
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)
示例#16
0
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 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 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 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)
示例#20
0
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)