From 52970894a90af4de077788adf8e39f3a681355ed Mon Sep 17 00:00:00 2001 From: "Brian S. Stephan" Date: Thu, 20 Dec 2012 09:58:25 -0600 Subject: [PATCH] DrBotIRC: add regex-matching global handlers add_global_regex_handler and remove_global_regex_handler are new methods that work the same as irclib's non-regex versions, but check patterns before adding/removing. this allows for a more direct link between event loop and module methods i'm hoping that one day it will allow for the removal of ancient shortcuts that just get in the way now, like do() and on_pub_or_privmsg() lightly tested, if anything is still brittle it's recursion, no doubt --- DrBotIRC.py | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 1 deletion(-) diff --git a/DrBotIRC.py b/DrBotIRC.py index ae0c95b..47f174b 100644 --- a/DrBotIRC.py +++ b/DrBotIRC.py @@ -17,6 +17,7 @@ along with this program. If not, see . """ +import bisect import copy from ConfigParser import NoOptionError, NoSectionError import logging @@ -213,6 +214,8 @@ class DrBotIRC(irclib.IRC): self.config = config self.xmlrpc = None + self.regex_handlers = dict() + # handle SIGINT signal.signal(signal.SIGINT, self.sigint_handler) @@ -257,8 +260,62 @@ class DrBotIRC(irclib.IRC): return self.server + def add_global_regex_handler(self, event, regex, handler, priority=0): + """Adds a global handler function for a specific event type and regex. + + The handler function is called whenever the specified event is + triggered in any of the connections and the regex matches. + See documentation for the Event class. + + The handler functions are called in priority order (lowest + number is highest priority). If a handler function returns + "NO MORE", no more handlers will be called. + + This is basically an extension of add_global_handler(), either may + work, though it turns out most modules probably want this one. + + The provided method should take as arguments: + * nick the nick creating the event, or None + * userhost the userhost creating the event, or None + * event the raw IRC event, which may be None + * from_admin whether or not the event came from an admin + * groups list of match.groups(), from re.search() + + Args: + event event type (a string), as in numeric_events + regex regex string to match before doing callback invocation + handler callback function to invoke + priority integer, the lower number, the higher priority + + """ + + if not event in self.regex_handlers: + self.regex_handlers[event] = [] + bisect.insort(self.regex_handlers[event], ((priority, regex, handler))) + + def remove_global_regex_handler(self, event, regex, handler): + """Removes a global regex handler function. + + Args: + event event type (a string), as in numeric_events + regex regex string that was used in matching + handler callback function to remove + + Returns: + 1 on success, otherwise 0. + + """ + + if not event in self.regex_handlers: + return 0 + + for h in self.regex_handlers[event]: + if regex == h[1] and handler == h[2]: + self.regex_handlers[event].remove(h) + return 1 + def _handle_event(self, connection, event): - """Override event handler to do recursion. + """Override event handler to do recursion and regex checking. Args: connection source connection @@ -287,6 +344,41 @@ class DrBotIRC(irclib.IRC): self.try_recursion(connection, event) self.try_alias(connection, event) + nick = None + userhost = None + admin = False + + if event.source() is not None: + nick = irclib.nm_to_n(event.source()) + try: + userhost = irclib.nm_to_uh(event.source()) + except IndexError: pass + + try: + if userhost == self.config.get('dr.botzo', 'admin_userhost'): + admin = True + except NoOptionError: pass + + # try regex handlers first, since they're more specific + rh = self.regex_handlers + trh = sorted(rh.get('all_events', []) + rh.get(event.eventtype(), [])) + for handler in trh: + try: + prio, regex, method = handler + for line in event.arguments(): + match = re.search(regex, line) + if match: + log.debug("pattern matched, calling " + "{0:s}".format(method)) + # pattern matched, call method with pattern groups + ret = method(nick, userhost, event, admin, + match.groups()) + if ret == 'NO MORE': + return + except Exception as e: + log.error("exception floated up to DrBotIrc!") + log.exception(e) + h = self.handlers th = sorted(h.get('all_events', []) + h.get(event.eventtype(), [])) for handler in th: @@ -335,6 +427,41 @@ class DrBotIRC(irclib.IRC): replies = [] + nick = None + userhost = None + admin = False + + if event.source() is not None: + nick = irclib.nm_to_n(event.source()) + try: + userhost = irclib.nm_to_uh(event.source()) + except IndexError: pass + + try: + if userhost == self.config.get('dr.botzo', 'admin_userhost'): + admin = True + except NoOptionError: pass + + # try regex handlers first, since they're more specific + rh = self.regex_handlers + trh = sorted(rh.get('all_events', []) + rh.get(event.eventtype(), [])) + for handler in trh: + try: + prio, regex, method = handler + for line in event.arguments(): + match = re.search(regex, line) + if match: + log.debug("pattern matched, calling " + "{0:s}".format(method)) + # pattern matched, call method with pattern groups + ret = method(nick, userhost, event, admin, + match.groups()) + if ret: + replies.append(ret) + except Exception as e: + log.error("exception floated up to DrBotIrc!") + log.exception(e) + h = self.handlers th = sorted(h.get('all_events', []) + h.get(event.eventtype(), [])) for handler in th: