Ejemplo n.º 1
0
class MessageDispatcher(object):
    def __init__(self, client, plugins):
        self._client = client
        self._pool = WorkerPool(self.dispatch_msg, settings.WORKERS_NUM)
        self._plugins = plugins
        self._channel_info = {}
        self.event = None

    def start(self):
        self._pool.start()

    @staticmethod
    def get_message(msg):
        return msg.get('data', {}).get('post', {}).get('message', '').strip()

    def ignore(self, _msg):
        msg = self.get_message(_msg)
        for prefix in settings.IGNORE_NOTIFIES:
            if msg.startswith(prefix):
                return True

    def is_mentioned(self, msg):
        mentions = msg.get('data', {}).get('mentions', [])
        return self._client.user['id'] in mentions

    def is_personal(self, msg):
        channel_id = msg['data']['post']['channel_id']
        if channel_id in self._channel_info:
            channel_type = self._channel_info[channel_id]
        else:
            channel = self._client.api.channel(channel_id)
            channel_type = channel['channel']['type']
            self._channel_info[channel_id] = channel_type
        return channel_type == 'D'

    def dispatch_msg(self, msg):
        category = msg[0]
        msg = msg[1]
        text = self.get_message(msg)
        responded = False
        msg['message_type'] = '?'
        if self.is_personal(msg):
            msg['message_type'] = 'D'

        for func, args in self._plugins.get_plugins(category, text):
            if func:
                responded = True
                try:
                    func(Message(self._client, msg, self._pool), *args)
                except Exception as err:
                    logger.exception(err)
                    reply = '[%s] I have problem when handling "%s"\n' % (
                        func.__name__, text)
                    reply += '```\n%s\n```' % traceback.format_exc()
                    self._client.channel_msg(
                        msg['data']['post']['channel_id'], reply)

        if not responded and category == 'respond_to':
            if settings.DEFAULT_REPLY_MODULE is not None:
                mod = importlib.import_module(settings.DEFAULT_REPLY_MODULE)
                if hasattr(mod, 'default_reply'):
                    return getattr(mod, 'default_reply')(self, msg)
            self._default_reply(msg)

    def _on_new_message(self, msg):
        if self.ignore(msg) is True:
            return

        msg = self.filter_text(msg)
        if self.is_mentioned(msg) or self.is_personal(msg):
            self._pool.add_task(('respond_to', msg))
        else:
            self._pool.add_task(('listen_to', msg))

    def filter_text(self, msg):
        text = self.get_message(msg)
        if self.is_mentioned(msg):
            m = MESSAGE_MATCHER.match(text)
            if m:
                msg['data']['post']['message'] = m.group(2).strip()
        return msg

    def load_json(self):
        if self.event.get('data', {}).get('post'):
            self.event['data']['post'] = json.loads(
                self.event['data']['post'])
        if self.event.get('data', {}).get('mentions'):
            self.event['data']['mentions'] = json.loads(
                self.event['data']['mentions'])

    def loop(self):
        for self.event in self._client.messages(True, 'posted'):
            self.load_json()
            self._on_new_message(self.event)

    def _default_reply(self, msg):
        if settings.DEFAULT_REPLY:
            return self._client.channel_msg(
                msg['data']['post']['channel_id'], settings.DEFAULT_REPLY)

        default_reply = [
            u'Bad command "%s", You can ask me one of the '
            u'following questions:\n' % self.get_message(msg),
        ]
        docs_fmt = u'{1}' if settings.PLUGINS_ONLY_DOC_STRING else u'`{0}` {1}'

        default_reply += [
            docs_fmt.format(p.pattern, v.__doc__ or "")
            for p, v in iteritems(self._plugins.commands['respond_to'])]

        self._client.channel_msg(
            msg['data']['post']['channel_id'], '\n'.join(default_reply))
Ejemplo n.º 2
0
class MessageDispatcher(object):
    def __init__(self, client, plugins):
        self._client = client
        self._pool = WorkerPool(self.dispatch_msg)
        self._plugins = plugins
        self._channel_info = {}

    def start(self):
        self._pool.start()

    @staticmethod
    def get_message(msg):
        return msg.get(
            'props', {}).get('post', {}).get('message', '').strip()

    def is_mentioned(self, msg):
        mentions = msg.get('props', {}).get('mentions', [])
        return self._client.user['id'] in mentions

    def is_personal(self, msg):
        channel_id = msg['channel_id']
        if channel_id in self._channel_info:
            channel_type = self._channel_info[channel_id]
        else:
            channel = self._client.api.channel(channel_id)
            channel_type = channel['channel']['type']
            self._channel_info[channel_id] = channel_type
        return channel_type == 'D'

    def dispatch_msg(self, msg):
        category = msg[0]
        msg = msg[1]
        text = self.get_message(msg)
        responded = False
        msg['message_type'] = '?'
        if self.is_personal(msg):
            msg['message_type'] = 'D'
        for func, args in self._plugins.get_plugins(category, text):
            if func:
                responded = True
                try:
                    func(Message(self._client, msg), *args)
                except Exception as err:
                    logger.exception(err)
                    reply = '[%s] I have problem when handling "%s"\n' % (
                        func.__name__, text)
                    reply += '```\n%s\n```' % traceback.format_exc()
                    self._client.channel_msg(msg['channel_id'], reply)

        if not responded and category == 'respond_to':
            self._default_reply(msg)

    def _on_new_message(self, msg):
        msg = self.filter_text(msg)
        if self.is_mentioned(msg) or self.is_personal(msg):
            self._pool.add_task(('respond_to', msg))
        else:
            self._pool.add_task(('listen_to', msg))

    def filter_text(self, msg):
        text = self.get_message(msg)
        if self.is_mentioned(msg):
            m = MESSAGE_MATCHER.match(text)
            if m:
                msg['props']['post']['message'] = m.group(2).strip()
        return msg

    def loop(self):
        for self.event in self._client.messages(True, 'posted'):
            self._on_new_message(self.event)

    def _default_reply(self, msg):
        default_reply = [
            u'Bad command "%s", You can ask me one of the '
            u'following questions:\n' % self.get_message(msg),
        ]
        docs_fmt = u'{1}' if settings.PLUGINS_ONLY_DOC_STRING else u'`{0}` {1}'

        default_reply += [
            docs_fmt.format(p.pattern, v.__doc__ or "")
            for p, v in iteritems(self._plugins.commands['respond_to'])]

        self._client.channel_msg(msg['channel_id'], '\n'.join(default_reply))
