diff --git a/DrBotIRC.py b/DrBotIRC.py index 4626e7e..6f88362 100644 --- a/DrBotIRC.py +++ b/DrBotIRC.py @@ -21,11 +21,62 @@ import copy from ConfigParser import NoOptionError, NoSectionError import re import signal +from SimpleXMLRPCServer import SimpleXMLRPCServer import socket import sys +import thread from extlib import irclib +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.""" + + self.irc = irc + + def echo(self, message): + """ + Just reply to the client, for testing purposes. + Arguments: message - the message to echo + """ + + return message + + def say(self, target, message): + """ + Say a message in a channel/to a nick. + 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. + 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: + if modname == module.__class__.__name__: + try: + func = getattr(module, method) + except AttributeError: + return "couldn't find " + method + " in found module " + modname + + if hasattr(func, '__call__'): + return func(*args) + else: + return method + " in found module " + modname + " is not callable" + + return "couldn't find " + modname + class DrBotServerConnection(irclib.ServerConnection): """Subclass irclib's ServerConnection, in order to expand privmsg.""" @@ -90,10 +141,26 @@ class DrBotIRC(irclib.IRC): irclib.IRC.__init__(self) self.config = config + self.xmlrpc = None # handle SIGINT signal.signal(signal.SIGINT, self.sigint_handler) + # load XML-RPC server + try: + if self.config.has_section('XMLRPC'): + host = self.config.get('XMLRPC', 'host') + port = self.config.getint('XMLRPC', 'port') + if host and port: + self.funcs = DrBotzoMethods(self) + + self.xmlrpc = SimpleXMLRPCServer((host, port)) + self.xmlrpc.register_introspection_functions() + self.xmlrpc.register_instance(self.funcs) + + thread.start_new_thread(self._xmlrpc_listen, ()) + except NoOptionError: pass + def server(self): """Create a DrBotServerConnection.""" self.server = DrBotServerConnection(self) @@ -129,6 +196,11 @@ class DrBotIRC(irclib.IRC): except Exception as e: print("exception floated up to DrBotIrc: " + str(e)) + def _xmlrpc_listen(self): + """Begin listening. Hopefully this was called in a new thread.""" + + self.xmlrpc.serve_forever() + def try_to_replace_event_text_with_module_text(self, connection, event): """Do something very similar to _handle_event, but for recursion. @@ -296,12 +368,20 @@ class DrBotIRC(irclib.IRC): connection.quit(msg) print(self.save_config()) + self._xmlrpc_shutdown() sys.exit() def save_modules(self): for module in self.modlist: module.save() + def _xmlrpc_shutdown(self): + """Shut down the XML-RPC server.""" + + if self.xmlrpc is not None: + self.xmlrpc.shutdown() + self.xmlrpc.server_close() + def save_config(self): with open('dr.botzo.cfg', 'w') as cfg: self.config.write(cfg) @@ -379,6 +459,7 @@ class DrBotIRC(irclib.IRC): module.shutdown() print(self.save_config()) + self._xmlrpc_shutdown() sys.exit() # vi:tabstop=4:expandtab:autoindent diff --git a/modules/XmlRpcServer.py b/modules/XmlRpcServer.py deleted file mode 100644 index fc61fbd..0000000 --- a/modules/XmlRpcServer.py +++ /dev/null @@ -1,112 +0,0 @@ -""" -XmlRpcServer - access dr.botzo functionality via XML-RPC -Copyright (C) 2012 Brian S. Stephan - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -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 thread -from SimpleXMLRPCServer import SimpleXMLRPCServer - -from extlib import irclib - -from Module import Module - -class DrBotzoMethods: - - """Methods to expose to the XML-RPC server.""" - - def __init__(self, irc, config, server): - """Store the same stuff the core module would, since we'll probably need it.""" - - self.irc = irc - self.config = config - self.server = server - - def echo(self, message): - """ - Just reply to the client, for testing purposes. - Arguments: message - the message to echo - """ - - return message - - def say(self, target, message): - """ - Say a message in a channel/to a nick. - 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. - 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: - if modname == module.__class__.__name__: - try: - func = getattr(module, method) - except AttributeError: - return "couldn't find " + method + " in found module " + modname - - if hasattr(func, '__call__'): - return func(*args) - else: - return method + " in found module " + modname + " is not callable" - - return "couldn't find " + modname - -class XmlRpcServer(Module): - - """A module to expose an XML-RPC service, to achieve various things.""" - - def __init__(self, irc, config, server): - """Create the XML-RPC server.""" - - Module.__init__(self, irc, config, server) - - self.xmlrpc = None - self.funcs = DrBotzoMethods(irc, config, server) - - self.xmlrpc = SimpleXMLRPCServer(("localhost", 8180)) - self.xmlrpc.register_introspection_functions() - self.xmlrpc.register_instance(self.funcs) - - thread.start_new_thread(self._listen, ()) - - def _listen(self): - """Begin listening. Hopefully this was called in a new thread.""" - - self.xmlrpc.serve_forever() - - def do(self, connection, event, nick, userhost, what, admin_unlocked): - """Do nothing.""" - - return - - def shutdown(self): - """Shut down the XML-RPC server.""" - - if self.xmlrpc is not None: - self.xmlrpc.shutdown() - self.xmlrpc.server_close() - -# vi:tabstop=4:expandtab:autoindent