diff --git a/scripts/setup.py b/scripts/setup.py index fe3f6b039..e851d382d 100644 --- a/scripts/setup.py +++ b/scripts/setup.py @@ -36,31 +36,38 @@ # Provide a list of modules to load by default. # See what other commands the user would like to run by default. # Go through conf.py options and see which ones the user would like. -import sys -sys.path.insert(0, 'src') -from fix import * -from questions import * import os import imp +import sys +import socket +import pprint +sys.path.insert(0, 'src') + +from fix import * +from questions import * + import conf import ircdb +import ircutils + sys.path.insert(0, conf.pluginDir) -import socket + if __name__ == '__main__': + fd = file('src/template.py') + template = fd.read() + fd.close() ### # First things first. ### - if ny('Are you an advanced Supybot/IRC user?') == 'y': + if yn('Are you an advanced Supybot/IRC user?') == 'y': advanced = True else: advanced = False ### # Basic stuff. ### - name = anything('What would you like to name your config file?') - if not name.endswith('.conf'): - name += '.conf' - configfd = file(os.path.join(conf.confDir, name), 'w') + + # Server. server = '' while not server: server = anything('What server would you like to connect to?') @@ -71,28 +78,48 @@ if __name__ == '__main__': except: print 'Sorry, but I couldn\'t find that server.' server = '' - if ny('Does that server require connection on a non-standard port?')=='y': - server = ':'.join(server, anything('What port is that?')) - configfd.write('Server: %s\n' % server) - if ny('Does the server require a password to connect?') == 'y': + if yn('Does that server require connection on a non-standard port?')=='y': + port = '' + while not port: + port = something('What port is that?') + try: + i = int(port) + if not (0 < i < 65536): + raise ValueError + except ValueError: + print 'That\'s not a valid port.' + port = '' + server = ':'.join((server, port)) + template = template.replace('%%server%%', repr(server)) + + # Password. + password = '' + if yn('Does the server require a password to connect?') == 'y': password = anything('What password would you like the bot to use?') - configfd.write('Pass: ' + password) - nick = anything('What nick would you like the bot to use?') - configfd.write('Nick: %s\n' % nick) - if advanced and ny('Would you like to set a user/ident?') == 'y': + template = template.replace('%%password%%', repr(password)) + + # Nick. + nick = something('What nick would you like the bot to use?') + while not ircutils.isNick(nick): + print 'That\'s not a valid nick.' + nick = something('What nick would you like the bot use?') + template = template.replace('%%nick%%', repr(nick)) + + # User/Ident. + user = nick + ident = nick + if advanced and yn('Would you like to set a user/ident?') == 'y': user = anything('What user would you like the bot to use?') - configfd.write('User: %s\n' % user) ident = anything('What ident would you like the bot to use?') - configfd.write('Ident: %s\n' % ident) - configfd.write('\n') + template = template.replace('%%user%%', repr(user)) + template = template.replace('%%ident%%', repr(ident)) + onStart = [] - onStart.append('# Commands to run before connecting.') + afterConnect = [] onStart.append('load AdminCommands') onStart.append('load UserCommands') onStart.append('load ChannelCommands') onStart.append('load MiscCommands') - afterConnect = [] - afterConnect.append('# Commands to run after connecting.') ### # Modules. @@ -141,6 +168,7 @@ if __name__ == '__main__': postConnect = 'Would you like any other commands to run ' \ 'when the bot is finished connecting to the server?' afterConnect.append(anything('What command?')) + ### # Set owner user. ### @@ -157,18 +185,31 @@ if __name__ == '__main__': ### # Finito! ### - for command in onStart: - configfd.write(command) - configfd.write('\n') - configfd.write('\n') - for command in afterConnect: - configfd.write(command) - configfd.write('\n') - configfd.close() + template = template.replace('%%onStart%%', pprint.pformat(onStart)) + template = template.replace('%%afterConnect%%', + pprint.pformat(afterConnect)) + + ### + # Configuration variables in conf.py. + ### + configVariables = {} + if advanced and \ + yn('Would you like to modify the default config variables?')=='y': + pass + template = template.replace('%%configVariables%%', + pprint.pformat(configVariables)) + + filename = '%s.py' % nick + fd = open(filename, 'w') + fd.write(template) + fd.close() + + os.chmod(filename, 0755) + print print 'You\'re done! Now run the bot with the command line:' - print 'src/bot.py conf/%s' % name + print './%s' % filename print # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: diff --git a/src/OwnerCommands.py b/src/OwnerCommands.py new file mode 100644 index 000000000..e2fde818e --- /dev/null +++ b/src/OwnerCommands.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python + +### +# Copyright (c) 2002, Jeremiah Fincher +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +### + +import os +import gc +import imp +import sys +import linecache + +import conf +import debug +import world +import ircmsgs +import drivers +import privmsgs +import callbacks + +class OwnerCommands(privmsgs.CapabilityCheckingPrivmsg): + capability = 'owner' + def __init__(self): + callbacks.Privmsg.__init__(self) + setattr(self.__class__, 'exec', self._exec) + + def eval(self, irc, msg, args): + """""" + if conf.allowEval: + s = privmsgs.getArgs(args) + try: + irc.reply(msg, repr(eval(s))) + except Exception, e: + irc.reply(msg, debug.exnToString(e)) + else: + irc.error(msg, conf.replyEvalNotAllowed) + + def _exec(self, irc, msg, args): + """""" + if conf.allowEval: + s = privmsgs.getArgs(args) + try: + exec s + irc.reply(msg, conf.replySuccess) + except Exception, e: + irc.reply(msg, debug.exnToString(e)) + else: + irc.error(msg, conf.replyEvalNotAllowed) + + def setdefaultcapability(self, irc, msg, args): + """ + + Sets the default capability to be allowed for any command. + """ + capability = privmsgs.getArgs(args) + conf.defaultCapabilities[capability] = True + irc.reply(msg, conf.replySuccess) + + def unsetdefaultcapability(self, irc, msg, args): + """ + + Unsets the default capability for any command. + """ + capability = privmsgs.getArgs(args) + del conf.defaultCapabilities[capability] + irc.reply(msg, conf.replySuccess) + + def settrace(self, irc, msg, args): + """takes no arguments + + Starts the function-tracing debug mode; beware that this makes *huge* + logfiles. + """ + sys.settrace(debug.tracer) + irc.reply(msg, conf.replySuccess) + + def unsettrace(self, irc, msg, args): + """takes no arguments + + Stops the function-tracing debug mode.""" + sys.settrace(None) + irc.reply(msg, conf.replySuccess) + + def ircquote(self, irc, msg, args): + """ + + Sends the raw string given to the server. + """ + s = privmsgs.getArgs(args) + try: + m = ircmsgs.IrcMsg(s) + irc.queueMsg(m) + except Exception: + debug.recoverableException() + irc.error(msg, conf.replyError) + + def quit(self, irc, msg, args): + """[] + + Exits the program with the given return value (the default is 0) + """ + try: + i = int(args[0]) + except (ValueError, IndexError): + i = 0 + for driver in drivers._drivers.itervalues(): + driver.die() + for irc in world.ircs[:]: + irc.die() + debug.exit(i) + + def flush(self, irc, msg, args): + """takes no arguments + + Runs all the periodic flushers in world.flushers. + """ + world.flush() + irc.reply(msg, conf.replySuccess) + + def upkeep(self, irc, msg, args): + """takes no arguments + + Runs the standard upkeep stuff (flushes and gc.collects()). + """ + world.upkeep() + if gc.garbage: + if len(gc.garbage) < 10: + irc.reply(msg, 'Garbage! %r' % gc.garbage) + else: + irc.reply(msg, 'Garbage! %s items.' % len(gc.garbage)) + else: + irc.reply(msg, conf.replySuccess) + + def set(self, irc, msg, args): + """ + + Sets the runtime variable to . Currently used variables + include "noflush" which, if set to true value, will prevent the + periodic flushing that normally occurs. + """ + (name, value) = privmsgs.getArgs(args, optional=1) + world.tempvars[name] = value + irc.reply(msg, conf.replySuccess) + + def unset(self, irc, msg, args): + """ + + Unsets the value of variables set via the 'set' command. + """ + name = privmsgs.getArgs(args) + try: + del world.tempvars[name] + irc.reply(msg, conf.replySuccess) + except KeyError: + irc.error(msg, 'That variable wasn\'t set.') + + def load(self, irc, msg, args): + """ + + Loads the plugin from the plugins/ directory. + """ + name = privmsgs.getArgs(args) + for cb in irc.callbacks: + if cb.name() == name: + irc.error(msg, 'That module is already loaded.') + return + try: + moduleInfo = imp.find_module(name) + except ImportError: + irc.error(msg, 'No plugin %s exists.' % name) + return + module = imp.load_module(name, *moduleInfo) + linecache.checkcache() + callback = module.Class() + if hasattr(callback, 'configure'): + callback.configure(irc) + irc.addCallback(callback) + irc.reply(msg, conf.replySuccess) + + ''' + def superreload(self, irc, msg, args): + """ + + Reloads a module, hopefully such that all vestiges of the old module + are gone. + """ + name = privmsgs.getArgs(args) + world.superReload(__import__(name)) + irc.reply(msg, conf.replySuccess) + ''' + + def reload(self, irc, msg, args): + """ + + Unloads and subsequently reloads the callback by name; use the 'list' + command to see a list of the currently loaded callbacks. + """ + name = privmsgs.getArgs(args) + callbacks = irc.removeCallback(name) + + if callbacks: + for callback in callbacks: + callback.die() + del callback + gc.collect() + try: + moduleInfo = imp.find_module(name) + module = imp.load_module(name, *moduleInfo) + linecache.checkcache() + callback = module.Class() + if hasattr(callback, 'configure'): + callback.configure(irc) + irc.addCallback(callback) + irc.reply(msg, conf.replySuccess) + except ImportError: + for callback in callbacks: + irc.addCallback(callback) + irc.error(msg, 'No plugin %s exists.' % name) + else: + irc.error(msg, 'There was no callback %s.' % name) + + def unload(self, irc, msg, args): + """ + + Unloads the callback by name; use the 'list' command to see a list + of the currently loaded callbacks. + """ + name = privmsgs.getArgs(args) + callbacks = irc.removeCallback(name) + if callbacks: + for callback in callbacks: + callback.die() + del callback + gc.collect() + irc.reply(msg, conf.replySuccess) + else: + irc.error(msg, 'There was no callback %s' % name) + + def cvsup(self, irc, msg, args): + """takes no arguments""" + irc.reply(msg, str(os.system('cvs up'))) + + def say(self, irc, msg, args): + """ """ + (channel, text) = privmsgs.getArgs(args, needed=2) + irc.queueMsg(ircmsgs.privmsg(channel, text)) + + +Class = OwnerCommands + + +# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: + diff --git a/src/bot.py b/src/bot.py deleted file mode 100755 index a4d950422..000000000 --- a/src/bot.py +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env python - -### -# Copyright (c) 2002, Jeremiah Fincher -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions, and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions, and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author of this software nor the name of -# contributors to this software may be used to endorse or promote products -# derived from this software without specific prior written consent. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -### - -""" -Main program file for running the bot. -""" - -from fix import * - -import sys -import time -import getopt - -import conf -import debug -import world -import irclib -import drivers -import ircmsgs -import privmsgs -import schedule - -sys.path.append(conf.pluginDir) - -world.startedAt = time.time() - -class ConfigAfter376(irclib.IrcCallback): - public = False - def __init__(self, commands): - self.commands = commands - - def do376(self, irc, msg): - for command in self.commands: - msg = ircmsgs.privmsg(irc.nick, command, prefix=irc.prefix) - irc.queueMsg(msg) - -def handleConfigFile(): - nick = conf.config['nick'] - user = conf.config['user'] - ident = conf.config['ident'] - password = conf.config['password'] - irc = irclib.Irc(nick, user, ident, password) - for Class in privmsgs.standardPrivmsgModules: - callback = Class() - if hasattr(callback, 'configure'): - callback.configure(irc) - irc.addCallback(callback) - irc.addCallback(ConfigAfter376(conf.config['afterConnect'])) - drivers.newDriver(conf.config['server'], irc) - -def main(): - (optlist, filenames) = getopt.getopt(sys.argv[1:], 'Opc:') - if len(filenames) != 1: - conf.reportConfigError('Command line', 'No configuration file given.') - - for (option, argument) in optlist: - if option == '-c': - myLocals = {} - myGlobals = {} - execfile(argument, myGlobals, myLocals) - for (key, value) in myGlobals.iteritems(): - setattr(conf, key, value) - for (key, value) in myLocals.iteritems(): - setattr(conf, key, value) - else: - print 'Unexpected argument %s; ignoring.' % option - filename = filenames[0] - conf.processConfig(filename) - handleConfigFile() - schedule.addPeriodicEvent(world.upkeep, 300) - try: - while world.ircs: - drivers.run() - except: - try: - debug.recoverableException() - except: # It must have been deadly on purpose. - sys.exit(0) - -if __name__ == '__main__': - if '-p' in sys.argv: - import profile - sys.argv.remove('-p') - profile.run('main()', '%i.prof' % time.time()) - if '-O' in sys.argv: - import psyco - psyco.full() - main() - else: - main() -# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: diff --git a/src/callbacks.py b/src/callbacks.py index e99492d8d..ea2fc66b8 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -443,7 +443,7 @@ class Privmsg(irclib.IrcCallback): def configure(self, irc): fakeIrc = ConfigIrcProxy(irc) - for args in conf.config['onStart']: + for args in conf.commandsOnStart: args = args[:] command = args.pop(0) if self.isCommand(command): @@ -461,13 +461,14 @@ class Privmsg(irclib.IrcCallback): irclib.IrcCallback.__call__(self, irc, msg) # Now, if there's anything in the rateLimiter... msg = self.rateLimiter.get() - if msg: + while msg: s = addressed(irc.nick, msg) try: args = tokenize(s) self.Proxy(irc, msg, args) except SyntaxError, e: irc.queueMsg(reply(msg, str(e))) + msg = self.rateLimiter.get() def isCommand(self, methodName): # This function is ugly, but I don't want users to call methods like @@ -496,7 +497,7 @@ class Privmsg(irclib.IrcCallback): funcname = f.im_func.func_name debug.msg('%s took %s seconds' % (funcname, elapsed), 'verbose') - _r = re.compile(r'^([\w_-]+)(?:\s+|$)') + _r = re.compile(r'^([\w_-]+)(?:\[|\s+|$)') def doPrivmsg(self, irc, msg): s = addressed(irc.nick, msg) #debug.printf('Privmsg.doPrivmsg: s == %r' % s) diff --git a/src/conf.py b/src/conf.py index 7bc977c82..5f2a5900d 100644 --- a/src/conf.py +++ b/src/conf.py @@ -197,78 +197,4 @@ driverModule = 'asyncoreDrivers' ############################### version ='0.70.0' -import debug - -class ConfigurationDict(dict): - def __init__(self, L=()): - L = [(key.lower(), value) for (key, value) in L] - dict.__init__(self, L) - - def __setitem__(self, key, value): - dict.__setitem__(self, key.lower(), value) - - def __getitem__(self, key): - try: - return dict.__getitem__(self, key.lower()) - except KeyError: - return '' - - def __contains__(self, key): - return dict.__contains__(self, key.lower()) - -config = ConfigurationDict() - -def reportConfigError(filename, msg): - debug.unrecoverableError('%s: %s' % (filename, msg)) - -def processConfig(filename): - import email - from callbacks import tokenize - try: - fd = file(filename) - m = email.message_from_file(fd) - for (k, v) in m.items(): - config[k] = v - if 'nick' not in config: - reportConfigError(filename, 'No nick defined.') - if 'server' not in config: - reportConfigError(filename, 'No server defined.') - if 'user' not in config: - config['user'] = config['nick'] - if 'ident' not in config: - config['ident'] = config['nick'] - server = config['server'] - if ':' in server: - (server, port) = server.split(':', 1) - try: - server = (server, int(port)) - except ValueError: - reportConfigError(filename, 'Server has an invalid port.') - else: - server = (server, 6667) - config['server'] = server - text = m.get_payload() - while 1: - # Gotta remove all extra newlines. - newtext = text.replace('\n\n\n', '\n\n') - if newtext == text: - break - else: - text = newtext - lines = text.splitlines() - # There are 2 newlines separating the commands to be run on startup - # from the commands to be run after connecting. This separates them - # based on those newlines. - (onStart, afterConnect) = tuple(itersplit(lines,lambda s: not s,True)) - config['onStart'] = [tokenize(s) for s in onStart if s and s[0] != '#'] - config['afterConnect'] = [s for s in afterConnect if s and s[0] != '#'] - - except IOError, e: - reportConfigError(filename, e) - except email.Errors.HeaderParseError, e: - s = str(e) - problem = s[s.rfind('`')+1:-2] - msg = 'Invalid configuration format: %s' % problem - reportConfigError(filename, msg) - # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: diff --git a/src/privmsgs.py b/src/privmsgs.py index f64a02c49..1c1046cae 100644 --- a/src/privmsgs.py +++ b/src/privmsgs.py @@ -31,19 +31,13 @@ from fix import * -import gc -import os import new -import sys -import imp -import linecache import conf import debug import world import ircdb import ircmsgs -import drivers import ircutils import callbacks @@ -109,233 +103,4 @@ class CapabilityCheckingPrivmsg(callbacks.Privmsg): else: irc.error(msg, conf.replyNoCapability % self.capability) - -class OwnerCommands(CapabilityCheckingPrivmsg): - capability = 'owner' - def __init__(self): - callbacks.Privmsg.__init__(self) - setattr(self.__class__, 'exec', self._exec) - - def eval(self, irc, msg, args): - """""" - if conf.allowEval: - s = getArgs(args) - try: - irc.reply(msg, repr(eval(s))) - except Exception, e: - irc.reply(msg, debug.exnToString(e)) - else: - irc.error(msg, conf.replyEvalNotAllowed) - - def _exec(self, irc, msg, args): - """""" - if conf.allowEval: - s = getArgs(args) - try: - exec s - irc.reply(msg, conf.replySuccess) - except Exception, e: - irc.reply(msg, debug.exnToString(e)) - else: - irc.error(msg, conf.replyEvalNotAllowed) - - def setdefaultcapability(self, irc, msg, args): - """ - - Sets the default capability to be allowed for any command. - """ - capability = getArgs(args) - conf.defaultCapabilities[capability] = True - irc.reply(msg, conf.replySuccess) - - def unsetdefaultcapability(self, irc, msg, args): - """ - - Unsets the default capability for any command. - """ - capability = getArgs(args) - del conf.defaultCapabilities[capability] - irc.reply(msg, conf.replySuccess) - - def settrace(self, irc, msg, args): - """takes no arguments - - Starts the function-tracing debug mode; beware that this makes *huge* - logfiles. - """ - sys.settrace(debug.tracer) - irc.reply(msg, conf.replySuccess) - - def unsettrace(self, irc, msg, args): - """takes no arguments - - Stops the function-tracing debug mode.""" - sys.settrace(None) - irc.reply(msg, conf.replySuccess) - - def ircquote(self, irc, msg, args): - """ - - Sends the raw string given to the server. - """ - s = getArgs(args) - try: - m = ircmsgs.IrcMsg(s) - irc.queueMsg(m) - except Exception: - debug.recoverableException() - irc.error(msg, conf.replyError) - - def quit(self, irc, msg, args): - """[] - - Exits the program with the given return value (the default is 0) - """ - try: - i = int(args[0]) - except (ValueError, IndexError): - i = 0 - for driver in drivers._drivers.itervalues(): - driver.die() - for irc in world.ircs[:]: - irc.die() - debug.exit(i) - - def flush(self, irc, msg, args): - """takes no arguments - - Runs all the periodic flushers in world.flushers. - """ - world.flush() - irc.reply(msg, conf.replySuccess) - - def upkeep(self, irc, msg, args): - """takes no arguments - - Runs the standard upkeep stuff (flushes and gc.collects()). - """ - world.upkeep() - if gc.garbage: - if len(gc.garbage) < 10: - irc.reply(msg, 'Garbage! %r' % gc.garbage) - else: - irc.reply(msg, 'Garbage! %s items.' % len(gc.garbage)) - else: - irc.reply(msg, conf.replySuccess) - - def set(self, irc, msg, args): - """ - - Sets the runtime variable to . Currently used variables - include "noflush" which, if set to true value, will prevent the - periodic flushing that normally occurs. - """ - (name, value) = getArgs(args, optional=1) - world.tempvars[name] = value - irc.reply(msg, conf.replySuccess) - - def unset(self, irc, msg, args): - """ - - Unsets the value of variables set via the 'set' command. - """ - name = getArgs(args) - try: - del world.tempvars[name] - irc.reply(msg, conf.replySuccess) - except KeyError: - irc.error(msg, 'That variable wasn\'t set.') - - def load(self, irc, msg, args): - """ - - Loads the plugin from the plugins/ directory. - """ - name = getArgs(args) - if name in [cb.name() for cb in irc.callbacks]: - irc.error(msg, 'That module is already loaded.') - return - try: - moduleInfo = imp.find_module(name) - except ImportError: - irc.error(msg, 'No plugin %s exists.' % name) - return - module = imp.load_module(name, *moduleInfo) - linecache.checkcache() - callback = module.Class() - if hasattr(callback, 'configure'): - callback.configure(irc) - irc.addCallback(callback) - irc.reply(msg, conf.replySuccess) - - ''' - def superreload(self, irc, msg, args): - """ - - Reloads a module, hopefully such that all vestiges of the old module - are gone. - """ - name = getArgs(args) - world.superReload(__import__(name)) - irc.reply(msg, conf.replySuccess) - ''' - - def reload(self, irc, msg, args): - """ - - Unloads and subsequently reloads the callback by name; use the 'list' - command to see a list of the currently loaded callbacks. - """ - name = getArgs(args) - callbacks = irc.removeCallback(name) - if callbacks: - for callback in callbacks: - callback.die() - del callback - gc.collect() - try: - moduleInfo = imp.find_module(name) - module = imp.load_module(name, *moduleInfo) - linecache.checkcache() - callback = module.Class() - if hasattr(callback, 'configure'): - callback.configure(irc) - irc.addCallback(callback) - irc.reply(msg, conf.replySuccess) - except ImportError: - for callback in callbacks: - irc.addCallback(callback) - irc.error(msg, 'No plugin %s exists.' % name) - else: - irc.error(msg, 'There was no callback %s.' % name) - - def unload(self, irc, msg, args): - """ - - Unloads the callback by name; use the 'list' command to see a list - of the currently loaded callbacks. - """ - name = getArgs(args) - callbacks = irc.removeCallback(name) - if callbacks: - for callback in callbacks: - callback.die() - del callback - gc.collect() - irc.reply(msg, conf.replySuccess) - else: - irc.error(msg, 'There was no callback %s' % name) - - def cvsup(self, irc, msg, args): - """takes no arguments""" - irc.reply(msg, str(os.system('cvs up'))) - - def say(self, irc, msg, args): - """ """ - (channel, text) = getArgs(args, needed=2) - irc.queueMsg(ircmsgs.privmsg(channel, text)) - - -standardPrivmsgModules = [OwnerCommands] - # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: diff --git a/src/template.py b/src/template.py new file mode 100755 index 000000000..f02bb123a --- /dev/null +++ b/src/template.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python + +### +# Copyright (c) 2002, Jeremiah Fincher +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +### + +""" +This is the template for bots. scripts/setup.py uses this file to make +customized startup files for bots. +""" + +import re +import sys +import time +import optparse + +started = time.time() +sys.path.insert(0, 'src') + +import conf + +conf.commandsOnStart = %%onStart%% + +afterConnect = %%afterConnect%% + +configVariables = %%configVariables%% + +for (name, value) in configVariables.iteritems(): + setattr(conf, name, value) + + +def main(): + import debug + import world + import drivers + world.startedAt = started + try: + while world.ircs: + drivers.run() + except: + try: + debug.recoverableException() + except: # It must've been deadly for a reason :) + sys.exit(0) + +if __name__ == '__main__': + ### + # Options: + # -p (profiling) + # -O (optimizing) + # -n, --nick (nick) + # -s, --server (server) + # --startup (commands to run onStart) + # --connect (commands to run afterConnect) + # --config (configuration values) + parser = optparse.OptionParser(usage='Usage: %prog [options]', + version='supybot %s' % conf.version) + parser.add_option('-P', '--profile', action='store_true', dest='profile', + help='enables profiling') + parser.add_option('-O', action='count', dest='optimize', + help='-O optimizes asserts out of the code; ' \ + '-OO optimizes asserts and uses psyco.') + parser.add_option('-n', '--nick', action='store', + dest='nick', default=%%nick%%, + help='nick the bot should use') # FIXME (quotes) + parser.add_option('-s', '--server', action='store', + dest='server', default=%%server%%, + help='server to connect to') # FIXME (quotes) + parser.add_option('-u', '--user', action='store', + dest='user', default=%%user%%, + help='full username the bot should use') + parser.add_option('-i', '--ident', action='store', + dest='ident', default=%%ident%%, + help='ident the bot should use') + parser.add_option('-p', '--password', action='store', + dest='password', default=%%password%%, + help='server password the bot should use') + parser.add_option('--startup', action='append', dest='onStart', + help='file of additional commands to run at startup.') + parser.add_option('--connect', action='append', dest='afterConnect', + help='file of additional commands to run after connect') + parser.add_option('--config', action='append', dest='conf', + help='file of configuration variables to set') + + (options, args) = parser.parse_args() + + if options.optimize: + __builtins__.__debug__ = False + if options.optimize > 1: + import psyco + psyco.full() + + if options.onStart: + for filename in options.onStart: + fd = file(filename) + for line in fd: + conf.commandsOnStart.append(line.rstrip()) + fd.close() + + if options.afterConnect: + for filename in options.afterConnect: + fd = file(filename) + for line in fd: + afterConnect.append(line.rstrip()) + fd.close() + + assignmentRe = re.compile('\s*[:=]\s*') + if options.conf: + for filename in options.conf: + fd = file(filename) + for line in fd: + (name, valueString) = assignmentRe.split(line.rstrip(), 1) + try: + value = eval(valueString) + except Exception, e: + sys.stderr.write('Invalid configuration value: %r' % \ + valueString) + sys.exit(-1) + + sys.path.append(conf.pluginDir) + + nick = options.nick + user = options.user + ident = options.ident + password = options.password + + if ':' in options.server: + serverAndPort = options.server.split(':', 1) + serverAndPort[1] = int(serverAndPort[1]) + server = tuple(serverAndPort) + else: + server = (options.server, 6667) + + import irclib + import ircmsgs + import drivers + import callbacks + import OwnerCommands + + class ConfigAfter376(irclib.IrcCallback): + public = False + def __init__(self, commands): + self.commands = commands + def do376(self, irc, msg): + for command in self.commands: + msg = ircmsgs.privmsg(irc.nick, command, prefix=irc.prefix) + irc.queueMsg(msg) + do377 = do376 + + # We pre-tokenize the commands just to save on significant amounts of work. + conf.commandsOnStart = map(callbacks.tokenize, conf.commandsOnStart) + + irc = irclib.Irc(nick, user, ident, password) + callback = OwnerCommands.Class() + callback.configure(irc) + irc.addCallback(callback) + irc.addCallback(ConfigAfter376(afterConnect)) + driver = drivers.newDriver(server, irc) + + if options.profile: + import profile + profile.run('main()') + else: + main() + + + +# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: