def pager_trigger(self, mess, args): """ Trigger an incident """ if self.config: if mess.getType() == "chat": return "Sorry, you need to use group chat for PagerDuty commands" else: pd_api = "https://events.pagerduty.com/generic/2010-04-15/create_event.json" body = { 'service_key': self.config['SERVICE_API_KEY'], 'event_type': 'trigger', 'description': 'Urgent page via chat', 'details': { 'requestor': get_sender_username(mess), 'message': " ".join(args) } } response = requests.post(pd_api, data=json.dumps(body)) if response.status_code in (200, ): return "Triggered incident %s" % ( response.json()['incident_key'], ) else: logging.error("[PagerDuty] Non-200 response: %s" % response.status_code) logging.error("[PagerDuty] Body: %s" % response.json()) return "Sorry, something went wrong. You should check the logs." else: return "Sorry, PagerDuty is not configured"
def _process_command(self, mess, cmd, args, match): """Process and execute a bot command""" logging.info("Processing command {} with parameters '{}'".format(cmd, args)) jid = mess.frm username = get_sender_username(mess) user_cmd_history = self.cmd_history[username] if (cmd, args) in user_cmd_history: user_cmd_history.remove((cmd, args)) # Avoids duplicate history items try: self.check_command_access(mess, cmd) except ACLViolation as e: if not HIDE_RESTRICTED_ACCESS: self.send_simple_reply(mess, str(e)) return f = self.re_commands[cmd] if match else self.commands[cmd] if f._err_command_admin_only and BOT_ASYNC: # If it is an admin command, wait until the queue is completely depleted so # we don't have strange concurrency issues on load/unload/updates etc... self.thread_pool.wait() if f._err_command_historize: user_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 str.split. # '' was chosen as default argument because this isn't a valid argument to str.split() if not match and f._err_command_split_args_with != '': try: if hasattr(f._err_command_split_args_with, "parse_args"): args = f._err_command_split_args_with.parse_args(args) elif callable(f._err_command_split_args_with): args = f._err_command_split_args_with(args) else: args = args.split(f._err_command_split_args_with) except Exception as e: self.send_simple_reply( mess, "Sorry, I couldn't parse your arguments. {}".format(e) ) return if BOT_ASYNC: wr = WorkRequest( self._execute_and_send, [], {'cmd': cmd, 'args': args, 'match': match, 'mess': mess, 'jid': jid, 'template_name': f._err_command_template} ) self.thread_pool.putRequest(wr) if f._err_command_admin_only: # Again, if it is an admin command, wait until the queue is completely # depleted so we don't have strange concurrency issues. self.thread_pool.wait() else: self._execute_and_send(cmd=cmd, args=args, match=match, mess=mess, jid=jid, template_name=f._err_command_template)
def xup_remove(self, mess, args): """Remove yourself from the ready list""" user = get_sender_username(mess) del self['users'][user] return "Done."
def pager_register(self, mess, args): """ Register as a PagerDuty user with the bot via your email address """ if self.config: if mess.getType() == "chat": return "Sorry, you need to use group chat for PagerDuty commands" else: if len(args) <= 0: return "I can't register you without an email address" else: users = self.get_users() uid = get_sender_username(mess) email = args[0] try: user_index = map(itemgetter('uid'), users).index(uid) user = users[user_index] return "You are already registered as %s" % (user['email'],) except: try: existing_user_index = map(itemgetter('email'), users).index(email) existing_user = users[existing_user_index] return "The email address you provided is already registered to %s" % (existing_user['uid'],) except: pd_id = self.get_pd_id_by_email(email) if pd_id is None: return "Sorry, I couldn't find a PagerDuty user with that email address" else: logging.info("[PagerDuty] registering user ID %s as %s" % (uid, email,)) self.add_user(uid=uid, email=email, pd_id=pd_id) updated_user_list = self.get_users() logging.debug("[PagerDuty] new user list: %s" % (updated_user_list,)) return "You are now licensed to kill" else: return "Sorry, PagerDuty is not configured"
def pager_resolve(self, mess, args): """ Resolve an incident by it's alphanumeric ID """ if self.config: if mess.getType() == "chat": return "Sorry, you need to use group chat for PagerDuty commands" else: try: requestor = self.get_user(get_sender_username(mess)) except: return "Sorry, I don't know who you are. Please use !pager register to teach me your email address." try: incident_id = args[0] logging.info( "[PagerDuty] resolving incident %s by request of %s" % (incident_id, requestor['uid'])) self.pager.incidents.update(requestor['pd_id'], { 'id': incident_id, 'status': 'resolved' }) return "Resolved incident %s" % (incident_id, ) except HTTPError as e: logging.error( "[PagerDuty] Error resolving incident %s: %s" % ( incident_id, e, )) return "Failed to resolve incident %s: %s" % ( incident_id, e, ) else: return "Sorry, PagerDuty is not configured"
def pager_steal(self, mess, args): """ Steal the pager for N minutes """ if self.config: if mess.getType() == "chat": return "Sorry, you need to use group chat for PagerDuty commands" else: if len(args) <= 0: return "Sorry, you need to specify the number of minutes for which you'd like to steal the pager" else: requestor_name = get_sender_username(mess) users = self.get_users() requestor_index = map(itemgetter('uid'), users).index(requestor_name) requestor = users[requestor_index] schedule_id = self.config['SCHEDULE_ID'] logging.info("[PagerDuty] override requestor found: %s" % (requestor, )) try: override_duration = int(args[0]) except: return "Sorry, I could not transform %s into an integer" if requestor['pd_id'] == self.get_oncall_pd_id( schedule_id): return "Sorry, you are already on call" else: local_tz = get_localzone() schedule_tz = pytz.timezone( self.config['SCHEDULE_TIMEZONE']) local_dt = local_tz.localize(datetime.now()) now = local_dt.astimezone(schedule_tz) later = now + timedelta(minutes=override_duration) try: schedule = self.pager.schedules.show(schedule_id) schedule.overrides.create( start=now.isoformat(), end=later.isoformat(), user_id=requestor['pd_id']) return "Rejoice ye oncall, %s has the pager for %s minutes(s)" % ( requestor['uid'], override_duration, ) except HTTPError as e: logging.error( "[PagerDuty] Error overriding schedule %s: %s" % ( schedule_id, e, )) raise Exception( "Error overriding schedule %s: %s" % ( schedule_id, e, )) else: return "Sorry, PagerDuty is not configured"
def history(self, mess, args): """display the command history""" answer = [] user_cmd_history = self.cmd_history[get_sender_username(mess)] l = len(user_cmd_history) for i in range(0, l): c = user_cmd_history[i] answer.append("%2i:%s%s %s" % (l - i, self.prefix, c[0], c[1])) return "\n".join(answer)
def callback_message(self, conn, mess): """Listen for Die Hard mentions and interject random lines from those characters who were mentioned. """ logging.debug("mess.getFrom().getStripped(): " + mess.getFrom().getStripped()) logging.debug("config.BOT_IDENTITY['username']: "******"username"]) logging.debug("get_sender_username(mess): " + get_sender_username(mess)) logging.debug("config.CHATROOM_FN: " + config.CHATROOM_FN) if (mess.getFrom().getStripped() == config.BOT_IDENTITY["username"]) or (get_sender_username(mess) == config.CHATROOM_FN): logging.debug("Ignore a message from myself") return False message = "" for character in DieHard.CHARACTERS: if mess.getBody().find("(%s)" % character) != -1: message += "(%s) %s " % (character, self.diehard.get_random(character)) if message: self.send(mess.getFrom(), message, message_type=mess.getType())
def history(self, mess, args): """display the command history""" answer = [] user_cmd_history = self.cmd_history[get_sender_username(mess)] l = len(user_cmd_history) for i in range(0, l): c = user_cmd_history[i] answer.append('%2i:%s%s %s' % (l - i, self.prefix, c[0], c[1])) return '\n'.join(answer)
def xup(self, mess, args): """Add yourself to the ready list, you can include an optional message.""" user = get_sender_username(mess) xup_args = {'user': user, 'args': args, 'message': mess.getBody(), 'time': datetime.utcnow()} self['users'][user] = xup_args return "Done."
def xup(self, mess, args): """Add yourself to the ready list, you can include an optional message.""" user = get_sender_username(mess) xup_args = { 'user': user, 'args': args, 'message': mess.getBody(), 'time': datetime.utcnow() } self['users'][user] = xup_args return "Done."
def callback_message(self, conn, mess): """Listen for Die Hard mentions and interject random lines from those characters who were mentioned. """ logging.debug("mess.getFrom().getStripped(): " + mess.getFrom().getStripped()) logging.debug("config.BOT_IDENTITY['username']: "******"username"]) logging.debug("get_sender_username(mess): " + get_sender_username(mess)) logging.debug("config.CHATROOM_FN: " + config.CHATROOM_FN) if (mess.getFrom().getStripped() == config.BOT_IDENTITY["username"] ) or (get_sender_username(mess) == config.CHATROOM_FN): logging.debug("Ignore a message from myself") return False message = "" for character in DieHard.CHARACTERS: if mess.getBody().find("(%s)" % character) != -1: message += "(%s) %s " % (character, self.diehard.get_random(character)) if message: self.send(mess.getFrom(), message, message_type=mess.getType())
def pager_whoami(self, mess, args): """ ... and how did I get here?""" if self.config: if mess.getType() == "chat": return "Sorry, you need to use group chat for PagerDuty commands" else: uid = get_sender_username(mess) users = self.get_users() try: user_index = map(itemgetter('uid'), users).index(uid) user = users[user_index] return "I have you registered as %s with PagerDuty ID %s" % (user['email'], user['pd_id'],) except ValueError as e: return "I don't think I know you: %s" % (e,) else: return "Sorry, PagerDuty is not configured"
def callback_message(self, conn, mess): command = re.match('s/([^/]+)/([^/]*)/?', mess.getBody()) if command: for old_mess in self.backlog[conn]: try: (replaced, n) = re.subn(command.group(1), command.group(2), old_mess.getBody()) except Exception as e: self.send(mess.getFrom(), "regex does not compute", message_type=mess.getType()) return if n > 0: reply = '{0} meant: "{1}"'.format(get_sender_username(old_mess), replaced) self.send(mess.getFrom(), reply, message_type=mess.getType()) return self.backlog[conn] = ([mess] + self.backlog[conn])[:self.config['BACKLOG_LENGTH']]
def pager_register(self, mess, args): """ Register as a PagerDuty user with the bot via your email address """ if self.config: if mess.getType() == "chat": return "Sorry, you need to use group chat for PagerDuty commands" else: if len(args) <= 0: return "I can't register you without an email address" else: users = self.get_users() uid = get_sender_username(mess) email = args[0] try: user_index = map(itemgetter('uid'), users).index(uid) user = users[user_index] return "You are already registered as %s" % ( user['email'], ) except: try: existing_user_index = map(itemgetter('email'), users).index(email) existing_user = users[existing_user_index] return "The email address you provided is already registered to %s" % ( existing_user['uid'], ) except: pd_id = self.get_pd_id_by_email(email) if pd_id is None: return "Sorry, I couldn't find a PagerDuty user with that email address" else: logging.info( "[PagerDuty] registering user ID %s as %s" % ( uid, email, )) self.add_user(uid=uid, email=email, pd_id=pd_id) updated_user_list = self.get_users() logging.debug("[PagerDuty] new user list: %s" % (updated_user_list, )) return "You are now licensed to kill" else: return "Sorry, PagerDuty is not configured"
def ping(self, mess, args): """A command which simply returns 'Example'""" who=args.split(' ',1) if len(who) > 1: pingTarget=who[0]+"@ping.pfralliance.com" sender=get_sender_username(mess) time=datetime.utcnow().strftime('%H:%M:%S') content=who[1]+"\n\n This is a broadcast sent at "+time+" EVE Time to "+who[0]+" from "+sender+". Replies are not monitored." self.send( pingTarget, content, message_type="chat" ) output="Ping sent to "+who[0] return output else: return "You did it wrong! Try !ping usergroup message. Check !groups for a list of groups you can ping."
def pager_unregister(self, mess, args): """ Remove your PagerDuty user registration """ if self.config: if mess.getType() == "chat": return "Sorry, you need to use group chat for PagerDuty commands" else: users = self.get_users() uid = get_sender_username(mess) for user in users: if user['uid'] == uid: logging.info("[PagerDuty] unregistering user ID %s" % (uid,)) self.remove_user(uid=uid) updated_user_list = self.get_users() logging.debug("[PagerDuty] new user list: %s" % (updated_user_list,)) return "You're dead to me" else: return "Sorry, I'm afraid I don't know who you are to begin with" else: return "Sorry, PagerDuty is not configured"
def pager_whoami(self, mess, args): """ ... and how did I get here?""" if self.config: if mess.getType() == "chat": return "Sorry, you need to use group chat for PagerDuty commands" else: uid = get_sender_username(mess) users = self.get_users() try: user_index = map(itemgetter('uid'), users).index(uid) user = users[user_index] return "I have you registered as %s with PagerDuty ID %s" % ( user['email'], user['pd_id'], ) except ValueError as e: return "I don't think I know you: %s" % (e, ) else: return "Sorry, PagerDuty is not configured"
def pager_resolve(self, mess, args): """ Resolve an incident by it's alphanumeric ID """ if self.config: if mess.getType() == "chat": return "Sorry, you need to use group chat for PagerDuty commands" else: try: requestor = self.get_user(get_sender_username(mess)) except: return "Sorry, I don't know who you are. Please use !pager register to teach me your email address." try: incident_id = args[0] logging.info("[PagerDuty] resolving incident %s by request of %s" % (incident_id, requestor['uid'])) self.pager.incidents.update(requestor['pd_id'], {'id': incident_id, 'status': 'resolved'}) return "Resolved incident %s" % (incident_id,) except HTTPError as e: logging.error("[PagerDuty] Error resolving incident %s: %s" % (incident_id, e,)) return "Failed to resolve incident %s: %s" % (incident_id, e,) else: return "Sorry, PagerDuty is not configured"
def pager_unregister(self, mess, args): """ Remove your PagerDuty user registration """ if self.config: if mess.getType() == "chat": return "Sorry, you need to use group chat for PagerDuty commands" else: users = self.get_users() uid = get_sender_username(mess) for user in users: if user['uid'] == uid: logging.info("[PagerDuty] unregistering user ID %s" % (uid, )) self.remove_user(uid=uid) updated_user_list = self.get_users() logging.debug("[PagerDuty] new user list: %s" % (updated_user_list, )) return "You're dead to me" else: return "Sorry, I'm afraid I don't know who you are to begin with" else: return "Sorry, PagerDuty is not configured"
def pager_steal(self, mess, args): """ Steal the pager for N minutes """ if self.config: if mess.getType() == "chat": return "Sorry, you need to use group chat for PagerDuty commands" else: if len(args) <= 0: return "Sorry, you need to specify the number of minutes for which you'd like to steal the pager" else: requestor_name = get_sender_username(mess) users = self.get_users() requestor_index = map(itemgetter('uid'), users).index(requestor_name) requestor = users[requestor_index] schedule_id = self.config['SCHEDULE_ID'] logging.info("[PagerDuty] override requestor found: %s" % (requestor,)) try: override_duration = int(args[0]) except: return "Sorry, I could not transform %s into an integer" if requestor['pd_id'] == self.get_oncall_pd_id(schedule_id): return "Sorry, you are already on call" else: local_tz = get_localzone() schedule_tz = pytz.timezone(self.config['SCHEDULE_TIMEZONE']) local_dt = local_tz.localize(datetime.now()) now = local_dt.astimezone(schedule_tz) later = now + timedelta(minutes=override_duration) try: schedule = self.pager.schedules.show(schedule_id) schedule.overrides.create(start=now.isoformat(), end=later.isoformat(), user_id=requestor['pd_id']) return "Rejoice ye oncall, %s has the pager for %s minutes(s)" % (requestor['uid'], override_duration,) except HTTPError as e: logging.error("[PagerDuty] Error overriding schedule %s: %s" % (schedule_id, e,)) raise Exception("Error overriding schedule %s: %s" % (schedule_id, e,)) else: return "Sorry, PagerDuty is not configured"
def pager_trigger(self, mess, args): """ Trigger an incident """ if self.config: if mess.getType() == "chat": return "Sorry, you need to use group chat for PagerDuty commands" else: pd_api = "https://events.pagerduty.com/generic/2010-04-15/create_event.json" body = {'service_key': self.config['SERVICE_API_KEY'], 'event_type': 'trigger', 'description': 'Urgent page via chat', 'details': {'requestor': get_sender_username(mess), 'message': " ".join(args)} } response = requests.post(pd_api, data=json.dumps(body)) if response.status_code in (200,): return "Triggered incident %s" % (response.json()['incident_key'],) else: logging.error("[PagerDuty] Non-200 response: %s" % response.status_code) logging.error("[PagerDuty] Body: %s" % response.json()) return "Sorry, something went wrong. You should check the logs." else: return "Sorry, PagerDuty is not configured"
def callback_message(self, conn, mess): command = re.match('s/([^/]+)/([^/]*)/?', mess.getBody()) if command: for old_mess in self.backlog: try: (replaced, n) = re.subn(command.group(1), command.group(2), old_mess.getBody()) except Exception as e: self.send(mess.getFrom(), "regex does not compute", message_type=mess.getType()) return if n > 0: reply = '{0} meant: "{1}"'.format( get_sender_username(old_mess), replaced) self.send(mess.getFrom(), reply, message_type=mess.getType()) return self.backlog.insert(0, mess) self.backlog = self.backlog[:self.config['BACKLOG_LENGTH']]
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() text = mess.getBody() username = get_sender_username(mess) user_cmd_history = self.cmd_history[username] if mess.isDelayed(): logging.debug("Message from history, ignore it") return False if type not in ("groupchat", "chat"): logging.debug("unhandled message type %s" % mess) 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 (jid.bareMatch(Identifier(self.jid)) or type == "groupchat" and mess.getMuckNick() == CHATROOM_FN): logging.debug("Ignoring message from self") return False 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 prefixed = False # Keeps track whether text was prefixed with a bot prefix only_check_re_command = False # Becomes true if text is determed to not be a regular command 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) prefixed = True longest = 0 for prefix in self.bot_alt_prefixes: l = len(prefix) if tomatch.startswith(prefix) and l > longest: longest = l logging.debug("Called with alternate prefix '{}'".format( text[:longest])) 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): only_check_re_command = True if text.startswith(BOT_PREFIX): text = text[len(BOT_PREFIX):] prefixed = True text = text.strip() text_split = text.split(' ') cmd = None command = None args = '' if not only_check_re_command: 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(user_cmd_history): cmd, args = user_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(user_cmd_history) >= index: cmd, args = user_cmd_history[-index] else: return False # no command in history # Try to match one of the regex commands if the regular commands produced no match matched_on_re_command = False if not cmd: if prefixed: commands = self.re_commands else: commands = { k: self.re_commands[k] for k in self.re_commands if not self.re_commands[k]._err_command_prefix_required } for name, func in commands.items(): match = func._err_command_re_pattern.search(text) if match: logging.debug( "Matching '{}' against '{}' produced a match".format( text, func._err_command_re_pattern.pattern)) matched_on_re_command = True self._process_command(mess, name, text, match) else: logging.debug( "Matching '{}' against '{}' produced no match".format( text, func._err_command_re_pattern.pattern)) if matched_on_re_command: return True if cmd: self._process_command(mess, cmd, args, match=None) elif not only_check_re_command: logging.debug("Command not found") if surpress_cmd_not_found: logging.debug("Surpressing command not found feedback") else: reply = self.unknown_command(mess, command, args) if reply is None: reply = self.MSG_UNKNOWN_COMMAND % {'command': command} if reply: self.send_simple_reply(mess, reply) return True
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() text = mess.getBody() username = get_sender_username(mess) user_cmd_history = self.cmd_history[username] if mess.isDelayed(): logging.debug("Message from history, ignore it") return False if type not in ("groupchat", "chat"): logging.debug("unhandled message type %s" % mess) return False 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 if text.startswith(BOT_PREFIX): 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(user_cmd_history): cmd, args = user_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(user_cmd_history) >= index: cmd, args = user_cmd_history[-index] else: return False # no command in history if (cmd, args) in user_cmd_history: user_cmd_history.remove((cmd, args)) # we readd it below if cmd: logging.info( "received command = %s matching [%s] with parameters [%s]" % (command, cmd, args)) access, accessError = self.check_command_access(mess, cmd) if not access: if not HIDE_RESTRICTED_ACCESS: self.send_simple_reply(mess, accessError) return False f = self.commands[cmd] if f._err_command_admin_only and 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: user_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( self._execute_and_send, [], { 'cmd': cmd, 'args': args, 'mess': mess, 'jid': jid, 'template_name': 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: self._execute_and_send(cmd=cmd, args=args, mess=mess, jid=jid, template_name=f._err_command_template) else: logging.debug("Command not found") if surpress_cmd_not_found: logging.debug("Surpressing command not found feedback") else: reply = self.unknown_command(mess, command, args) if reply is None: reply = self.MSG_UNKNOWN_COMMAND % {'command': command} if reply: self.send_simple_reply(mess, reply) return True
def helper(self, mess, args): output = func(self, mess, args) if len(output) > 8000: return "How appropriate, you code like a cow, {0}!".format(get_sender_username(mess)) else: return output
def todo_create(self, mess, args): """Creates a new entry on the todo list. Syntax: !todo create <title>.""" self.l.append(Entry(args, get_sender_username(mess))) self.write_csv_file() return "Created a new entry with id " + str(len(self.l)-1) + ", use !todo describe " + str(len(self.l)-1) + " to add a detailed description."
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() text = mess.getBody() username = get_sender_username(mess) if mess.isDelayed(): logging.debug("Message from history, ignore it") return False if type not in ("groupchat", "chat"): logging.debug("unhandled message type %s" % mess) return False 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 = str(reply) except Exception as e: tb = traceback.format_exc() logging.exception('An error happened while processing ' 'a message ("%s") from %s: %s"' % (text, jid, tb)) 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]) 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) else: logging.debug("Command not found") if surpress_cmd_not_found: logging.debug("Surpressing command not found feedback") else: reply = self.unknown_command(mess, command, args) if reply is None: reply = self.MSG_UNKNOWN_COMMAND % {'command': command} if reply: self.send_simple_reply(mess, reply) return True
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, mess): """ Needs to return False if we want to stop further treatment """ # Prepare to handle either private chats or group chats type_ = mess.type jid = mess.frm text = mess.body username = get_sender_username(mess) user_cmd_history = self.cmd_history[username] if mess.delayed: log.debug("Message from history, ignore it") return False if type_ not in ("groupchat", "chat"): log.debug("unhandled message type %s" % mess) 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 (jid.bare_match(self.jid) or type_ == "groupchat" and mess.nick == self.bot_config.CHATROOM_FN): # noqa log.debug("Ignoring message from self") return False log.debug("*** jid = %s" % jid) log.debug("*** username = %s" % username) log.debug("*** type = %s" % type_) log.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 prefixed = False # Keeps track whether text was prefixed with a bot prefix only_check_re_command = False # Becomes true if text is determed to not be a regular command tomatch = text.lower() if self.bot_config.BOT_ALT_PREFIX_CASEINSENSITIVE else text if len(self.bot_config.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) prefixed = True longest = 0 for prefix in self.bot_alt_prefixes: l = len(prefix) if tomatch.startswith(prefix) and l > longest: longest = l log.debug("Called with alternate prefix '{}'".format(text[:longest])) text = text[longest:] # Now also remove the separator from the text for sep in self.bot_config.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 self.bot_config.BOT_PREFIX_OPTIONAL_ON_CHAT: log.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(self.bot_config.BOT_PREFIX): only_check_re_command = True if text.startswith(self.bot_config.BOT_PREFIX): text = text[len(self.bot_config.BOT_PREFIX):] prefixed = True text = text.strip() text_split = text.split(' ') cmd = None command = None args = '' if not only_check_re_command: 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 == self.bot_config.BOT_PREFIX: # we did "!!" so recall the last command if len(user_cmd_history): cmd, args = user_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(user_cmd_history) >= index: cmd, args = user_cmd_history[-index] else: return False # no command in history # Try to match one of the regex commands if the regular commands produced no match matched_on_re_command = False if not cmd: if prefixed: commands = self.re_commands else: commands = {k: self.re_commands[k] for k in self.re_commands if not self.re_commands[k]._err_command_prefix_required} for name, func in commands.items(): if func._err_command_matchall: match = list(func._err_command_re_pattern.finditer(text)) else: match = func._err_command_re_pattern.search(text) if match: log.debug("Matching '{}' against '{}' produced a match" .format(text, func._err_command_re_pattern.pattern)) matched_on_re_command = True self._process_command(mess, name, text, match) else: log.debug("Matching '{}' against '{}' produced no match" .format(text, func._err_command_re_pattern.pattern)) if matched_on_re_command: return True if cmd: self._process_command(mess, cmd, args, match=None) elif not only_check_re_command: log.debug("Command not found") if surpress_cmd_not_found: log.debug("Surpressing command not found feedback") else: reply = self.unknown_command(mess, command, args) if reply is None: reply = self.MSG_UNKNOWN_COMMAND % {'command': command} if reply: self.send_simple_reply(mess, reply) return True
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() text = mess.getBody() username = get_sender_username(mess) user_cmd_history = self.cmd_history[username]; if mess.isDelayed(): logging.debug("Message from history, ignore it") return False if type not in ("groupchat", "chat"): logging.debug("unhandled message type %s" % mess) return False 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 if text.startswith(BOT_PREFIX): 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(user_cmd_history): cmd, args = user_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(user_cmd_history) >= index: cmd, args = user_cmd_history[-index] else: return False # no command in history if (cmd, args) in user_cmd_history: user_cmd_history.remove((cmd, args)) # we readd it below if cmd: logging.info("received command = %s matching [%s] with parameters [%s]" % (command, cmd, args)) access, accessError = self.checkCommandAccess(mess, cmd) if not access: if not HIDE_RESTRICTED_ACCESS: self.send_simple_reply(mess, accessError) return False f = self.commands[cmd] if f._err_command_admin_only and 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: user_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(self._execute_and_send, [], {'cmd': cmd, 'args': args, 'mess': mess, 'jid': jid, 'template_name': 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: self._execute_and_send(cmd=cmd, args=args, mess=mess, jid=jid, template_name=f._err_command_template) else: logging.debug("Command not found") if surpress_cmd_not_found: logging.debug("Surpressing command not found feedback") else: reply = self.unknown_command(mess, command, args) if reply is None: reply = self.MSG_UNKNOWN_COMMAND % {'command': command} if reply: self.send_simple_reply(mess, reply) return True
def slap(self, mess, args): """Let the bot slap people in the chatroom in an piratelike manner""" return "{0} slaps {1} around a bit with a large trout.".format(get_sender_username(mess), args)