diff --git a/DrBotIRC.py b/DrBotIRC.py index 06db472..29d869d 100644 --- a/DrBotIRC.py +++ b/DrBotIRC.py @@ -14,6 +14,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . + """ import bisect @@ -35,41 +36,56 @@ class DrBotzoMethods: """Methods to expose to the XML-RPC server.""" def __init__(self, irc): - """Store the same stuff the core module would, since we'll probably need it.""" + """Store the same stuff the core module would, since we'll probably + need it. + + Args: + irc the irc instance to save a reference to + + """ self.irc = irc self.log = logging.getLogger('drbotzo') def echo(self, message): - """ - Just reply to the client, for testing purposes. + """Just reply to the client, for testing purposes. + + Args: + message the message to echo + + Returns: + message, unmodified. - Keyword arguments: - message - the message to echo """ return message def say(self, target, message): - """ - Say a message in a channel/to a nick. + """Say a message in a channel/to a nick. + + Args: + target the nick/channel to privmsg + message the message to send + + Returns: + "OK", since there's no other feedback to give. - Keyword arguments: - target - the nick/channel to privmsg - message - the message to send """ self.irc.server.privmsg(target, message) return "OK" def execute_module_method(self, modname, method, args): - """ - Execute the method (with arguments) of the specified module. + """Execute the method (with arguments) of the specified module. + + Args: + modname the loaded module to retrieve + method the method to call from that module + args the arguments to provide to the method as a tuple + + Returns: + An error string, or the outcome of the method call. - Keyword arguments: - modname - the loaded module to retrieve - method - the method to call from that module - args - the arguments to provide to the method as a pythonic tuple """ for module in self.irc.modlist: @@ -93,6 +109,16 @@ class DrBotServerConnection(irclib.ServerConnection): nickmask = None def __init__(self, irclibobj): + """Instantiate the server connection. + + Also start guessing at the nickmask and get ready to do on_welcome + stuff. + + Args: + irclibobj the irclib instance to connect with + + """ + irclib.ServerConnection.__init__(self, irclibobj) # temporary. hopefully on_welcome() will set this @@ -102,7 +128,14 @@ class DrBotServerConnection(irclib.ServerConnection): self.add_global_handler('welcome', self.on_welcome, 1) def on_welcome(self, connection, event): - """Set the nickmask that the ircd tells us is us.""" + """Set the nickmask that the ircd tells us is us. + + Args: + connection source connection + event incoming event + + """ + what = event.arguments()[0] match = re.search('(\S+!\S+@\S+)', what) @@ -110,7 +143,13 @@ class DrBotServerConnection(irclib.ServerConnection): self.nickmask = match.group(1) def privmsg(self, target, text): - """Send a PRIVMSG command.""" + """Send a PRIVMSG command. + + Args: + target the destination nick/channel + text the message to send + + """ splitter = "..." @@ -132,6 +171,7 @@ class DrBotServerConnection(irclib.ServerConnection): times = times + 1 if times >= 4: + # this is stupidly long, abort return # done splitting @@ -148,6 +188,12 @@ class DrBotIRC(irclib.IRC): server = None def __init__(self, config): + """Initialize XML-RPC interface and save references. + + Args: + config the config structure to load stuff from + + """ irclib.IRC.__init__(self) self.config = config @@ -173,14 +219,26 @@ class DrBotIRC(irclib.IRC): except NoOptionError: pass def server(self): - """Create a DrBotServerConnection.""" + """Create a DrBotServerConnection. + + Returns: + The newly created DrBotServerConnection. + + """ + self.server = DrBotServerConnection(self) self.connections.append(self.server) return self.server def _handle_event(self, connection, event): - """Override event handler to do recursion.""" + """Override event handler to do recursion. + + Args: + connection source connection + event incoming event + + """ try: nick = irclib.nm_to_n(event.source()) @@ -209,14 +267,24 @@ class DrBotIRC(irclib.IRC): self.log.exception(e) def xmlrpc_register_function(self, func, name): - """Add a method to the XML-RPC interface.""" + """Add a method to the XML-RPC interface. + + Args: + func the method to register + name the name to expose the method as + + """ if func and self.xmlrpc: if hasattr(func, '__call__'): self.xmlrpc.register_function(func, name) def _xmlrpc_listen(self): - """Begin listening. Hopefully this was called in a new thread.""" + """Begin listening. + + Hopefully this was called in a new thread. + + """ self.xmlrpc.serve_forever() @@ -225,6 +293,11 @@ class DrBotIRC(irclib.IRC): The intent here is that we replace [text] with whatever a module provides to us. + + Args: + connection source connection + event incoming event + """ h = self.handlers @@ -240,6 +313,14 @@ class DrBotIRC(irclib.IRC): def try_alias_cmds(self, connection, event): """See if there is an alias ("!command") in the text, and if so do alias manipulation before any other recursion or aliasing. + + Args: + connection source connection + event incoming event + + Returns: + The outcome of the alias command, if the text had one. + """ try: @@ -303,7 +384,17 @@ class DrBotIRC(irclib.IRC): except NoSectionError: pass def try_alias(self, connection, event): - # try doing alias work + """Try turning aliases into commands. + + Args: + connection source connection + event incoming event + + Returns: + The de-aliased event object. + + """ + try: what = event.arguments()[0] alias_list = self.config.options('Alias') @@ -337,6 +428,11 @@ class DrBotIRC(irclib.IRC): command arg1 [anothercommand arg1 arg2] where the output of anothercommand is command's arg2..n. + + Args: + connection source connection + event incoming event + """ try: @@ -381,6 +477,13 @@ class DrBotIRC(irclib.IRC): except IndexError: pass def quit_irc(self, connection, msg): + """Quit IRC, disconnect, shut everything down. + + Args: + connection the connection to quit + msg the message to send while quitting + """ + for module in self.modlist: module.save() module.shutdown() @@ -391,8 +494,20 @@ class DrBotIRC(irclib.IRC): self.log.info("Bot shutting down.") sys.exit() - def reply(self, connection, event, replystr, stop_responding=False): - """Reply over IRC to replypath or return a string with the reply.""" + def reply(self, connection, event, replystr, stop=False): + """Reply over IRC to replypath or return a string with the reply. + + 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() @@ -407,10 +522,12 @@ class DrBotIRC(irclib.IRC): replies = replystr.split('\n') for reply in replies: connection.privmsg(replypath, reply) - if stop_responding: + if stop: return "NO MORE" def save_modules(self): + """Call each module's save(), in case they have something to do.""" + for module in self.modlist: module.save() @@ -422,6 +539,13 @@ class DrBotIRC(irclib.IRC): self.xmlrpc.server_close() def save_config(self): + """Write the config file to disk. + + Returns: + Short string indicating success. + + """ + with open('dr.botzo.cfg', 'w') as cfg: self.config.write(cfg) @@ -430,6 +554,13 @@ class DrBotIRC(irclib.IRC): def load_module(self, modname): """Load a module (in both the python and dr.botzo sense) if not already loaded. + + Args: + modname the module to attempt to load + + Returns: + String describing the outcome of the load attempt. + """ for module in self.modlist: @@ -457,7 +588,15 @@ class DrBotIRC(irclib.IRC): return "Module '" + modname + "' could not be loaded." def unload_module(self, modname): - """Attempt to unload and del a module if it's loaded.""" + """Attempt to unload and del a module if it's loaded. + + Args: + modname the module to attempt to unload + + Returns: + String describing the outcome of the unload attempt. + + """ modstr = 'modules.'+modname for module in self.modlist: @@ -485,7 +624,12 @@ class DrBotIRC(irclib.IRC): return 'Module ' + modname + ' is not loaded.' def list_modules(self): - """List loaded modules.""" + """List loaded modules. + + Returns: + A list of the loaded modules' names. + + """ modnames = [] for module in self.modlist: @@ -493,8 +637,9 @@ class DrBotIRC(irclib.IRC): return modnames - # SIGINT signal handler def sigint_handler(self, signal, frame): + """Cleanly shutdown on SIGINT.""" + for module in self.modlist: module.save() module.shutdown()