#!/usr/bin/python # # modules.py # # -Python Darkness Bot- # Module support module # # Copyright by Jannis A. Schnitzer # Created by Jannis A. Schnitzer on 2008-09-04 # # This library 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 2 of the License, or (at your option) # any later version. # # This library 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 library; if not, write to the Free Software Foundation, Inc., 59 # Temple Place - Suite 330, Boston, MA 02111-1307, USA. # import irc import auth import sys from essentials import * class Help: "Help wrapper class" def __init__(self, modules): self.modules = modules self.helpstr = "This is Xjs' modularized python Darkness Bot." self.helpstr2 = "Type 'help all' to show descriptions of all modules and commands, 'help modules' to show all modules," self.helpstr3 = "'help ' to show all commands of a specific module, or help to show help about a specific command." def module(self, modname, outlong = False): "Returns help for the given module, and long help for all commands there if 'outlong' is True" if not self.modules.modlist.has_key(modname): return "" general = helpstr(self.modules.modlist[modname]) if general: general = " "+general modstr = "["+modname+"]"+general cmdlist = [modstr] cmdstr = " " count = 0 if not self.modules.cmdlist.has_key(modname): return [modstr, cmdstr] for i in self.modules.cmdlist[modname]: cstr = i.command if not cstr: continue if not outlong: count = count + 1 cmdstr += cstr + ", " else: cmdstr = " "+self.cmd_long_raw(i) cmdlist.append(cmdstr) if not outlong: return [modstr, cmdstr[:len(cmdstr)-2]] else: return cmdlist def cmd_long_raw (self, cmd): "Returns a fancy string from a command entry" if not cmd.command: return "" authstr = "" if cmd.access: authstr = " (Access level "+str(cmd.access)+")" return "\'"+cmd.command+"\': "+helpstr(cmd.get_fcn())+authstr def cmd_long(self, cmdname): "Searches for the command in the databases and returns a string" for i in self.modules.cmdlist: for j in self.modules.cmdlist[i]: if j.command == cmdname: return self.cmd_long_raw(j) return "Command "+cmdname+" was not found!" def version(self, irc): "Returns bot version" return irc.version def empty(): return None class Modules: "Module manager: supports loading and unloading of modules" def __init__(self, irc): self.irc = irc self.modlist = {} self.classlist = {} self.activelist = {} self.typelist = {} # contains lists for each typelist[type] self.cmdlist = {} # contains Commands for later lookup self.typelist[types.ALL] = {"":{}} pm = Command("-ModuleManager-") pm.ctype = types.DMSG pm.command = "module" pm.chanre = "*" pm.access = 100 pm.clas = self pm.fcn = "pmodules" ph = Command("-ModuleManager-") ph.ctype = types.DMSG ph.command = "help" ph.chanre = "*" ph.access = 0 ph.clas = self ph.fcn = "p_help" pv = Command("-ModuleManager-") pv.ctype = types.DMSG pv.command = "version" pv.chanre = "*" pv.access = 0 pv.clas = self pv.fcn = "p_version" self.typelist[types.DMSG] = {} self.typelist[types.DMSG]["module"] = {} self.typelist[types.DMSG]["module"]["-ModuleManager-"] = pm self.typelist[types.DMSG]["help"] = {} self.typelist[types.DMSG]["help"]["-ModuleManager-"] = ph self.typelist[types.DMSG]["version"] = {} self.typelist[types.DMSG]["version"]["-ModuleManager-"] = pv self.cmdlist["-AuthModule-"] = [] for ac in self.irc.auth.cmdlist: if not self.typelist.has_key(ac.ctype): self.typelist[ac.ctype] = {} if ac.has_cmd(): self.typelist[ac.ctype][ac.command] = {} self.typelist[ac.ctype][ac.command]["-AuthModule-"] = ac else: self.typelist[ac.ctype] = {} self.typelist[ac.ctype]["-AuthModule-"] = ac self.cmdlist["-AuthModule-"].append(ac) self.modlist["-AuthModule-"] = self.irc.auth # for the helpstring ;-) self.cmdlist["-ModuleManager-"] = [pm, ph] self.modlist["-ModuleManager-"] = self def __del__(self): nlist = [] for i in self.classlist: nlist.append(i) for i in nlist: self.ondelete(i) del self.classlist[i] def pmodules(self, msg): "[add, del] Activate or deactivate modules from IRC" if len(msg.arglist) < 2: return False name = msg.arglist[1] if msg.arglist[0] == "add": retval = self.addmod(name) if retval: xsend(self.irc, msg.sender.nick, msg.channel, "Successfully loaded module "+name+".") else: xsend(self.irc, msg.sender.nick, msg.channel, "Error while loading module "+name+".") elif msg.arglist[0] == "del": self.delmod(name) xsend(self.irc, msg.sender.nick, msg.channel, "Successfully deactivated module "+name+".") return True def p_version(self, msg): helpc = Help(self) self.irc.notice(msg.sender.nick, helpc.version(self.irc)) return True def gethelp (self, name, module = False): if module: if self.modlist.has_key(name): return helpstr(self.modlist[name]) for i in (types.MSG, types.DMSG, types.NOTICE): if self.typelist.has_key[i]: if self.typelist[i].has_key(name): return helpstr(self.typelist[i][name].values()[0].get_fcn()) def p_help(self, msg): "Show this help" helpc = Help(self) if not len (msg.arglist): if msg.channel[0] == '#': dest = msg.channel prefix = msg.sender.nick+": " else: dest = msg.sender.nick prefix = "" if not prefix: self.irc.notice(dest, helpc.helpstr) else: self.irc.msg(dest, prefix+helpc.helpstr) self.irc.notice(msg.sender.nick, helpc.helpstr2) self.irc.notice(msg.sender.nick, helpc.helpstr3) else: if msg.arglist[0] == "modules": mylist = [] modlist = self.modlist.keys() modlist.sort() for i in modlist: mylist.extend(helpc.module(i)) self.irc.notice(msg.sender.nick, "Modules listing:") for i in mylist: self.irc.notice(msg.sender.nick, i) elif msg.arglist[0] == "all": mylist = [] modlist = self.modlist.keys() modlist.sort() for i in modlist: mylist.extend(helpc.module(i, True)) self.irc.notice(msg.sender.nick, "Long modules and commands listing:") for i in mylist: self.irc.notice(msg.sender.nick, i) elif self.modlist.has_key(msg.arglist[0]): modlist = helpc.module(msg.arglist[0], True) for i in modlist: self.irc.notice(msg.sender.nick, i) else: self.irc.notice(msg.sender.nick, helpc.cmd_long(msg.arglist[0])) return True def addmod (self, name): # name is the modname, and it's saved in the database under this name. "Loads module if it's not already loaded, adds it to the database, activates it and reads its given commands" if self.classlist.has_key(name) and self.activelist[name]: self.ondelete(name) themod = reload (self.modlist[name]) for i in self.typelist: # i contains the different types now if i == types.MSG or i == types.NOTICE or i == types.DMSG: for j in self.typelist[i]: # j contains the func name if self.typelist[i][j].has_key(name): del (self.typelist[i][j][name]) else: if self.typelist[i].has_key(name): del (self.typelist[i][name]) for i in self.cmdlist[name]: del i # Ok, cleaned up. else: themod = None try: modules = __import__("modules."+name) # (we hope that works ;-) modules = reload(modules) themod = getattr(modules, name) except: print "Sorry, import error while importing "+str(name)+" module: "+str(sys.exc_info()[0]) return False self.modlist[name] = themod there_is_parse = "parse" in dir(themod) and "register" in dir(themod.parse) # register gives us all its commands. if there_is_parse: try: self.classlist[name] = themod.parse(self.irc) # parse gets the irc object as argument. except: print "Sorry, import error while importing "+str(name)+" module: "+str(sys.exc_info()[0]) return False else: print "Sorry, import error while importing "+str(name)+" module: module not valid" return False self.activelist[name] = True self.cmdlist[name] = self.classlist[name].register() for cmd in self.cmdlist[name]: if not self.typelist.has_key(cmd.ctype): self.typelist[cmd.ctype] = {} if cmd.has_cmd(): # It's the same for DMSGs as for Channel PRIVMSGs here. if not self.typelist[cmd.ctype].has_key(cmd.command): self.typelist[cmd.ctype][cmd.command] = {} self.typelist[cmd.ctype][cmd.command][cmd.module] = cmd elif cmd.ctype == types.MSG or cmd.ctype == types.DMSG or cmd.ctype == types.NOTICE or cmd.ctype == types.ALL: if not self.typelist[cmd.ctype].has_key(""): self.typelist[cmd.ctype][""] = {} self.typelist[cmd.ctype][""][cmd.module] = cmd else: self.typelist[cmd.ctype][cmd.module] = cmd return True def ondelete(self, name): try: self.classlist[name].ondelete() except: pass def delmod (self, name): "Deactivates the given module" self.ondelete(name) self.activelist[name] = False for cmd in self.cmdlist[name]: if cmd.has_cmd(): self.typelist[cmd.ctype][cmd.command][cmd.module].active = False elif cmd.ctype == types.MSG or cmd.ctype == types.DMSG or cmd.ctype == types.NOTICE or cmd.ctype == types.ALL: self.typelist[cmd.ctype][""][cmd.module].active = False else: self.typelist[cmd.ctype][cmd.module].active = False #del self.cmdlist[name] def call (self, cmd, msg): retval = False try: #print "Let "+i+" parse msg..." retval = getattr(cmd.clas, cmd.fcn).__call__(msg) except NameError: exc_info = sys.exc_info() print "NameError: "+str(cmd.command)+"/"+str(cmd.fcn)+" of "+cmd.module+" - message: "+str(exc_info[1]) print "Line: "+str(exc_info[2].tb_lineno) return False except (SystemExit, IRCQuit), e: exc_info = sys.exc_info() print "Raised", str(exc_info[0]) raise except: exc_info = sys.exc_info() print "Error ("+str(exc_info[0])+") in "+str(cmd.command)+"/"+str(cmd.fcn)+" of "+cmd.module+" - message: "+str(exc_info[1]) print "Line: "+str(exc_info[2].tb_lineno) return False return retval def parse(self, msg): mtype = msg.mtype if msg.dmsg: mtype = types.DMSG if not self.typelist.has_key(mtype): self.typelist[mtype] = {} if msg.has_cmd(): if not self.typelist[mtype].has_key(msg.command): if (mtype == types.MSG or mtype == types.DMSG or mtype == types.NOTICE) and self.typelist[mtype].has_key("") and len(self.typelist[mtype][""]) > 0: cmd = self.typelist[mtype][""] elif self.typelist[types.ALL].has_key(msg.command): cmd = self.typelist[types.ALL][msg.command] else: cmd = self.typelist[types.ALL][""] else: cmd = self.typelist[mtype][msg.command] elif (mtype == types.MSG or mtype == types.DMSG or mtype == types.NOTICE): if self.typelist[mtype].has_key("") and len(self.typelist[mtype][""]) > 0: cmd = self.typelist[mtype][""] else: cmd = self.typelist[types.ALL][""] else: cmd = self.typelist[mtype] if len(cmd) == 0: cmd = self.typelist[types.ALL][""] for i in cmd: if not cmd[i].active or (cmd[i].access and (self.irc.auth.is_authed(msg.sender.nick) < cmd[i].access)): continue if self.call(cmd[i], msg): break