Source code for tasbot.Plugin

# -*- coding: utf-8 -*-
from customlog import Log
import sys
import traceback
import inspect
import ctypes
import plugins
import threading

def _async_raise(tid, exctype):
    '''Raises an exception in the threads with id tid (never seen working)'''
    if not inspect.isclass(exctype):
        raise TypeError("Only types can be raised (not instances)")
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
    if res == 0:
        Log.Error("Cannot kill thread %i" % tid)
    if res != 1:
        # """if it returns a number greater than one, you're in trouble, 
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
    
[docs]class PluginThread(threading.Thread): """tiny wrapper to execute function with args in a thread""" def __init__(self, func, *args ): self.func = func self.args = args threading.Thread.__init__(self)
[docs] def run(self): self.func( *self.args )
[docs]class IPlugin(object): """base class all plugins should derive from (and expose fitting ctor)""" def __init__(self,name,tasclient): self.tasclient = tasclient self.name = name self.logger = Log.getPluginLogger( name ) self.dying = False self.threads = []
[docs] def ondestroy(self): """tell myself i'm dying and try to stop all my threads""" self.dying = True try: for thread in self.threads: try: thread.join(5) except AttributeError: #if its an old style id still try to terminate it (will prolly fail tho) _async_raise( thread, SystemExit ) except Exception, e: self.logger.Critical( "detroying %s plugin failed"%self.name) self.logger.Except( e ) self.threads = filter( lambda thread: isinstance(thread, PluginThread) and thread.isAlive(), self.threads ) if len(self.threads): self.logger.Error( "%d threads left alive after destroy was called" )
[docs] def startThread(self,func,*args): """run a given function with args in a new thread that is added to an internal list""" self.threads.append( PluginThread(func, *args) ) #app exists if only daemon threads are left alive self.threads[-1].daemon = True self.threads[-1].start()
[docs]class PluginHandler(object): """ manage runtime loaded modules (plugins) """ def __init__(self,main): self.app = main self.plugins = dict() self.pluginthreads = dict()
[docs] def addplugin(self,name,tasc): """try to import module name and init it""" if name in self.plugins: Log.bad("Plugin %s is already loaded" % name) return try: code = __import__(name) except ImportError, imp: Log.Error("Cannot load plugin "+name) Log.Except( imp ) return try: self.plugins.update([(name,code.Main(name,tasc))]) except TypeError, t: self.plugins.update([(name,code.Main())]) Log.Error( 'loaded old-style plugin %s. Please derive from IPlugin'%name) self.plugins[name].socket = tasc.socket try: if "onload" in dir(self.plugins[name]): self.plugins[name].onload(tasc) if "onloggedin" in dir(self.plugins[name]) and self.app.connected: self.plugins[name].onloggedin(tasc.socket) except Exception, e: Log.Except( e ) return Log.loaded("Plugin " + name)
[docs] def unloadplugin(self,name): """ unload plugin, stop all its threads via ondestroy and remove from interal list""" if not name in self.plugins: Log.Error("Plugin %s not loaded"%name) return try: if "ondestroy" in dir(self.plugins[name]): self.plugins[name].ondestroy() self.plugins.pop(name) Log.notice("%s Unloaded" % name) except Exception, e: Log.Error("Cannot unload plugin "+name) Log.Error("Use forceunload to remove it anyway") Log.Except( e )
[docs] def unloadAll(self): """convenience function to unload all plugins at once""" #make copy because unload changes the dict names = [ name for name in self.plugins ] for name in names: self.unloadplugin(name)
[docs] def forceunloadplugin(self,name,tasc): """simply removes name from internal list, only call if unload else fails""" if not name in self.plugins: Log.Error("Plugin %s not loaded"%name) return self.plugins.pop(name) Log.bad("%s UnLog.loaded(Forced)" % name)
[docs] def reloadplugin(self,name): """broken""" if not name in self.plugins: Log.Error("Plugin %s not loaded"%name) return try: if "ondestroy" in dir(self.plugins[name]): self.plugins[name].ondestroy() Log.notice("%s Unloaded" % name) except: Log.Error("Cannot unload plugin "+name) Log.Error("Use forceunload to remove it anyway") Log.Error( traceback.print_exc() ) try: code = reload(sys.modules[name]) except: Log.Error("Cannot reload plugin %s!" % name) return self.plugins.update([(name,code.Main())]) self.plugins[name].socket = self.app.tasclient.socket try: if "onload" in dir(self.plugins[name]): self.plugins[name].onload(self.app.tasclient) except: Log.Error("Cannot load plugin "+name) Log.Error( traceback.print_exc() ) return Log.loaded("Plugin " + name)
[docs] def forall(self,func_name,*args): """ execute a given function(name) on all plugins that expose it""" for name,plugin in filter(lambda (name,plugin): func_name in dir(plugin), self.plugins.iteritems() ): try: func = getattr(plugin,func_name) func( *args ) except SystemExit: raise SystemExit(0) except Exception,e : Log.Error("PLUGIN %s ERROR calling %s"%(func_name,name)) Log.Except( e )
[docs] def onconnected(self): self.forall( "onconnected" )
def ondisconnected(self): self.forall( "ondisconnected")
[docs] def onmotd(self,content): self.forall( "onmotd", content)
[docs] def onsaid(self,channel,user,message): self.forall( "onsaid",channel,user,message)
[docs] def onsaidex(self,channel,user,message): self.forall( "onsaidex",channel,user,message)
[docs] def onsaidprivate(self,user,message): """react on a few given keywords and also pass the call to all plugins""" args = message.split(" ") if args[0].lower() == "!reloadconfig" and user in self.app.admins: self.app.ReloadConfig() if args[0].lower() == "!unloadplugin" and user in self.app.admins and len(args) == 2: try: self.unloadplugin(args[1]) except: Log.bad("Unloadplugin failed") Log.Error( traceback.print_exc() ) if args[0].lower() == "!loadplugin" and user in self.app.admins and len(args) == 2: try: self.addplugin(args[1],self.app.tasclient) except: Log.bad("addplugin failed") Log.Error( traceback.print_exc() ) if args[0].lower() == "!reloadplugin" and user in self.app.admins and len(args) == 2: try: self.reloadplugin(args[1]) except: Log.bad("Unloadplugin failed") Log.Error( traceback.print_exc() ) self.forall( "onsaidprivate",user,message)
[docs] def onloggedin(self,socket): self.forall( "onloggedin",socket)
[docs] def onpong(self): self.forall( "onpong" )
[docs] def oncommandfromserver(self,command,args,socket): self.forall( "oncommandfromserver",command,args,socket)
[docs] def onexit(self): self.forall( "onexit" )
[docs] def ondisconnected(self): self.forall( "ondisconnected" )