ircbot: attempt to bring recursion back

so initial tests suggest this is working well, but it's not exactly the
clearest code, so let's call this a rough proof of concept of recursion
in the bot
This commit is contained in:
Brian S. Stephan 2015-05-15 18:20:59 -05:00
parent 115e82f0fc
commit 5716e285b6
1 changed files with 134 additions and 0 deletions

View File

@ -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: