diff --git a/DrBotIRC.py b/DrBotIRC.py index 401e3ac..a5078f2 100644 --- a/DrBotIRC.py +++ b/DrBotIRC.py @@ -527,6 +527,15 @@ class DrBotIRC(irclib.IRC): def reply(self, event, replystr, stop=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 use + this method rather than send a privmsg reply, as failing to call this + method will certainly have recursion do odd things with your module. + Args: event incoming event replystr the message to reply with diff --git a/Module.py b/Module.py index 191c647..62d0c6f 100644 --- a/Module.py +++ b/Module.py @@ -121,46 +121,6 @@ class Module(object): return self.do(connection, event, nick, userhost, what, admin_unlocked) - def reply(self, connection, event, replystr, stop=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. - - Args: - connection source connection - event incoming event - replystr the message to reply with - stop whether or not to let other handlers see this - - Returns: - The replystr if the event is inside recursion, or, potentially, - "NO MORE" to stop other event handlers from acting. - - """ - - replypath = event.target() - - # check for privmsg - if replypath == connection.get_nickname(): - replypath = irclib.nm_to_n(event.source()) - - if replystr is not None: - if event._recursing: - return replystr - else: - replies = replystr.split('\n') - for reply in replies: - connection.privmsg(replypath, reply) - if stop: - return "NO MORE" - def sendmsg(self, connection, target, msg): """Send a privmsg over IRC to target. diff --git a/modules/Achievements.py b/modules/Achievements.py index 5b39dae..7a94dc6 100644 --- a/modules/Achievements.py +++ b/modules/Achievements.py @@ -206,21 +206,21 @@ class Achievements(Module): """Do stuff when commanded.""" if self.joinre.search(what): - return self.reply(connection, event, self._join_system(nick)) + return self.irc.reply(event, self._join_system(nick)) elif self.leavere.search(what): - return self.reply(connection, event, self._leave_system(nick)) + return self.irc.reply(event, self._leave_system(nick)) elif self.infore.search(what): match = self.infore.search(what) achievement = match.group(1) desc = self._get_achievement_info(achievement) if desc: - return self.reply(connection, event, achievement + ': ' + desc) + return self.irc.reply(event, achievement + ': ' + desc) elif self.rankre.search(what): match = self.rankre.search(what) player = match.group(1) achievements = self._get_player_achievements(player) if len(achievements): - return self.reply(connection, event, player + ' has obtained ' + ', '.join(achievements)) + return self.irc.reply(event, player + ' has obtained ' + ', '.join(achievements)) def thread_do(self): """Do the scan for achievements and other miscellaneous tasks.""" diff --git a/modules/Acro.py b/modules/Acro.py index 2243095..64c9f0d 100644 --- a/modules/Acro.py +++ b/modules/Acro.py @@ -93,17 +93,17 @@ class Acro(Module): target = event.target() if self.statusre.search(what): - return self.reply(connection, event, self.do_status(what)) + return self.irc.reply(event, self.do_status(what)) if self.startre.search(what): - return self.reply(connection, event, self.do_start(connection, target, what)) + return self.irc.reply(event, self.do_start(connection, target, what)) if self.submitre.search(what): match = self.submitre.search(what) - return self.reply(connection, event, self.do_submit(nick, match.group(1))) + return self.irc.reply(event, self.do_submit(nick, match.group(1))) if self.votere.search(what): match = self.votere.search(what) - return self.reply(connection, event, self.do_vote(nick, match.group(1))) + return self.irc.reply(event, self.do_vote(nick, match.group(1))) if self.quitre.search(what): - return self.reply(connection, event, self.do_quit(what)) + return self.irc.reply(event, self.do_quit(what)) def do_status(self, what): """Return the status of the currently-running game, if there is one.""" diff --git a/modules/Babelfish.py b/modules/Babelfish.py index 66b36a4..9c453a3 100644 --- a/modules/Babelfish.py +++ b/modules/Babelfish.py @@ -38,7 +38,7 @@ class Babelfish(Module): tolang = match.group(2) text = match.group(3) - return self.reply(connection, event, self.translate(fromlang, tolang, text)) + return self.irc.reply(event, self.translate(fromlang, tolang, text)) def translate(self, fromlang, tolang, text): diff --git a/modules/Countdown.py b/modules/Countdown.py index c1356ef..ba727d6 100644 --- a/modules/Countdown.py +++ b/modules/Countdown.py @@ -44,11 +44,11 @@ class Countdown(Module): self.config.set(self.__class__.__name__, item, target.astimezone(tzutc()).isoformat()) replystr = 'added countdown item ' + item - return self.reply(connection, event, replystr) + return self.irc.reply(event, replystr) except ValueError as e: self.log.error("could not parse countdown item") self.log.exception(e) - return self.reply(connection, event, + return self.irc.reply(event, "could not parse countdown item: {0:s}".format(str(e))) match = re.search('^!countdown\s+remove\s+(\S+)$', what) @@ -57,7 +57,7 @@ class Countdown(Module): item = match.group(1) if self.config.remove_option(self.__class__.__name__, item): replystr = 'removed countdown item ' + item - return self.reply(connection, event, replystr) + return self.irc.reply(event, replystr) except NoSectionError: pass match = re.search('^!countdown\s+list$', what) @@ -67,7 +67,7 @@ class Countdown(Module): self.remove_metaoptions(cdlist) cdlist.sort() liststr = ', '.join(cdlist) - return self.reply(connection, event, liststr) + return self.irc.reply(event, liststr) except NoSectionError: pass match = re.search('^!countdown\s+(\S+)$', what) @@ -108,7 +108,7 @@ class Countdown(Module): if rdelta.seconds > 1: relstr += 's' relstr += ', ' - return self.reply(connection, event, relstr[0:-2]) + return self.irc.reply(event, relstr[0:-2]) except NoOptionError: pass # vi:tabstop=4:expandtab:autoindent diff --git a/modules/Dice.py b/modules/Dice.py index 573c85b..373756c 100644 --- a/modules/Dice.py +++ b/modules/Dice.py @@ -292,7 +292,7 @@ class Dice(Module): dicestr = match.group(1) reply = nick + ": " + self.do_roll(dicestr) # irc format it - return self.reply(connection, event, + return self.irc.reply(event, re.sub(r"(\d+)(.*?\s+)(\(.*?\))", r"\1\214\3", reply)) match = re.search('!ctech\s+(.*)$', what) @@ -394,7 +394,7 @@ class Dice(Module): if count is not len(rollitrs)-1: reply += "; " if reply is not "": - return self.reply(connection, event, nick + ': ' + reply) + return self.irc.reply(event, nick + ': ' + reply) # vi:tabstop=4:expandtab:autoindent # kate: indent-mode python;indent-width 4;replace-tabs on; diff --git a/modules/Echo.py b/modules/Echo.py index 925e1ca..aa7e60a 100644 --- a/modules/Echo.py +++ b/modules/Echo.py @@ -29,7 +29,7 @@ class Echo(Module): match = re.search('^!echo\s+(.*)$', what) if match: - return self.reply(connection, event, match.group(1)) + return self.irc.reply(event, match.group(1)) # vi:tabstop=4:expandtab:autoindent # kate: indent-mode python;indent-width 4;replace-tabs on; diff --git a/modules/EightBall.py b/modules/EightBall.py index d4eba5d..94a6920 100644 --- a/modules/EightBall.py +++ b/modules/EightBall.py @@ -61,7 +61,7 @@ class EightBall(Module): match = re.search('^!8ball', what) if match: response = self.responses[random.randint(1,len(self.responses))-1] - return self.reply(connection, event, response) + return self.irc.reply(event, response) # vi:tabstop=4:expandtab:autoindent # kate: indent-mode python;indent-width 4;replace-tabs on; diff --git a/modules/Facts.py b/modules/Facts.py index d20986f..ee7e168 100644 --- a/modules/Facts.py +++ b/modules/Facts.py @@ -80,23 +80,23 @@ class Facts(Module): cur.execute('''INSERT INTO facts_facts (category, fact, who, userhost) VALUES (%s, %s, %s, %s)''', (category, fact, nick, userhost)) db.commit() - return self.reply(connection, event, category + ' added.') + return self.irc.reply(event, category + ' added.') match = re.search('^!facts\s+(\S+)\s+(.*)$', what) if match: category = match.group(1) regex = match.group(2) - return self.reply(connection, event, self._get_fact(category, regex)) + return self.irc.reply(event, self._get_fact(category, regex)) match = re.search('^!facts\s+(\S+)$', what) if match: category = match.group(1) - return self.reply(connection, event, self._get_fact(category)) + return self.irc.reply(event, self._get_fact(category)) except mdb.Error as e: db.rollback() self.log.error("database error during add/retrieve") self.log.exception(e) - return self.reply(connection, event, "database error during add/retrieve fact") + return self.irc.reply(event, "database error during add/retrieve fact") finally: cur.close() def _get_fact(self, category, search=""): diff --git a/modules/Help.py b/modules/Help.py index 77c1b62..e5e8efd 100644 --- a/modules/Help.py +++ b/modules/Help.py @@ -42,11 +42,11 @@ class Help(Module): if match: (module, key) = match.group(2,4) if (module == None): - return self.reply(connection, event, self.handle_help_descriptions()) + return self.irc.reply(event, self.handle_help_descriptions()) elif (key == None): - return self.reply(connection, event, self.handle_help_module(module)) + return self.irc.reply(event, self.handle_help_module(module)) else: - return self.reply(connection, event, self.handle_help_detail(module, key)) + return self.irc.reply(event, self.handle_help_detail(module, key)) def handle_help_descriptions(self): """print the general help""" diff --git a/modules/IrcAdmin.py b/modules/IrcAdmin.py index a7511bb..aec0207 100644 --- a/modules/IrcAdmin.py +++ b/modules/IrcAdmin.py @@ -72,25 +72,25 @@ class IrcAdmin(Module): whats = what.split(' ') if whats[0] == '!join' and admin_unlocked and len(whats) >= 2: - return self.reply(connection, event, self.sub_join_channel(connection, event, nick, userhost, what, admin_unlocked)) + return self.irc.reply(event, self.sub_join_channel(connection, event, nick, userhost, what, admin_unlocked)) elif whats[0] == '!part' and admin_unlocked and len(whats) >= 2: - return self.reply(connection, event, self.sub_part_channel(connection, event, nick, userhost, what, admin_unlocked)) + return self.irc.reply(event, self.sub_part_channel(connection, event, nick, userhost, what, admin_unlocked)) elif whats[0] == '!quit' and admin_unlocked: - return self.reply(connection, event, self.sub_quit_irc(connection, event, nick, userhost, what, admin_unlocked)) + return self.irc.reply(event, self.sub_quit_irc(connection, event, nick, userhost, what, admin_unlocked)) elif whats[0] == '!autojoin' and admin_unlocked and len(whats) >= 3: - return self.reply(connection, event, self.sub_autojoin_manipulate(connection, event, nick, userhost, what, admin_unlocked)) + return self.irc.reply(event, self.sub_autojoin_manipulate(connection, event, nick, userhost, what, admin_unlocked)) elif whats[0] == '!save' and admin_unlocked: self.irc.save_modules() self.irc.save_config() - return self.reply(connection, event, 'Saved.') + return self.irc.reply(event, 'Saved.') elif whats[0] == '!nick' and admin_unlocked and len(whats) >= 2: - return self.reply(connection, event, self.sub_change_nick(connection, event, nick, userhost, what, admin_unlocked)) + return self.irc.reply(event, self.sub_change_nick(connection, event, nick, userhost, what, admin_unlocked)) elif whats[0] == '!load' and admin_unlocked and len(whats) >= 2: - return self.reply(connection, event, self.sub_load_module(connection, event, nick, userhost, what, admin_unlocked)) + return self.irc.reply(event, self.sub_load_module(connection, event, nick, userhost, what, admin_unlocked)) elif whats[0] == '!unload' and admin_unlocked and len(whats) >= 2: - return self.reply(connection, event, self.sub_unload_module(connection, event, nick, userhost, what, admin_unlocked)) + return self.irc.reply(event, self.sub_unload_module(connection, event, nick, userhost, what, admin_unlocked)) elif whats[0] == '!modules': - return self.reply(connection, event, self.sub_list_modules(connection, event, nick, userhost, what, admin_unlocked)) + return self.irc.reply(event, self.sub_list_modules(connection, event, nick, userhost, what, admin_unlocked)) def sub_join_channel(self, connection, event, nick, userhost, what, admin_unlocked): whats = what.split(' ') diff --git a/modules/Karma.py b/modules/Karma.py index 092aa48..d11b02d 100644 --- a/modules/Karma.py +++ b/modules/Karma.py @@ -94,11 +94,11 @@ class Karma(Module): self.handle_karma_change(connection, nick, userhost, what) if (self.queryre.search(what)): - return self.reply(connection, event, self.handle_karma_query(connection, nick, userhost, what)) + return self.irc.reply(event, self.handle_karma_query(connection, nick, userhost, what)) elif (self.statre.search(what)): - return self.reply(connection, event, self.handle_stat_query(connection, nick, userhost, what)) + return self.irc.reply(event, self.handle_stat_query(connection, nick, userhost, what)) elif (self.reportre.search(what)): - return self.reply(connection, event, self.handle_report_query(connection, nick, userhost, what)) + return self.irc.reply(event, self.handle_report_query(connection, nick, userhost, what)) def handle_karma_change(self, connection, nick, userhost, what): """ diff --git a/modules/Markov.py b/modules/Markov.py index d966b2f..2ad9426 100644 --- a/modules/Markov.py +++ b/modules/Markov.py @@ -167,10 +167,10 @@ class Markov(Module): target = event.target() if self.learnre.search(what): - return self.reply(connection, event, self.markov_learn(connection, event, nick, + return self.irc.reply(event, self.markov_learn(connection, event, nick, userhost, what, admin_unlocked)) elif self.replyre.search(what) and not self.shut_up: - return self.reply(connection, event, self.markov_reply(connection, event, nick, + return self.irc.reply(event, self.markov_reply(connection, event, nick, userhost, what, admin_unlocked)) if not self.shut_up: @@ -181,12 +181,12 @@ class Markov(Module): if addressed_re.match(what): # i was addressed directly, so respond, addressing the speaker self.lines_seen.append(('.self.said.', datetime.now())) - return self.reply(connection, event, '{0:s}: {1:s}'.format(nick, + return self.irc.reply(event, '{0:s}: {1:s}'.format(nick, self._generate_line(target, line=addressed_re.match(what).group(1)))) else: # i wasn't addressed directly, so just respond self.lines_seen.append(('.self.said.', datetime.now())) - return self.reply(connection, event, '{0:s}'.format(self._generate_line(target, line=what))) + return self.irc.reply(event, '{0:s}'.format(self._generate_line(target, line=what))) def markov_learn(self, connection, event, nick, userhost, what, admin_unlocked): """Learn one line, as provided to the command.""" diff --git a/modules/Pi.py b/modules/Pi.py index 575a6cb..5d9c52d 100644 --- a/modules/Pi.py +++ b/modules/Pi.py @@ -104,11 +104,11 @@ class Pi(Module): db.rollback() self.log.error("database error doing pi stuff") self.log.exception(e) - return self.reply(connection, event, + return self.irc.reply(event, "database error while estimating pi: {0:s}".format(str(e))) finally: cur.close() - return self.reply(connection, event, + return self.irc.reply(event, "({0:.10f}, {1:.10f}) is {2}within the unit circle. "\ "pi is {5:.10f}. (i:{3:d} p:{4:d})".format(x, y, "" if inside else "not ", diff --git a/modules/Radio.py b/modules/Radio.py index eb6138f..805e70b 100644 --- a/modules/Radio.py +++ b/modules/Radio.py @@ -73,7 +73,7 @@ class Radio(Module): """Handle commands and inputs from IRC events.""" if self.statusre.search(what): - return self.reply(connection, event, self.get_status()) + return self.irc.reply(event, self.get_status()) def get_status(self): """Get the status (playlist entries, mostly) of the MPD server.""" diff --git a/modules/Seen.py b/modules/Seen.py index 863a9e5..82f29d7 100644 --- a/modules/Seen.py +++ b/modules/Seen.py @@ -91,9 +91,9 @@ class Seen(Module): if result: seentime = result['time'].replace(tzinfo=tzlocal()) replystr = 'last saw {0:s} in {3:s} at {1:s} saying \'{2:s}\'.'.format(result['nick'], seentime.astimezone(tzlocal()).strftime('%Y/%m/%d %H:%M:%S %Z'), result['what'], result['location']) - return self.reply(connection, event, replystr) + return self.irc.reply(event, replystr) else: - return self.reply(connection, event, 'i have not seen {0:s} in {1:s}.'.format(nick, where)) + return self.irc.reply(event, 'i have not seen {0:s} in {1:s}.'.format(nick, where)) except mdb.Error as e: db.rollback() self.log.error("database error retrieving seen data") diff --git a/modules/Storycraft.py b/modules/Storycraft.py index b0f7533..31cd2ce 100644 --- a/modules/Storycraft.py +++ b/modules/Storycraft.py @@ -172,23 +172,23 @@ class Storycraft(Module): """Pass storycraft control commands to the appropriate method based on input.""" if self.statusre.search(what): - return self.reply(connection, event, self.storycraft_status(connection, event, nick, userhost, what, admin_unlocked)) + return self.irc.reply(event, self.storycraft_status(connection, event, nick, userhost, what, admin_unlocked)) elif self.newgamere.search(what): - return self.reply(connection, event, self.storycraft_newgame(connection, event, nick, userhost, what, admin_unlocked)) + return self.irc.reply(event, self.storycraft_newgame(connection, event, nick, userhost, what, admin_unlocked)) elif self.joingamere.search(what): - return self.reply(connection, event, self.storycraft_joingame(connection, event, nick, userhost, what, admin_unlocked)) + return self.irc.reply(event, self.storycraft_joingame(connection, event, nick, userhost, what, admin_unlocked)) elif self.listgamesre.search(what): - return self.reply(connection, event, self.storycraft_listgames(connection, event, nick, userhost, what, admin_unlocked)) + return self.irc.reply(event, self.storycraft_listgames(connection, event, nick, userhost, what, admin_unlocked)) elif self.startgamere.search(what): - return self.reply(connection, event, self.storycraft_startgame(connection, event, nick, userhost, what, admin_unlocked)) + return self.irc.reply(event, self.storycraft_startgame(connection, event, nick, userhost, what, admin_unlocked)) elif self.showlinere.search(what): - return self.reply(connection, event, self.storycraft_showline(connection, event, nick, userhost, what, admin_unlocked)) + return self.irc.reply(event, self.storycraft_showline(connection, event, nick, userhost, what, admin_unlocked)) elif self.addlinere.search(what): - return self.reply(connection, event, self.storycraft_addline(connection, event, nick, userhost, what, admin_unlocked)) + return self.irc.reply(event, self.storycraft_addline(connection, event, nick, userhost, what, admin_unlocked)) elif self.gamestatusre.search(what): - return self.reply(connection, event, self.storycraft_gamestatus(connection, event, nick, userhost, what, admin_unlocked)) + return self.irc.reply(event, self.storycraft_gamestatus(connection, event, nick, userhost, what, admin_unlocked)) elif self.exportre.search(what): - return self.reply(connection, event, self.storycraft_export(connection, event, nick, userhost, what, admin_unlocked)) + return self.irc.reply(event, self.storycraft_export(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.""" diff --git a/modules/TextTransform.py b/modules/TextTransform.py index 9f0fe16..e9cda19 100644 --- a/modules/TextTransform.py +++ b/modules/TextTransform.py @@ -35,15 +35,15 @@ class TextTransform(Module): reply = [''] if self.rot13(what, reply): - return self.reply(connection, event, reply[0]) + return self.irc.reply(event, reply[0]) elif self.base64(what, reply): - return self.reply(connection, event, reply[0]) + return self.irc.reply(event, reply[0]) elif self.upper(what, reply): - return self.reply(connection, event, reply[0]) + return self.irc.reply(event, reply[0]) elif self.lower(what, reply): - return self.reply(connection, event, reply[0]) + return self.irc.reply(event, reply[0]) elif self.al_bhed(what, reply): - return self.reply(connection, event, reply[0]) + return self.irc.reply(event, reply[0]) def rot13(self, what, reply): """ diff --git a/modules/Twitter.py b/modules/Twitter.py index 2ffb56e..56ef617 100644 --- a/modules/Twitter.py +++ b/modules/Twitter.py @@ -128,22 +128,22 @@ class Twitter(Module): """Attempt to do twitter things.""" if self.getstatusre.search(what): - return self.reply(connection, event, self.twitter_getstatus(connection, event, nick, + return self.irc.reply(event, self.twitter_getstatus(connection, event, nick, userhost, what, admin_unlocked)) elif self.getuserstatusre.search(what): - return self.reply(connection, event, self.twitter_getuserstatus(connection, event, nick, + return self.irc.reply(event, self.twitter_getuserstatus(connection, event, nick, userhost, what, admin_unlocked)) elif self.tweetre.search(what): - return self.reply(connection, event, self.twitter_tweet(connection, event, nick, + return self.irc.reply(event, self.twitter_tweet(connection, event, nick, userhost, what, admin_unlocked)) elif self.replytore.search(what): - return self.reply(connection, event, self.twitter_replyto(connection, event, nick, + return self.irc.reply(event, self.twitter_replyto(connection, event, nick, userhost, what, admin_unlocked)) elif self.gettokenre.search(what): - return self.reply(connection, event, self.twitter_gettoken(connection, event, nick, + return self.irc.reply(event, self.twitter_gettoken(connection, event, nick, userhost, what, admin_unlocked)) elif self.authre.search(what): - return self.reply(connection, event, self.twitter_auth(connection, event, nick, + return self.irc.reply(event, self.twitter_auth(connection, event, nick, userhost, what, admin_unlocked)) def twitter_getstatus(self, connection, event, nick, userhost, what, admin_unlocked): diff --git a/modules/Weather.py b/modules/Weather.py index da8008f..61d80d5 100644 --- a/modules/Weather.py +++ b/modules/Weather.py @@ -80,15 +80,15 @@ class Weather(Module): if queryitems[0] == "conditions": # current weather query results = self.get_conditions_for_query(queryitems[1:]) - return self.reply(connection, event, results) + return self.irc.reply(event, results) elif queryitems[0] == "forecast": # forecast query results = self.get_forecast_for_query(queryitems[1:]) - return self.reply(connection, event, results) + return self.irc.reply(event, results) else: # assume they wanted current weather results = self.get_conditions_for_query(queryitems) - return self.reply(connection, event, results) + return self.irc.reply(event, results) def get_conditions_for_query(self, queryitems): """Make a wunderground conditions call, return as string."""