diff --git a/dr_botzo/ircbot/bot.py b/dr_botzo/ircbot/bot.py index 41e4c47..d71bccf 100644 --- a/dr_botzo/ircbot/bot.py +++ b/dr_botzo/ircbot/bot.py @@ -2,6 +2,7 @@ import bisect import collections +import copy import logging import re import ssl @@ -98,6 +99,8 @@ class DrReactor(irc.client.Reactor): log.debug("EVENT: e[%s] s[%s] t[%s] a[%s]", event.type, event.source, event.target, event.arguments) + self.try_recursion(connection, event) + # only do aliasing for pubmsg/privmsg if event.type in ['pubmsg', 'privmsg']: what = event.arguments[0] @@ -132,10 +135,138 @@ class DrReactor(irc.client.Reactor): self.handlers.get(event.type, []) ) for handler in matching_handlers: + log.debug(u"not-match") result = handler.callback(connection, event) if result == "NO MORE": return + def try_recursion(self, connection, event): + """Scan message for subcommands to execute and use as part of this command. + + Upon seeing a line intended for this module, see if there are subcommands + that we should do what is basically a text replacement on. The intent is to + allow things like the following: + + command arg1 [anothercommand arg1 arg2] + + where the output of anothercommand is command's arg2..n. + + Args: + connection source connection + event incoming event + + """ + + log.debug(u"RECURSING EVENT: e[%s] s[%s] t[%s] a[%s]", event.type, event.source, + event.target, event.arguments) + + try: + # begin recursion search + attempt = event.arguments[0] + + log.debug(u"checking it against %s", attempt) + + start_idx = attempt.find('[') + subcmd = attempt[start_idx+1:] + end_idx = subcmd.rfind(']') + subcmd = subcmd[:end_idx] + + if start_idx != -1 and end_idx != -1 and len(subcmd) > 0: + log.debug(u"subcmd: %s", subcmd) + + # found recursion candidate + # copy the event and see if IT has recursion to do + newevent = copy.deepcopy(event) + newevent.arguments[0] = subcmd + newevent._recursing = True + + log.debug(u"new event copied") + + self.try_recursion(connection, newevent) + + # now that we have a string that has been + # recursed all the way deeper into its text, see if any + # modules can do something with it. this calls the same + # event handlers in the same way as if this were a native + # event. + self.try_to_replace_event_text_with_module_text(connection, newevent) + + # we have done all we can do with the sub-event. whatever + # the text of that event now is, we should replace the parent + # event's [] section with it. + oldtext = event.arguments[0] + newtext = oldtext.replace('['+subcmd+']', newevent.arguments[0]) + log.debug(u"oldtext: '%s' newtext: '%s'", oldtext, newtext) + event.arguments[0] = newtext + + # we have now resolved the []. recursion will unfold, replacing + # it further and further, until we eventually get back to the + # original irc event in _handle_event, which will do one + # last search on the text. + else: + log.debug(u"no more recursion here") + except IndexError: + log.debug(u"no more recursion here") + + def try_to_replace_event_text_with_module_text(self, connection, event): + """Do something very similar to _handle_event, but for recursion. + + The intent here is that we replace [text] with whatever a module + provides to us. + + Args: + connection source connection + event incoming event + + """ + + replies = [] + + # only do aliasing for pubmsg/privmsg + if event.type in ['pubmsg', 'privmsg']: + what = event.arguments[0] + log.debug(u"checking for (recursion) alias for %s", what) + + for alias in Alias.objects.all(): + repl = alias.replace(what) + if repl: + # we found an alias for our given string, doing a replace + event.arguments[0] = repl + + with self.mutex: + # doing regex version first as it has the potential to be more specific + log.debug(u"checking (recursion) regex handlers for %s", event.type) + matching_handlers = sorted( + self.regex_handlers.get("all_events", []) + + self.regex_handlers.get(event.type, []) + ) + log.debug(u"got %d", len(matching_handlers)) + for handler in matching_handlers: + log.debug(u"checking (recursion) %s vs. %s", handler, event.arguments) + for line in event.arguments: + match = re.search(handler.regex, line) + if match: + log.debug(u"match (recursion)!") + result = handler.callback(connection, event, match) + log.debug(u"result: %s", result) + if result: + log.debug(u"appending %s to replies", result) + replies.append(result) + + matching_handlers = sorted( + self.handlers.get("all_events", []) + + self.handlers.get(event.type, []) + ) + for handler in matching_handlers: + log.debug(u"not-match (recursion)") + result = handler.callback(connection, event) + log.debug(u"result: %s", result) + if result: + log.debug(u"appending %s to replies", result) + replies.append(result) + + if len(replies): + event.arguments[0] = '\n'.join(replies) class IRCBot(irc.client.SimpleIRCClient): @@ -514,10 +645,13 @@ class IRCBot(irc.client.SimpleIRCClient): "NO MORE" to stop other event handlers from acting. """ + log.debug(u"in reply for e[%s] r[%s]", event, replystr) replypath = ircbotlib.reply_destination_for_event(event) + log.debug(u"replypath: %s", replypath) if replystr is not None: recursing = getattr(event, '_recursing', False) + log.debug("determined recursing to be %s", recursing) if recursing: return replystr else: