rewrite recursion/alias code for the 500th time.

more of a moving of the code, actually, it now exists in (an overridden)
_handle_event, so that recursions happen against irc events directly,
rather than an already partially interpreted object.

with this change, modules don't need to implement do() nor do we have a
need for the internal_bus, which was doing an additional walk of the
modules after the irc event was already handled and turned into text. now
the core event handler does the recursion scans.

to support this, we bring back the old replypath trick and use it again,
so we know when to send a privmsg reply and when to return text so that
it may be chained in recursion. this feels old hat by now, but if you
haven't been following along, you should really look at the diff.

that's the meat of the change. the rest is updating modules to use
self.reply() and reimplementing (un)register_handlers where appropriate
This commit is contained in:
Brian S. Stephan 2011-02-17 01:08:45 -06:00
parent 64df118c65
commit 2aa369add7
22 changed files with 208 additions and 116 deletions

View File

@ -17,6 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
import bisect import bisect
import copy
from ConfigParser import NoOptionError, NoSectionError from ConfigParser import NoOptionError, NoSectionError
import re import re
import signal import signal
@ -82,7 +83,6 @@ class DrBotIRC(irclib.IRC):
"""Implement a customized irclib IRC.""" """Implement a customized irclib IRC."""
modlist = [] modlist = []
internal_bus = []
config = None config = None
server = None server = None
@ -104,6 +104,31 @@ class DrBotIRC(irclib.IRC):
return self.server return self.server
def _handle_event(self, connection, event):
"""Override event handler to do recursion."""
self.try_recursion(connection, event)
self.try_alias(connection, event)
h = self.handlers
for handler in h.get("all_events", []) + h.get(event.eventtype(), []):
if handler[1](connection, event) == "NO MORE":
return
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.
"""
h = self.handlers
event._target = None
for handler in h.get("all_events", []) + h.get(event.eventtype(), []):
ret = handler[1](connection, event)
if ret:
event.arguments()[0] = ret
def on_pubmsg(self, connection, event): def on_pubmsg(self, connection, event):
"""See if there is an alias ("!command") in the text, and if so, translate it into """See if there is an alias ("!command") in the text, and if so, translate it into
an internal bot command and run it. an internal bot command and run it.
@ -156,11 +181,10 @@ class DrBotIRC(irclib.IRC):
except NoOptionError: pass except NoOptionError: pass
except NoSectionError: pass except NoSectionError: pass
return self.reply(connection, replypath, self.try_recursion(connection, event, nick, userhost, what, admin_unlocked)) def try_alias(self, connection, event):
def try_alias(self, connection, event, nick, userhost, what, admin_unlocked):
# try doing alias work # try doing alias work
try: try:
what = event.arguments()[0]
alias_list = self.config.options('Alias') alias_list = self.config.options('Alias')
for alias in alias_list: for alias in alias_list:
@ -168,19 +192,24 @@ class DrBotIRC(irclib.IRC):
if alias_re.search(what): if alias_re.search(what):
# we found an alias for our given string, doing a replace # we found an alias for our given string, doing a replace
command = re.sub(alias, self.config.get('Alias', alias), what) command = re.sub(alias, self.config.get('Alias', alias), what)
event.arguments()[0] = command
# now we have to check it for recursions again
self.try_recursion(connection, event)
# i guess someone could have an alias of an alias... try again # i guess someone could have an alias of an alias... try again
command = self.try_alias(connection, event, nick, userhost, command, admin_unlocked) return self.try_alias(connection, event)
return command
except NoOptionError: pass except NoOptionError: pass
except NoSectionError: pass except NoSectionError: pass
except IndexError: pass
# if we got here, there are no matching aliases, so return what we got # if we got here, there are no matching aliases, so return what we got
return what return event
def reply(self, connection, replypath, replystr, stop_responding=False): def reply(self, connection, event, replystr, stop_responding=False):
"""Reply over IRC to replypath or return a string with the reply.""" """Reply over IRC to replypath or return a string with the reply."""
replypath = event.target()
if replystr is not None: if replystr is not None:
if replypath is None: if replypath is None:
return replystr return replystr
@ -191,7 +220,7 @@ class DrBotIRC(irclib.IRC):
if stop_responding: if stop_responding:
return "NO MORE" return "NO MORE"
def try_recursion(self, connection, event, nick, userhost, what, admin_unlocked): def try_recursion(self, connection, event):
"""Scan message for subcommands to execute and use as part of this command. """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 Upon seeing a line intended for this module, see if there are subcommands
@ -203,44 +232,44 @@ class DrBotIRC(irclib.IRC):
where the output of anothercommand is command's arg2..n. where the output of anothercommand is command's arg2..n.
""" """
attempt = what try:
# begin recursion search
attempt = event.arguments()[0]
# check for aliases start_idx = attempt.find('[')
attempt = self.try_alias(connection, event, nick, userhost, attempt, admin_unlocked) subcmd = attempt[start_idx+1:]
end_idx = subcmd.rfind(']')
subcmd = subcmd[:end_idx]
# begin recursion search if start_idx != -1 and end_idx != -1 and len(subcmd) > 0:
# found recursion candidate
# copy the event and see if IT has recursion to do
newevent = copy.deepcopy(event)
newevent.arguments()[0] = subcmd
self.try_recursion(connection, newevent)
start_idx = attempt.find('[') # recursion over, check for aliases
subcmd = attempt[start_idx+1:] self.try_alias(connection, newevent)
end_idx = subcmd.rfind(']')
subcmd = subcmd[:end_idx]
if start_idx == -1 or end_idx == -1 or len(subcmd) == 0: # now that we have a string that has been de-aliased and
# no recursion, so see if there's a module to handle this # recursed all the way deeper into its text, see if any
return self.scan_modules(connection, event, nick, userhost, attempt, admin_unlocked) # modules can do something with it. this calls the same
else: # event handlers in the same way as if this were a native
# found recursion, search again # event.
ret = self.try_recursion(connection, event, nick, userhost, subcmd, admin_unlocked) self.try_to_replace_event_text_with_module_text(connection, newevent)
if ret is not None:
# recursion search had a hit, replace [foo] with it and re-recurse
return self.try_recursion(connection, event, nick, userhost, attempt.replace('['+subcmd+']', ret), admin_unlocked)
else:
# recursion search didn't have a hit, so see if there's a module to handle this
return self.scan_modules(connection, event, nick, userhost, attempt, admin_unlocked)
def scan_modules(self, connection, event, nick, userhost, attempt, admin_unlocked): # we have done all we can do with the sub-event. whatever
"""Walk the loaded modules, see if any reply to input text.""" # 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])
event.arguments()[0] = newtext
# aliases resolved. run result against each module # we have now resolved the []. recursion will unfold, replacing
for (priority, handler) in self.internal_bus: # it further and further, until we eventually get back to the
try: # original irc event in _handle_event, which will do one
ret = handler(connection, event, nick, userhost, attempt, admin_unlocked) # last search on the text.
if ret is not None: except IndexError: pass
# a module had a result for us, post-alias, so return it
# TODO: scan all modules with compounding results
return ret
except Exception as e:
print('EXCEPTION: ' + str(e))
def quit_irc(self, connection, msg): def quit_irc(self, connection, msg):
for module in self.modlist: for module in self.modlist:
@ -277,8 +306,7 @@ class DrBotIRC(irclib.IRC):
module = sys.modules[modstr] module = sys.modules[modstr]
botmod = eval('module.' + modname + '(self, self.config, self.server)') botmod = eval('module.' + modname + '(self, self.config, self.server)')
self.modlist.append(botmod) self.modlist.append(botmod)
bisect.insort(self.internal_bus, (botmod.priority(), botmod.do)) botmod.register_handlers()
botmod.register_handlers(self.server)
# might as well add it to the list # might as well add it to the list
modset = set(self.config.get('dr.botzo', 'module_list').split(',')) modset = set(self.config.get('dr.botzo', 'module_list').split(','))
@ -301,7 +329,6 @@ class DrBotIRC(irclib.IRC):
# remove module references # remove module references
self.modlist.remove(module) self.modlist.remove(module)
self.internal_bus.remove((module.priority(), module.do))
module.unregister_handlers() module.unregister_handlers()
# del it # del it

View File

@ -57,27 +57,80 @@ class Module(object):
# print what was loaded, for debugging # print what was loaded, for debugging
print("loaded " + self.__class__.__name__) print("loaded " + self.__class__.__name__)
def register_handlers(self, server): def register_handlers(self):
"""Hook handler functions into the IRC library. This is called when the """Hook handler functions into the IRC library. This is called when the
module is loaded. Classes with special stuff to do could implement this module is loaded. Classes with special stuff to do could implement this
and set up the appropriate handlers, e.g.: and set up the appropriate handlers, e.g.:
self.server.add_global_handler('welcome', self.on_connect) self.server.add_global_handler('welcome', self.on_connect)
Unless you're sure what you're doing, don't register pubmsg or privmsg. By default, a module attaches to pubmsg/privmsg, which sets up some common
They are handled by the alias support in DrBotIRC, and will call your variables and then calls do(). You are free to implement do() (see below),
module's do(). or override this and do whatever you want.
""" """
self.server.add_global_handler('pubmsg', self.on_pub_or_privmsg, self.priority())
self.server.add_global_handler('privmsg', self.on_pub_or_privmsg, self.priority())
def unregister_handlers(self): def unregister_handlers(self):
"""Unhook handler functions from the IRC library. Inverse of the above. """Unhook handler functions from the IRC library. Inverse of the above.
This is called by reload, to remove the soon-to-be old object from the server This is called by reload, to remove the soon-to-be old object from the server
global handlers (or whatever has been added via register_handlers). Classes global handlers (or whatever has been added via register_handlers). Classes
inheriting from Module could implement this, e.g.: inheriting from Module could reimplement this, e.g.:
self.server.remove_global_handler('welcome', self.on_connect) self.server.remove_global_handler('welcome', self.on_connect)
""" """
self.server.remove_global_handler('pubmsg', self.on_pub_or_privmsg)
self.server.remove_global_handler('privmsg', self.on_pub_or_privmsg)
def on_pub_or_privmsg(self, connection, event):
"""Do a default thing on a pubmsg or privmsg.
Sets up a couple variables and then calls do(), which by default we
expect implementers to implement.
"""
nick = irclib.nm_to_n(event.source())
userhost = irclib.nm_to_uh(event.source())
replypath = event.target()
what = event.arguments()[0]
admin_unlocked = False
# privmsg
if replypath == connection.get_nickname():
replypath = nick
try:
if userhost == self.config.get('dr.botzo', 'admin_userhost'):
admin_unlocked = True
except NoOptionError: pass
return self.do(connection, event, nick, userhost, what, admin_unlocked)
def reply(self, connection, event, replystr, stop_responding=False):
"""Reply over IRC to replypath or return a string with the reply.
The primary utility for this is to properly handle recursion. The recursion
code in DrBotIRC will set up a couple hints that this method picks up on and
will appropriately send an IRC event or return a string.
Unless you know what you are doing, the modules you write should return
this method rather than send a privmsg reply, as failing to call this
method will certainly have recursion do odd things with your module.
"""
replypath = event.target()
if replystr is not None:
if replypath is None:
return replystr
else:
replies = replystr.split('\n')
for reply in replies:
connection.privmsg(replypath, reply)
if stop_responding:
return "NO MORE"
def save(self): def save(self):
"""Save whatever the module may need to save. Sync files, etc. """Save whatever the module may need to save. Sync files, etc.

View File

@ -44,7 +44,7 @@ class Countdown(Module):
self.config.set(self.__class__.__name__, item, target.astimezone(tzutc()).isoformat()) self.config.set(self.__class__.__name__, item, target.astimezone(tzutc()).isoformat())
replystr = 'added countdown item ' + item replystr = 'added countdown item ' + item
return replystr return self.reply(connection, event, replystr)
match = re.search('^!countdown\s+remove\s+(\S+)$', what) match = re.search('^!countdown\s+remove\s+(\S+)$', what)
if match: if match:
@ -52,7 +52,7 @@ class Countdown(Module):
item = match.group(1) item = match.group(1)
if self.config.remove_option(self.__class__.__name__, item): if self.config.remove_option(self.__class__.__name__, item):
replystr = 'removed countdown item ' + item replystr = 'removed countdown item ' + item
return replystr return self.reply(connection, event, replystr)
except NoSectionError: pass except NoSectionError: pass
match = re.search('^!countdown\s+list$', what) match = re.search('^!countdown\s+list$', what)
@ -62,7 +62,7 @@ class Countdown(Module):
self.remove_metaoptions(cdlist) self.remove_metaoptions(cdlist)
cdlist.sort() cdlist.sort()
liststr = ', '.join(cdlist) liststr = ', '.join(cdlist)
return liststr return self.reply(connection, event, liststr)
except NoSectionError: pass except NoSectionError: pass
match = re.search('^!countdown\s+(\S+)$', what) match = re.search('^!countdown\s+(\S+)$', what)
@ -103,7 +103,7 @@ class Countdown(Module):
if rdelta.seconds > 1: if rdelta.seconds > 1:
relstr += 's' relstr += 's'
relstr += ', ' relstr += ', '
return relstr[0:-2] return self.reply(connection, event, relstr[0:-2])
except NoOptionError: pass except NoOptionError: pass
# vi:tabstop=4:expandtab:autoindent # vi:tabstop=4:expandtab:autoindent

View File

@ -268,7 +268,7 @@ class Dice(Module):
yacc.parse(dicestr) yacc.parse(dicestr)
reply = self.get_result() reply = self.get_result()
if reply is not "": if reply is not "":
return nick + ': ' + reply return self.reply(connection, event, nick + ': ' + reply)
match = re.search('^!ctech\s+(.*)$', what) match = re.search('^!ctech\s+(.*)$', what)
if match: if match:
@ -373,7 +373,7 @@ class Dice(Module):
if count is not len(rollitrs)-1: if count is not len(rollitrs)-1:
reply += "; " reply += "; "
if reply is not "": if reply is not "":
return nick + ': ' + reply return self.reply(connection, event, nick + ': ' + reply)
# vi:tabstop=4:expandtab:autoindent # vi:tabstop=4:expandtab:autoindent
# kate: indent-mode python;indent-width 4;replace-tabs on; # kate: indent-mode python;indent-width 4;replace-tabs on;

View File

@ -31,7 +31,7 @@ class Echo(Module):
match = re.search('^!echo\s+(.*)$', what) match = re.search('^!echo\s+(.*)$', what)
if match: if match:
return match.group(1) return self.reply(connection, event, match.group(1))
# vi:tabstop=4:expandtab:autoindent # vi:tabstop=4:expandtab:autoindent
# kate: indent-mode python;indent-width 4;replace-tabs on; # kate: indent-mode python;indent-width 4;replace-tabs on;

View File

@ -63,7 +63,7 @@ class EightBall(Module):
match = re.search('^!8ball', what) match = re.search('^!8ball', what)
if match: if match:
response = self.responses[random.randint(1,len(self.responses))-1] response = self.responses[random.randint(1,len(self.responses))-1]
return response return self.reply(connection, event, response)
# vi:tabstop=4:expandtab:autoindent # vi:tabstop=4:expandtab:autoindent
# kate: indent-mode python;indent-width 4;replace-tabs on; # kate: indent-mode python;indent-width 4;replace-tabs on;

View File

@ -53,7 +53,7 @@ class FactFile(Module):
to_print = facts[random.randint(1, len(facts))-1] to_print = facts[random.randint(1, len(facts))-1]
# return text # return text
return to_print.rstrip() return self.reply(connection, event, to_print.rstrip())
else: else:
print('filename in config file for \'' + whats[0] + '\' is wrong') print('filename in config file for \'' + whats[0] + '\' is wrong')

View File

@ -70,7 +70,7 @@ class Facts(Module):
cur.execute('''INSERT INTO facts_facts (category, fact, who, userhost) cur.execute('''INSERT INTO facts_facts (category, fact, who, userhost)
VALUES (?, ?, ?, ?)''', (category, fact, nick, userhost)) VALUES (?, ?, ?, ?)''', (category, fact, nick, userhost))
db.commit() db.commit()
return category + ' added.' return self.reply(connection, event, category + ' added.')
match = re.search('^!facts\s+(\S+)\s+(.*)$', what) match = re.search('^!facts\s+(\S+)\s+(.*)$', what)
if match: if match:
@ -80,7 +80,7 @@ class Facts(Module):
facts = category_facts.fetchall() facts = category_facts.fetchall()
if len(facts) > 0: if len(facts) > 0:
fact = facts[random.randint(1,len(facts))-1] fact = facts[random.randint(1,len(facts))-1]
return fact['fact'].rstrip().encode('utf-8', 'ignore') return self.reply(connection, event, fact['fact'].rstrip().encode('utf-8', 'ignore'))
match = re.search('^!facts\s+(\S+)$', what) match = re.search('^!facts\s+(\S+)$', what)
if match: if match:
@ -89,10 +89,10 @@ class Facts(Module):
facts = category_facts.fetchall() facts = category_facts.fetchall()
if len(facts) > 0: if len(facts) > 0:
fact = facts[random.randint(1,len(facts))-1] fact = facts[random.randint(1,len(facts))-1]
return fact['fact'].rstrip().encode('utf-8', 'ignore') return self.reply(connection, event, fact['fact'].rstrip().encode('utf-8', 'ignore'))
except sqlite3.Error as e: except sqlite3.Error as e:
return "sqlite error: " + str(e) return self.reply(connection, event, "sqlite error: " + str(e))
# vi:tabstop=4:expandtab:autoindent # vi:tabstop=4:expandtab:autoindent
# kate: indent-mode python;indent-width 4;replace-tabs on; # kate: indent-mode python;indent-width 4;replace-tabs on;

View File

@ -61,7 +61,7 @@ class GoogleTranslate(Module):
translation = translation.replace('\\u0026gt;', '>') translation = translation.replace('\\u0026gt;', '>')
translation = translation.replace('\\u0026#39;', '\'') translation = translation.replace('\\u0026#39;', '\'')
return translation return self.reply(connection, event, translation)
# vi:tabstop=4:expandtab:autoindent # vi:tabstop=4:expandtab:autoindent
# kate: indent-mode python;indent-width 4;replace-tabs on; # kate: indent-mode python;indent-width 4;replace-tabs on;

View File

@ -43,11 +43,11 @@ class Help(Module):
if match: if match:
(module, key) = match.group(2,4) (module, key) = match.group(2,4)
if (module == None): if (module == None):
return self.handle_help_descriptions() return self.reply(connection, event, self.handle_help_descriptions())
elif (key == None): elif (key == None):
return self.handle_help_module(module) return self.reply(connection, event, self.handle_help_module(module))
else: else:
return self.handle_help_detail(module, key) return self.reply(connection, event, self.handle_help_detail(module, key))
def handle_help_descriptions(self): def handle_help_descriptions(self):
"""print the general help""" """print the general help"""

View File

@ -28,10 +28,14 @@ class IrcAdmin(Module):
"""Support miscellaneous IRC stuff --- joining channels, changing the nick, etc.""" """Support miscellaneous IRC stuff --- joining channels, changing the nick, etc."""
def register_handlers(self, server): def register_handlers(self):
server.add_global_handler('welcome', self.on_connect, self.priority()) self.server.add_global_handler('pubmsg', self.on_pub_or_privmsg, self.priority())
self.server.add_global_handler('privmsg', self.on_pub_or_privmsg, self.priority())
self.server.add_global_handler('welcome', self.on_connect, self.priority())
def unregister_handlers(self): def unregister_handlers(self):
self.server.remove_global_handler('pubmsg', self.on_pub_or_privmsg)
self.server.remove_global_handler('privmsg', self.on_pub_or_privmsg)
self.server.remove_global_handler('welcome', self.on_connect) self.server.remove_global_handler('welcome', self.on_connect)
def on_connect(self, connection, event): def on_connect(self, connection, event):
@ -60,27 +64,27 @@ class IrcAdmin(Module):
whats = what.split(' ') whats = what.split(' ')
if whats[0] == '!join' and admin_unlocked and len(whats) >= 2: if whats[0] == '!join' and admin_unlocked and len(whats) >= 2:
return self.sub_join_channel(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.sub_join_channel(connection, event, nick, userhost, what, admin_unlocked))
elif whats[0] == '!part' and admin_unlocked and len(whats) >= 2: elif whats[0] == '!part' and admin_unlocked and len(whats) >= 2:
return self.sub_part_channel(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.sub_part_channel(connection, event, nick, userhost, what, admin_unlocked))
elif whats[0] == '!quit' and admin_unlocked: elif whats[0] == '!quit' and admin_unlocked:
return self.sub_quit_irc(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.sub_quit_irc(connection, event, nick, userhost, what, admin_unlocked))
elif whats[0] == '!autojoin' and admin_unlocked and len(whats) >= 3: elif whats[0] == '!autojoin' and admin_unlocked and len(whats) >= 3:
return self.sub_autojoin_manipulate(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.sub_autojoin_manipulate(connection, event, nick, userhost, what, admin_unlocked))
elif whats[0] == '!save' and admin_unlocked: elif whats[0] == '!save' and admin_unlocked:
self.irc.save_modules() self.irc.save_modules()
self.irc.save_config() self.irc.save_config()
return 'Saved.' return self.reply(connection, event, 'Saved.')
elif whats[0] == '!nick' and admin_unlocked and len(whats) >= 2: elif whats[0] == '!nick' and admin_unlocked and len(whats) >= 2:
return self.sub_change_nick(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.sub_change_nick(connection, event, nick, userhost, what, admin_unlocked))
elif whats[0] == '!load' and admin_unlocked and len(whats) >= 2: elif whats[0] == '!load' and admin_unlocked and len(whats) >= 2:
return self.sub_load_module(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.sub_load_module(connection, event, nick, userhost, what, admin_unlocked))
elif whats[0] == '!reload' and admin_unlocked and len(whats) >= 2: elif whats[0] == '!reload' and admin_unlocked and len(whats) >= 2:
return self.sub_reload_module(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.sub_reload_module(connection, event, nick, userhost, what, admin_unlocked))
elif whats[0] == '!unload' and admin_unlocked and len(whats) >= 2: elif whats[0] == '!unload' and admin_unlocked and len(whats) >= 2:
return self.sub_unload_module(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.sub_unload_module(connection, event, nick, userhost, what, admin_unlocked))
elif whats[0] == '!modules': elif whats[0] == '!modules':
return self.sub_list_modules(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.sub_list_modules(connection, event, nick, userhost, what, admin_unlocked))
def sub_join_channel(self, connection, event, nick, userhost, what, admin_unlocked): def sub_join_channel(self, connection, event, nick, userhost, what, admin_unlocked):
whats = what.split(' ') whats = what.split(' ')

View File

@ -98,13 +98,13 @@ class Karma(Module):
"""look for karma strings at the start of messages""" """look for karma strings at the start of messages"""
if (self.karmare.search(what)): if (self.karmare.search(what)):
return self.handle_karma_change(connection, nick, userhost, what) return self.reply(connection, event, self.handle_karma_change(connection, nick, userhost, what))
elif (self.queryre.search(what)): elif (self.queryre.search(what)):
return self.handle_karma_query(connection, nick, userhost, what) return self.reply(connection, event, self.handle_karma_query(connection, nick, userhost, what))
elif (self.statre.search(what)): elif (self.statre.search(what)):
return self.handle_stat_query(connection, nick, userhost, what) return self.reply(connection, event, self.handle_stat_query(connection, nick, userhost, what))
elif (self.reportre.search(what)): elif (self.reportre.search(what)):
return self.handle_report_query(connection, nick, userhost, what) return self.reply(connection, event, self.handle_report_query(connection, nick, userhost, what))
def handle_karma_change(self, connection, nick, userhost, what): def handle_karma_change(self, connection, nick, userhost, what):
""" """

View File

@ -68,13 +68,17 @@ class Markov(Module):
self.brain = {} self.brain = {}
self.brain.setdefault((self.start1, self.start2), []).append(self.stop) self.brain.setdefault((self.start1, self.start2), []).append(self.stop)
def register_handlers(self, server): def register_handlers(self):
"""Handle pubmsg/privmsg, to learn and/or reply to IRC events.""" """Handle pubmsg/privmsg, to learn and/or reply to IRC events."""
self.server.add_global_handler('pubmsg', self.on_pub_or_privmsg, self.priority())
self.server.add_global_handler('privmsg', self.on_pub_or_privmsg, self.priority())
self.server.add_global_handler('pubmsg', self.learn_from_irc_event) self.server.add_global_handler('pubmsg', self.learn_from_irc_event)
self.server.add_global_handler('privmsg', self.learn_from_irc_event) self.server.add_global_handler('privmsg', self.learn_from_irc_event)
def unregister_handlers(self): def unregister_handlers(self):
self.server.remove_global_handler('pubmsg', self.on_pub_or_privmsg)
self.server.remove_global_handler('privmsg', self.on_pub_or_privmsg)
self.server.remove_global_handler('pubmsg', self.learn_from_irc_event) self.server.remove_global_handler('pubmsg', self.learn_from_irc_event)
self.server.remove_global_handler('privmsg', self.learn_from_irc_event) self.server.remove_global_handler('privmsg', self.learn_from_irc_event)
@ -100,11 +104,11 @@ class Markov(Module):
"""Handle commands and inputs.""" """Handle commands and inputs."""
if self.trainre.search(what): if self.trainre.search(what):
return self.markov_train(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.markov_train(connection, event, nick, userhost, what, admin_unlocked))
elif self.learnre.search(what): elif self.learnre.search(what):
return self.markov_learn(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.markov_learn(connection, event, nick, userhost, what, admin_unlocked))
elif self.replyre.search(what): elif self.replyre.search(what):
return self.markov_reply(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.markov_reply(connection, event, nick, userhost, what, admin_unlocked))
# not a command, so see if i'm being mentioned # not a command, so see if i'm being mentioned
if re.search(connection.get_nickname(), what, re.IGNORECASE) is not None: if re.search(connection.get_nickname(), what, re.IGNORECASE) is not None:
@ -112,10 +116,10 @@ class Markov(Module):
addressed_re = re.compile(addressed_pattern) addressed_re = re.compile(addressed_pattern)
if addressed_re.match(what): if addressed_re.match(what):
# i was addressed directly, so respond, addressing the speaker # i was addressed directly, so respond, addressing the speaker
return '{0:s}: {1:s}'.format(nick, self._reply_to_line(addressed_re.match(what).group(1))) return self.reply(connection, event, '{0:s}: {1:s}'.format(nick, self._reply_to_line(addressed_re.match(what).group(1))))
else: else:
# i wasn't addressed directly, so just respond # i wasn't addressed directly, so just respond
return '{0:s}'.format(self._reply_to_line(what)) return self.reply(connection, event, '{0:s}'.format(self._reply_to_line(what)))
def markov_train(self, connection, event, nick, userhost, what, admin_unlocked): def markov_train(self, connection, event, nick, userhost, what, admin_unlocked):
"""Learn lines from a file. Good for initializing a brain.""" """Learn lines from a file. Good for initializing a brain."""

View File

@ -39,13 +39,17 @@ class MegaHAL(Module):
mh_python.initbrain() mh_python.initbrain()
def register_handlers(self, server): def register_handlers(self):
"""Handle pubmsg/privmsg, so we can learn only from IRC events.""" """Handle pubmsg/privmsg, so we can learn only from IRC events."""
self.server.add_global_handler('pubmsg', self.on_pub_or_privmsg, self.priority())
self.server.add_global_handler('privmsg', self.on_pub_or_privmsg, self.priority())
self.server.add_global_handler('pubmsg', self.learn_from_irc_event) self.server.add_global_handler('pubmsg', self.learn_from_irc_event)
self.server.add_global_handler('privmsg', self.learn_from_irc_event) self.server.add_global_handler('privmsg', self.learn_from_irc_event)
def unregister_handlers(self): def unregister_handlers(self):
self.server.remove_global_handler('pubmsg', self.on_pub_or_privmsg)
self.server.remove_global_handler('privmsg', self.on_pub_or_privmsg)
self.server.remove_global_handler('pubmsg', self.learn_from_irc_event) self.server.remove_global_handler('pubmsg', self.learn_from_irc_event)
self.server.remove_global_handler('privmsg', self.learn_from_irc_event) self.server.remove_global_handler('privmsg', self.learn_from_irc_event)
@ -81,7 +85,7 @@ class MegaHAL(Module):
if reply is not "": if reply is not "":
if append_nick: if append_nick:
reply = nick + ": " + reply reply = nick + ": " + reply
return reply return self.reply(connection, event, reply)
# vi:tabstop=4:expandtab:autoindent # vi:tabstop=4:expandtab:autoindent
# kate: indent-mode python;indent-width 4;replace-tabs on; # kate: indent-mode python;indent-width 4;replace-tabs on;

View File

@ -98,9 +98,9 @@ class Pi(Module):
db.commit() db.commit()
except sqlite3.Error as e: except sqlite3.Error as e:
db.rollback() db.rollback()
return "sqlite error: " + str(e) return self.reply(connection, event, "sqlite error: " + str(e))
return "({0:.10f}, {1:.10f}) is {2}within the circle. pi is {5:.10f}. (i:{3:d} p:{4:d})".format(x, y, "" if inside else "not ", count_inside, count, pi) return self.reply(connection, event, "({0:.10f}, {1:.10f}) is {2}within the circle. pi is {5:.10f}. (i:{3:d} p:{4:d})".format(x, y, "" if inside else "not ", count_inside, count, pi))
# vi:tabstop=4:expandtab:autoindent # vi:tabstop=4:expandtab:autoindent
# kate: indent-mode python;indent-width 4;replace-tabs on; # kate: indent-mode python;indent-width 4;replace-tabs on;

View File

@ -44,7 +44,7 @@ class Seen(Module):
seendata = self.config.get(self.__class__.__name__, query).split('|:|') seendata = self.config.get(self.__class__.__name__, query).split('|:|')
converted = datetime.strptime(seendata[1], "%Y-%m-%dT%H:%M:%S.%f").replace(tzinfo=tzutc()) converted = datetime.strptime(seendata[1], "%Y-%m-%dT%H:%M:%S.%f").replace(tzinfo=tzutc())
replystr = 'last saw ' + query + ' at ' + converted.astimezone(tzlocal()).strftime("%Y/%m/%d %H:%M:%S %Z") + ' saying \'' + seendata[2] + '\'' replystr = 'last saw ' + query + ' at ' + converted.astimezone(tzlocal()).strftime("%Y/%m/%d %H:%M:%S %Z") + ' saying \'' + seendata[2] + '\''
return replystr return self.reply(connection, event, replystr)
except NoOptionError: pass except NoOptionError: pass
# vi:tabstop=4:expandtab:autoindent # vi:tabstop=4:expandtab:autoindent

View File

@ -162,21 +162,21 @@ class Storycraft(Module):
"""Pass storycraft control commands to the appropriate method based on input.""" """Pass storycraft control commands to the appropriate method based on input."""
if self.statusre.search(what): if self.statusre.search(what):
return self.storycraft_status(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.storycraft_status(connection, event, nick, userhost, what, admin_unlocked))
elif self.newgamere.search(what): elif self.newgamere.search(what):
return self.storycraft_newgame(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.storycraft_newgame(connection, event, nick, userhost, what, admin_unlocked))
elif self.joingamere.search(what): elif self.joingamere.search(what):
return self.storycraft_joingame(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.storycraft_joingame(connection, event, nick, userhost, what, admin_unlocked))
elif self.listgamesre.search(what): elif self.listgamesre.search(what):
return self.storycraft_listgames(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.storycraft_listgames(connection, event, nick, userhost, what, admin_unlocked))
elif self.startgamere.search(what): elif self.startgamere.search(what):
return self.storycraft_startgame(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.storycraft_startgame(connection, event, nick, userhost, what, admin_unlocked))
elif self.showlinere.search(what): elif self.showlinere.search(what):
return self.storycraft_showline(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.storycraft_showline(connection, event, nick, userhost, what, admin_unlocked))
elif self.addlinere.search(what): elif self.addlinere.search(what):
return self.storycraft_addline(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.storycraft_addline(connection, event, nick, userhost, what, admin_unlocked))
elif self.exportre.search(what): elif self.exportre.search(what):
return self.storycraft_export(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.storycraft_export(connection, event, nick, userhost, what, admin_unlocked))
def storycraft_status(self, connection, event, nick, userhost, what, admin_unlocked): def storycraft_status(self, connection, event, nick, userhost, what, admin_unlocked):
"""Print information about the storycraft games, or one specific game.""" """Print information about the storycraft games, or one specific game."""

View File

@ -37,11 +37,11 @@ class TextTransform(Module):
reply = [''] reply = ['']
if self.rot13(what, reply): if self.rot13(what, reply):
return reply[0] return self.reply(connection, event, reply[0])
elif self.base64(what, reply): elif self.base64(what, reply):
return reply[0] return self.reply(connection, event, reply[0])
elif self.upper(what, reply): elif self.upper(what, reply):
return reply[0] return self.reply(connection, event, reply[0])
def rot13(self, what, reply): def rot13(self, what, reply):
""" """

View File

@ -36,7 +36,7 @@ class Trigger(Module):
for trigger in trigger_list: for trigger in trigger_list:
if re.search(trigger, what) is not None: if re.search(trigger, what) is not None:
output = self.config.get(self.__class__.__name__, trigger) output = self.config.get(self.__class__.__name__, trigger)
return output return self.reply(connection, event, output)
except NoOptionError: pass except NoOptionError: pass
except NoSectionError: pass except NoSectionError: pass

View File

@ -99,17 +99,17 @@ class Twitter(Module):
"""Attempt to do twitter things.""" """Attempt to do twitter things."""
if self.getstatusre.search(what): if self.getstatusre.search(what):
return self.twitter_getstatus(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.twitter_getstatus(connection, event, nick, userhost, what, admin_unlocked))
elif self.getuserstatusre.search(what): elif self.getuserstatusre.search(what):
return self.twitter_getuserstatus(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.twitter_getuserstatus(connection, event, nick, userhost, what, admin_unlocked))
elif self.tweetre.search(what): elif self.tweetre.search(what):
return self.twitter_tweet(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.twitter_tweet(connection, event, nick, userhost, what, admin_unlocked))
elif self.replytore.search(what): elif self.replytore.search(what):
return self.twitter_replyto(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.twitter_replyto(connection, event, nick, userhost, what, admin_unlocked))
elif self.gettokenre.search(what): elif self.gettokenre.search(what):
return self.twitter_gettoken(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.twitter_gettoken(connection, event, nick, userhost, what, admin_unlocked))
elif self.authre.search(what): elif self.authre.search(what):
return self.twitter_auth(connection, event, nick, userhost, what, admin_unlocked) return self.reply(connection, event, self.twitter_auth(connection, event, nick, userhost, what, admin_unlocked))
def twitter_getstatus(self, connection, event, nick, userhost, what, admin_unlocked): def twitter_getstatus(self, connection, event, nick, userhost, what, admin_unlocked):
"""Get a status by tweet ID.""" """Get a status by tweet ID."""

View File

@ -45,7 +45,7 @@ class Urls(Module):
urlstr = ' '.join(whats[2:]) + "\n" urlstr = ' '.join(whats[2:]) + "\n"
file.write(urlstr) file.write(urlstr)
return 'added url' return self.reply(connection, event, 'added url')
# default: get a random url # default: get a random url
@ -64,7 +64,7 @@ class Urls(Module):
to_print = urls[random.randint(1, len(urls))-1] to_print = urls[random.randint(1, len(urls))-1]
# return text # return text
return to_print.rstrip() return self.reply(connection, event, to_print.rstrip())
else: else:
print('filename in config file for urls is wrong') print('filename in config file for urls is wrong')
except NoOptionError: pass except NoOptionError: pass

View File

@ -62,11 +62,11 @@ class Weather(Module):
for city in google_weather['forecasts']: for city in google_weather['forecasts']:
weatherstr += " " + city['day_of_week'].encode('utf-8') + ": " + city['condition'].encode('utf-8') + ". High " + city['high'].encode('utf-8') + "°F, Low " + city['low'].encode('utf-8') + "°F." weatherstr += " " + city['day_of_week'].encode('utf-8') + ": " + city['condition'].encode('utf-8') + ". High " + city['high'].encode('utf-8') + "°F, Low " + city['low'].encode('utf-8') + "°F."
return weatherstr return self.reply(connection, event, weatherstr)
except URLError as e: except URLError as e:
return "error connecting to google weather:" + str(e) return self.reply(connection, event, "error connecting to google weather:" + str(e))
except IndexError as e: except IndexError as e:
return "error in pywapi: " + str(e) return self.reply(connection, event, "error in pywapi: " + str(e))
# vi:tabstop=4:expandtab:autoindent # vi:tabstop=4:expandtab:autoindent
# kate: indent-mode python;indent-width 4;replace-tabs on; # kate: indent-mode python;indent-width 4;replace-tabs on;