Exemple #1
0
    def callback_message(self, conn, mess):
        message = mess.getBody()
        if not message:
            return

        username = get_jid_from_message(mess)
        self.shelf[username] = datetime.now()
        self.shelf.sync()
Exemple #2
0
    def callback_message(self, conn, mess):
        message = mess.getBody()
        if not message:
            return

        username = get_jid_from_message(mess)
        self.shelf[username] = datetime.now()
        self.shelf.sync()
Exemple #3
0
 def seen(self, mess, args):
     """ find out when someone last said something """
     username = get_jid_from_message(mess)
     if username == args:
         return 'I can see you now'
     try:
         last_seen = self.shelf[str(args)]
         return 'I last saw %s %s ago (on %s)' % (
             args, format_timedelta(datetime.now() - last_seen),
             datetime.strftime(last_seen, '%A, %b %d at %H:%M'))
     except KeyError:
         return 'I have no record of %s' % args
Exemple #4
0
 def callback_message(self, conn, mess):
     # Ignore messages from myself
     if self.jid.bareMatch(get_jid_from_message(mess)):
         logging.debug('Ignore a message from myself')
         return
     super(ErrBot, self).callback_message(conn, mess)
     for bot in get_all_active_plugin_objects():
         if hasattr(bot, 'callback_message'):
             try:
                 bot.callback_message(conn, mess)
             except:
                 logging.exception("Probably a type error")
Exemple #5
0
 def seen(self, mess, args):
     """ find out when someone last said something """
     username = get_jid_from_message(mess)
     if username == args:
         return "I can see you now"
     try:
         last_seen = self.shelf[str(args)]
         return "I last saw %s %s ago (on %s)" % (
             args,
             format_timedelta(datetime.now() - last_seen),
             datetime.strftime(last_seen, "%A, %b %d at %H:%M"),
         )
     except KeyError:
         return "I have no record of %s" % args
Exemple #6
0
 def callback_message(self, conn, mess):
     #if mess.getBody():
     #    logging.debug(u'Received message %s' % mess.getBody())
     if mess.getType() in ('groupchat', 'chat'):
         try:
             username = get_jid_from_message(mess)
             if username in CHATROOM_RELAY:
                 logging.debug('Message to relay from %s.' % username)
                 body = mess.getBody()
                 rooms = CHATROOM_RELAY[username]
                 for room in rooms:
                     self.send(room, body, message_type='groupchat')
         except Exception, e:
             logging.exception('crashed in callback_message %s' % e)
Exemple #7
0
    def callback_message(self, conn, mess):
        """Messages sent to the bot will arrive here.
        Command handling + routing is done in this function.
        return False if the message should be ignored """
        self.__lastping = time.time()
        if is_from_history(mess):
            self.log.debug("Message from history, ignore it")
            return False

        # Ignore messages from myself
        if self.jid.bareMatch(get_jid_from_message(mess)):
            logging.debug('Ignore a message from myself')
            return False

        return super(JabberBot, self).callback_message(conn, mess)
Exemple #8
0
    def callback_message(self, conn, mess):
        """Messages sent to the bot will arrive here.
        Command handling + routing is done in this function.
        return False if the message should be ignored """
        self.__lastping = time.time()
        if is_from_history(mess):
            self.log.debug("Message from history, ignore it")
            return False

        # Ignore messages from myself
        if self.jid.bareMatch(get_jid_from_message(mess)):
            logging.debug('Ignore a message from myself')
            return False

        return super(JabberBot, self).callback_message(conn, mess)
Exemple #9
0
 def build_reply(self, mess, text=None, private=False):
     """Build a message for responding to another message.
     Message is NOT sent"""
     response = self.build_message(text)
     if private:
         # Use get_jid_from_message here instead of mess.getFrom because
         # getFrom will return the groupchat id instead of user's jid when
         # sent from a chatroom
         response.setTo(get_jid_from_message(mess))
         response.setType('chat')
         response.setFrom(self.jid)
     else:
         response.setTo(mess.getFrom().getStripped())
         response.setType(mess.getType())
         response.setFrom(self.jid)
     return response