Ejemplo n.º 3
0
class MessageDispatcher(object):
    def __init__(self, client, plugins):
        self._client = client
        self._pool = WorkerPool(self.dispatch_msg, settings.WORKERS_NUM)
        self._plugins = plugins
        self._channel_info = {}
        self.event = None

    def start(self):
        self._pool.start()

    @staticmethod
    def get_message(msg):
        return msg.get('data', {}).get('post', {}).get('message', '').strip()

    def ignore(self, _msg):
        msg = self.get_message(_msg)
        if any(item in msg for item in settings.IGNORE_NOTIFIES):
            return True

    def is_mentioned(self, msg):
        mentions = msg.get('data', {}).get('mentions', [])
        return self._client.user['id'] in mentions

    def is_personal(self, msg):
        try:
            channel_id = msg['data']['post']['channel_id']
            if channel_id in self._channel_info:
                channel_type = self._channel_info[channel_id]
            else:
                channel = self._client.api.channel(channel_id)
                channel_type = channel['channel']['type']
                self._channel_info[channel_id] = channel_type
            return channel_type == 'D'
        except KeyError as err:
            logger.info('Once time workpool exception caused by \
                         bot [added to/leave] [team/channel].')
            return False

    def dispatch_msg(self, msg):
        category = msg[0]
        msg = msg[1]
        text = self.get_message(msg)
        responded = False
        msg['message_type'] = '?'
        if self.is_personal(msg):
            msg['message_type'] = 'D'

        for func, args in self._plugins.get_plugins(category, text):
            if func:
                responded = True
                try:
                    func(Message(self._client, msg, self._pool), *args)
                except Exception as err:
                    logger.exception(err)
                    reply = '[%s] I have problem when handling "%s"\n' % (
                        func.__name__, text)
                    reply += '```\n%s\n```' % traceback.format_exc()
                    self._client.channel_msg(
                        msg['data']['post']['channel_id'], reply)

        if not responded and category == 'respond_to':
            if settings.DEFAULT_REPLY_MODULE is not None:
                mod = importlib.import_module(settings.DEFAULT_REPLY_MODULE)
                if hasattr(mod, 'default_reply'):
                    return getattr(mod, 'default_reply')(self, msg)
            self._default_reply(msg)

    def _on_new_message(self, msg):
        if self.ignore(msg) is True:
            return

        msg = self.filter_text(msg)
        if self.is_mentioned(msg) or self.is_personal(msg):
            self._pool.add_task(('respond_to', msg))
        else:
            self._pool.add_task(('listen_to', msg))

    def filter_text(self, msg):
        text = self.get_message(msg)
        if self.is_mentioned(msg):
            m = MESSAGE_MATCHER.match(text)
            if m:
                msg['data']['post']['message'] = m.group(2).strip()
        return msg

    def load_json(self):
        if self.event.get('data', {}).get('post'):
            self.event['data']['post'] = json.loads(
                self.event['data']['post'])
        if self.event.get('data', {}).get('mentions'):
            self.event['data']['mentions'] = json.loads(
                self.event['data']['mentions'])

    def loop(self):
        for self.event in self._client.messages(True,
                                        ['posted', 'added_to_team', 'leave_team', \
                                         'user_added', 'user_removed']):
            if self.event:
                self.load_json()
                self._on_new_message(self.event)

    def _default_reply(self, msg):
        if settings.DEFAULT_REPLY:
            return self._client.channel_msg(
                msg['data']['post']['channel_id'], settings.DEFAULT_REPLY)

        default_reply = [
            u'Bad command "%s", Here is what I currently know '
            u'how to do:\n' % self.get_message(msg),
        ]

        # create dictionary organizing commands by plugin
        modules = {}
        for p, v in iteritems(self._plugins.commands['respond_to']):
            key = v.__module__.title().split('.')[1]
            if not key in modules:
                modules[key] = []
            modules[key].append(v.__doc__)

        docs_fmt = u'\t{0}' if settings.PLUGINS_ONLY_DOC_STRING else u'\t - {0}'

        for module, commands in modules.items():
            default_reply += [u'**Available Commands**']
            # commands.sort(key=lambda x: x)
            for description in commands:
                if description:
                    default_reply += [docs_fmt.format(description)]

        self._client.channel_msg(
            msg['data']['post']['channel_id'], '\n'.join(default_reply))