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()
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
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")
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
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)
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)
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
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)
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)
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)
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)
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]
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)
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)