Exemple #10
0
    def callback_message(self, conn, mess):
        """Messages sent to the bot will arrive here.
        Command handling + routing is done in this function.
        return False if the message should be ignored """
        self.__lastping = time.time()
        if is_from_history(mess):
            self.log.debug("Message from history, ignore it")
            return False

        # Ignore messages from ourselves. Because it isn't always possible to get the
        # real JID from a MUC participant (including ourself), matching the JID against
        # ourselves isn't enough (see https://github.com/gbin/err/issues/90 for
        # background discussion on this). Matching against CHATROOM_FN isn't technically
        # correct in all cases because a MUC could give us another nickname, but it
        # covers 99% of the MUC cases, so it should suffice for the time being.
        if self.jid.bareMatch(get_jid_from_message(mess)) or mess.getType() == "groupchat" and mess.getFrom().getResource() == CHATROOM_FN:
            logging.debug('Ignore a message from myself')
            return False

        return super(JabberBot, self).callback_message(conn, mess)
Exemple #11
0
    def poll_vote(self, mess, args):
        """Vote for the currently running poll."""
        if not PollBot.active_poll:
            return u'No active poll. Use !poll start to start a poll.'

        index = args.strip()

        if not index:
            return u'usage: !poll vote <option_number>'

        if not index.isdigit():
            return u'Please vote using the numerical index of the option.'

        poll = self.shelf[PollBot.active_poll]
        options = poll[0]

        index = int(index)
        if index > len(options) or index < 1:
            return u'Please choose a number between 1 and %d (inclusive).' % len(
                options)

        option = options.keys()[index - 1]

        if not option in options:
            return u'Option not found. Use !poll show to see all options of the current poll.'

        usernames = poll[1]
        username = get_jid_from_message(mess)

        if username in usernames:
            return u'You have already voted.'

        usernames.append(username)

        options[option] += 1
        self.shelf[PollBot.active_poll] = poll

        return self.format_poll(PollBot.active_poll)
Exemple #12
0
 def callback_message(self, conn, mess):
     if bot.mode != 'campfire': # no relay support in campfire
         try:
             mess_type = mess.getType()
             if mess_type == 'chat':
                 username = get_jid_from_message(mess)
                 if username in CHATROOM_RELAY:
                     logging.debug('Message to relay from %s.' % username)
                     body = mess.getBody()
                     rooms = CHATROOM_RELAY[username]
                     for room in rooms:
                         self.send(room, body, message_type='groupchat')
             elif mess_type == 'groupchat':
                 fr = mess.getFrom()
                 chat_room = fr.node + '@' + fr.domain if fr.domain else fr.node # some backends has no domain notion
                 if chat_room in REVERSE_CHATROOM_RELAY:
                     users_to_relay_to = REVERSE_CHATROOM_RELAY[chat_room]
                     logging.debug('Message to relay to %s.' % users_to_relay_to)
                     body = '[%s] %s' % (fr.resource, mess.getBody())
                     for user in users_to_relay_to:
                         self.send(user, body, message_type='chat')
         except Exception as e:
             logging.exception('crashed in callback_message %s' % e)
Exemple #13
0
    def poll_vote(self, mess, args):
        """Vote for the currently running poll."""
        if not PollBot.active_poll:
            return u'No active poll. Use !poll start to start a poll.'

        index = args.strip()

        if not index:
            return u'usage: !poll vote <option_number>'

        if not index.isdigit():
            return u'Please vote using the numerical index of the option.'

        poll = self.shelf[PollBot.active_poll]
        options = poll[0]

        index = int(index)
        if index > len(options) or index < 1:
            return u'Please choose a number between 1 and %d (inclusive).' % len(options)

        option = options.keys()[index - 1]

        if not option in options:
            return u'Option not found. Use !poll show to see all options of the current poll.'

        usernames = poll[1]
        username = get_jid_from_message(mess)

        if username in usernames:
            return u'You have already voted.'

        usernames.append(username)

        options[option] += 1
        self.shelf[PollBot.active_poll] = poll

        return self.format_poll(PollBot.active_poll)
Exemple #14
0
 def callback_message(self, conn, mess):
     if bot.mode != 'campfire':  # no relay support in campfire
         try:
             mess_type = mess.getType()
             if mess_type == 'chat':
                 username = get_jid_from_message(mess)
                 if username in CHATROOM_RELAY:
                     logging.debug('Message to relay from %s.' % username)
                     body = mess.getBody()
                     rooms = CHATROOM_RELAY[username]
                     for room in rooms:
                         self.send(room, body, message_type='groupchat')
             elif mess_type == 'groupchat':
                 fr = mess.getFrom()
                 chat_room = fr.node + '@' + fr.domain if fr.domain else fr.node  # some backends has no domain notion
                 if chat_room in REVERSE_CHATROOM_RELAY:
                     users_to_relay_to = REVERSE_CHATROOM_RELAY[chat_room]
                     logging.debug('Message to relay to %s.' %
                                   users_to_relay_to)
                     body = '[%s] %s' % (fr.resource, mess.getBody())
                     for user in users_to_relay_to:
                         self.send(user, body, message_type='chat')
         except Exception as e:
             logging.exception('crashed in callback_message %s' % e)
Exemple #15
0
    def callback_message(self, conn, mess):
        """Messages sent to the bot will arrive here.
        Command handling + routing is done in this function."""
        self.__lastping = time.time()

        # Prepare to handle either private chats or group chats
        type = mess.getType()
        jid = mess.getFrom()
        props = mess.getProperties()
        text = mess.getBody()
        username = get_sender_username(mess)

        if type not in ("groupchat", "chat"):
            self.log.debug("unhandled message type %s" % mess)

            return
        if 'urn:xmpp:delay' in props:
            self.log.debug("Message from history, ignore it")
            return
            # Ignore messages from before we joined
        if xmpp.NS_DELAY in props: return

        # Ignore messages from myself
        if self.jid.bareMatch(jid): return

        self.log.debug("*** props = %s" % props)
        self.log.debug("*** jid = %s" % jid)
        self.log.debug("*** username = %s" % username)
        self.log.debug("*** type = %s" % type)
        self.log.debug("*** text = %s" % text)

        # If a message format is not supported (eg. encrypted),
        # txt will be None
        if not text: return

        # Remember the last-talked-in message thread for replies
        # FIXME i am not threadsafe
        self.__threads[jid] = mess.getThread()

        if not text.startswith('!'):
            return

        text = text[1:]
        text_split = text.strip().split(' ')

        cmd = None
        command = None
        args = ''
        if len(text_split) > 1:
            command = (text_split[0] + '_' + text_split[1]).lower()
            if self.commands.has_key(command):
                cmd = command
                args = ' '.join(text_split[2:])

        if not cmd:
            command = text_split[0].lower()
            if self.commands.has_key(command):
                cmd = command
                if len(text_split) > 1:
                    args = ' '.join(text_split[1:])

        if command == '!': # we did "!!" so recall the last command
            if len(self.cmd_history):
                cmd, args = self.cmd_history[-1]
            else:
                return # no command in history
        elif command.isdigit(): # we did "!#" so we recall the specified command
            index = int(command)
            if len(self.cmd_history) >= index:
                cmd, args = self.cmd_history[-index]
            else:
                return # no command in history

        if (cmd, args) in self.cmd_history:
            self.cmd_history.remove((cmd, args)) # we readd it below

        self.log.info("received command = %s matching [%s] with parameters [%s]" % (command, cmd, args))

        if cmd:
            def execute_and_send():
                try:
                    reply = self.commands[cmd](mess, args)
                except Exception, e:
                    self.log.exception('An error happened while processing '\
                                       'a message ("%s") from %s: %s"' %
                                       (text, jid, traceback.format_exc(e)))
                    reply = self.MSG_ERROR_OCCURRED + ':\n %s' % e
                if reply:
                    if len(reply) > self.MESSAGE_SIZE_LIMIT:
                        reply = reply[:self.MESSAGE_SIZE_LIMIT - len(self.MESSAGE_SIZE_ERROR_MESSAGE)] + self.MESSAGE_SIZE_ERROR_MESSAGE
                    self.send_simple_reply(mess, reply)

            f = self.commands[cmd]

            if f._jabberbot_command_admin_only:
                if mess.getType() == 'groupchat':
                    self.send_simple_reply(mess, 'You cannot administer the bot from a chatroom, message the bot directly')
                    return
                usr = get_jid_from_message(mess)
                if usr not in BOT_ADMINS:
                    self.send_simple_reply(mess, 'You cannot administer the bot from this user %s.' % usr)
                    return

            if f._jabberbot_command_historize:
                self.cmd_history.append((cmd,  args)) # add it to the history only if it is authorized to be so

            if f._jabberbot_command_split_args_with:
                args = args.split(f._jabberbot_command_split_args_with)
            # Experimental!
            # if command should be executed in a seperate thread do it
            if f._jabberbot_command_thread:
                thread.start_new_thread(execute_and_send, ())
            else:
                execute_and_send()
 def get_current_contact(self, mess):
     return get_jid_from_message(mess).split('@')[0]
Exemple #17
0
    def callback_message(self, conn, mess):
        """
        Needs to return False if we want to stop further treatment
        """
        # Prepare to handle either private chats or group chats
        type = mess.getType()
        jid = mess.getFrom()
        props = mess.getProperties()
        text = mess.getBody()
        username = get_sender_username(mess)

        if type not in ("groupchat", "chat"):
            logging.debug("unhandled message type %s" % mess)
            return False

        logging.debug("*** props = %s" % props)
        logging.debug("*** jid = %s" % jid)
        logging.debug("*** username = %s" % username)
        logging.debug("*** type = %s" % type)
        logging.debug("*** text = %s" % text)

        # If a message format is not supported (eg. encrypted),
        # txt will be None
        if not text: return False

        if not text.startswith(BOT_PREFIX):
            return True

        text = text[1:]
        text_split = text.strip().split(' ')

        cmd = None
        command = None
        args = ''
        if len(text_split) > 1:
            command = (text_split[0] + '_' + text_split[1]).lower()
            if self.commands.has_key(command):
                cmd = command
                args = ' '.join(text_split[2:])

        if not cmd:
            command = text_split[0].lower()
            args = ' '.join(text_split[1:])
            if self.commands.has_key(command):
                cmd = command
                if len(text_split) > 1:
                    args = ' '.join(text_split[1:])

        if command == BOT_PREFIX: # we did "!!" so recall the last command
            if len(self.cmd_history):
                cmd, args = self.cmd_history[-1]
            else:
                return False # no command in history
        elif command.isdigit(): # we did "!#" so we recall the specified command
            index = int(command)
            if len(self.cmd_history) >= index:
                cmd, args = self.cmd_history[-index]
            else:
                return False # no command in history

        if (cmd, args) in self.cmd_history:
            self.cmd_history.remove((cmd, args)) # we readd it below

        logging.info("received command = %s matching [%s] with parameters [%s]" % (command, cmd, args))

        if cmd:
            def execute_and_send(template_name):
                try:
                    reply = self.commands[cmd](mess, args)

                    # integrated templating
                    if template_name:
                        reply = tenv().get_template(template_name + '.html').render(**reply)

                except Exception, e:
                    logging.exception(u'An error happened while processing '\
                                      u'a message ("%s") from %s: %s"' %
                                      (text, jid, traceback.format_exc(e)))
                    reply = self.MSG_ERROR_OCCURRED + ':\n %s' % e
                if reply:
                    if len(reply) > self.MESSAGE_SIZE_LIMIT:
                        reply = reply[:self.MESSAGE_SIZE_LIMIT - len(self.MESSAGE_SIZE_ERROR_MESSAGE)] + self.MESSAGE_SIZE_ERROR_MESSAGE
                    self.send_simple_reply(mess, reply, cmd in DIVERT_TO_PRIVATE)

            f = self.commands[cmd]

            if f._err_command_admin_only:
                if mess.getType() == 'groupchat':
                    self.send_simple_reply(mess, 'You cannot administer the bot from a chatroom, message the bot directly')
                    return False
                usr = get_jid_from_message(mess)
                if usr not in BOT_ADMINS:
                    self.send_simple_reply(mess, 'You cannot administer the bot from this user %s.' % usr)
                    return False
                if BOT_ASYNC:
                    self.thread_pool.wait() # If it is an admin command, wait that the queue is completely depleted so we don't have strange concurrency issues on load/unload/updates etc ...

            if f._err_command_historize:
                self.cmd_history.append((cmd, args)) # add it to the history only if it is authorized to be so

            if f._err_command_split_args_with:
                args = args.split(f._err_command_split_args_with)
            if BOT_ASYNC:
                wr = WorkRequest(execute_and_send, [f._err_command_template]) #execute_and_send(f._err_command_template)
                self.thread_pool.putRequest(wr)
                if f._err_command_admin_only:
                    self.thread_pool.wait() # Again wait for the completion before accepting a new command that could generate weird concurrency issues
            else:
                execute_and_send(f._err_command_template)
Exemple #18
0
    def callback_message(self, conn, mess):
        """
        Needs to return False if we want to stop further treatment
        """
        # Prepare to handle either private chats or group chats
        type = mess.getType()
        jid = mess.getFrom()
        props = mess.getProperties()
        text = mess.getBody()
        username = get_sender_username(mess)

        if type not in ("groupchat", "chat"):
            logging.debug("unhandled message type %s" % mess)
            return False

        logging.debug("*** props = %s" % props)
        logging.debug("*** jid = %s" % jid)
        logging.debug("*** username = %s" % username)
        logging.debug("*** type = %s" % type)
        logging.debug("*** text = %s" % text)

        # If a message format is not supported (eg. encrypted),
        # txt will be None
        if not text: return False

        surpress_cmd_not_found = False

        tomatch = text.lower() if BOT_ALT_PREFIX_CASEINSENSITIVE else text
        if len(BOT_ALT_PREFIXES) > 0 and tomatch.startswith(self.bot_alt_prefixes):
            # Yay! We were called by one of our alternate prefixes. Now we just have to find out
            # which one... (And find the longest matching, in case you have 'err' and 'errbot' and
            # someone uses 'errbot', which also matches 'err' but would leave 'bot' to be taken as
            # part of the called command in that case)
            longest = 0
            for prefix in self.bot_alt_prefixes:
                l = len(prefix)
                if tomatch.startswith(prefix) and l > longest:
                    longest = l
            text = text[longest:]

            # Now also remove the separator from the text
            for sep in BOT_ALT_PREFIX_SEPARATORS:
                # While unlikely, one may have separators consisting of
                # more than one character
                l = len(sep)
                if text[:l] == sep:
                    text = text[l:]
        elif type == "chat" and BOT_PREFIX_OPTIONAL_ON_CHAT:
            logging.debug("Assuming '%s' to be a command because BOT_PREFIX_OPTIONAL_ON_CHAT is True" % text)
            # In order to keep noise down we surpress messages about the command
            # not being found, because it's possible a plugin will trigger on what
            # was said with trigger_message.
            surpress_cmd_not_found = True
        elif not text.startswith(BOT_PREFIX):
            return True
        else:
            text = text[len(BOT_PREFIX):]

        text_split = text.strip().split(' ')
        cmd = None
        command = None
        args = ''
        if len(text_split) > 1:
            command = (text_split[0] + '_' + text_split[1]).lower()
            if command in self.commands:
                cmd = command
                args = ' '.join(text_split[2:])

        if not cmd:
            command = text_split[0].lower()
            args = ' '.join(text_split[1:])
            if command in self.commands:
                cmd = command
                if len(text_split) > 1:
                    args = ' '.join(text_split[1:])

        if command == BOT_PREFIX:  # we did "!!" so recall the last command
            if len(self.cmd_history):
                cmd, args = self.cmd_history[-1]
            else:
                return False  # no command in history
        elif command.isdigit():  # we did "!#" so we recall the specified command
            index = int(command)
            if len(self.cmd_history) >= index:
                cmd, args = self.cmd_history[-index]
            else:
                return False  # no command in history

        if (cmd, args) in self.cmd_history:
            self.cmd_history.remove((cmd, args))  # we readd it below

        logging.info("received command = %s matching [%s] with parameters [%s]" % (command, cmd, args))

        if cmd:
            def execute_and_send(template_name):
                try:
                    reply = self.commands[cmd](mess, args)

                    # integrated templating
                    if template_name:
                        reply = tenv().get_template(template_name + '.html').render(**reply)

                    # Reply should be all text at this point (See https://github.com/gbin/err/issues/96)
                    reply = unicode(reply)
                except Exception, e:
                    logging.exception(u'An error happened while processing '
                                      u'a message ("%s") from %s: %s"' %
                                      (text, jid, traceback.format_exc(e)))
                    reply = self.MSG_ERROR_OCCURRED + ':\n %s' % e
                if reply:
                    if len(reply) > self.MESSAGE_SIZE_LIMIT:
                        reply = reply[:self.MESSAGE_SIZE_LIMIT - len(self.MESSAGE_SIZE_ERROR_MESSAGE)] + self.MESSAGE_SIZE_ERROR_MESSAGE
                    self.send_simple_reply(mess, reply, cmd in DIVERT_TO_PRIVATE)

            usr = str(get_jid_from_message(mess))
            typ = mess.getType()
            if cmd not in ACCESS_CONTROLS:
                ACCESS_CONTROLS[cmd] = ACCESS_CONTROLS_DEFAULT

            if 'allowusers' in ACCESS_CONTROLS[cmd] and usr not in ACCESS_CONTROLS[cmd]['allowusers']:
                self.send_simple_reply(mess, "You're not allowed to access this command from this user")
                return False
            if 'denyusers' in ACCESS_CONTROLS[cmd] and usr in ACCESS_CONTROLS[cmd]['denyusers']:
                self.send_simple_reply(mess, "You're not allowed to access this command from this user")
                return False
            if typ == 'groupchat':
                stripped = mess.getFrom().getStripped()
                if 'allowmuc' in ACCESS_CONTROLS[cmd] and ACCESS_CONTROLS[cmd]['allowmuc'] is False:
                    self.send_simple_reply(mess, "You're not allowed to access this command from a chatroom")
                    return False
                if 'allowrooms' in ACCESS_CONTROLS[cmd] and stripped not in ACCESS_CONTROLS[cmd]['allowrooms']:
                    self.send_simple_reply(mess, "You're not allowed to access this command from this room")
                    return False
                if 'denyrooms' in ACCESS_CONTROLS[cmd] and stripped in ACCESS_CONTROLS[cmd]['denyrooms']:
                    self.send_simple_reply(mess, "You're not allowed to access this command from this room")
                    return False
            else:
                if 'allowprivate' in ACCESS_CONTROLS[cmd] and ACCESS_CONTROLS[cmd]['allowprivate'] is False:
                    self.send_simple_reply(mess, "You're not allowed to access this command via private message to me")
                    return False

            f = self.commands[cmd]

            if f._err_command_admin_only:
                if typ == 'groupchat':
                    self.send_simple_reply(mess, 'You cannot administer the bot from a chatroom, message the bot directly')
                    return False
                if usr not in BOT_ADMINS:
                    self.send_simple_reply(mess, 'You cannot administer the bot from this user %s.' % usr)
                    return False
                if BOT_ASYNC:
                    self.thread_pool.wait() # If it is an admin command, wait that the queue is completely depleted so we don't have strange concurrency issues on load/unload/updates etc ...

            if f._err_command_historize:
                self.cmd_history.append((cmd, args)) # add it to the history only if it is authorized to be so

            # Don't check for None here as None can be a valid argument to split.
            # '' was chosen as default argument because this isn't a valid argument to split()
            if f._err_command_split_args_with != '':
                args = args.split(f._err_command_split_args_with)
            if BOT_ASYNC:
                wr = WorkRequest(execute_and_send, [f._err_command_template]) #execute_and_send(f._err_command_template)
                self.thread_pool.putRequest(wr)
                if f._err_command_admin_only:
                    self.thread_pool.wait() # Again wait for the completion before accepting a new command that could generate weird concurrency issues
            else:
                execute_and_send(f._err_command_template)