class NGram: """Class for an N-Gram model of a natural language corpus""" def __init__(self, corpus, n, debug=False, model=None): self.log = Log() if debug: self.log.setLevel(self.log.DEBUG) self.n = n self.log.info("Initialising {}-Gram model...".format(n)) if model: self.model = model else: if type(corpus) == type([]): self.buildModel(corpus) else: self.buildModel([corpus]) self.log.info("{}-Gram model ready for use".format(self.n)) def buildModel(self, list_corpus): """Build a model from a list of strings""" #Pad out the message corpus = "" self.log.info("Creating N-Gram model from {} string{}".format(len(list_corpus), "s" if len(list_corpus)>1 else "")) for i in list_corpus: #Remove punctuation i = i.replace(","," ").replace("."," ") corpus += " <s> "*(self.n-1) + i + " <s> "*(self.n-2)+" <end>" corpus = corpus.strip().lower() nlen = [] self.log.info("Splitting corpus into {} length strings".format(self.n)) corpus = [x for x in corpus.split(" ") if x != ''] for i in range(len(corpus)-1): nlen.append(corpus[i:i+self.n]) if len(nlen) > 6000: nlen = nlen[:6000] qqq = len(nlen) self.log.info("Processing {} {}-length strings".format(len(nlen), self.n)) self.model = [] endWords = [] givenWords = [] for i in nlen: word = i[-1] given = i[:-1] if word not in endWords: endWords.append(word) if given not in givenWords: givenWords.append(given) self.log.info("Calculating probabilities...") #calculate probs percentages = range(10, 101, 10) for w in givenWords: p = givenWords.index(w) / qqq p = 100*p if p > percentages[0]: self.log.info("{}% done...".format(p)) percentages = percentages[1:] occurances = [x for x in nlen if x[:-1] == w] for x in occurances: y = x[-1] ##endword endswith = [z for z in occurances if z[-1] == y] ##Count number of occurrences try: modelAdd = [x[-1], w, 1.0 / (1+len(occurances)-len(endswith))] except ZeroDivisionError: ##Prob must be 1 modelAdd = [x[-1],w,1.0] self.model.append(modelAdd) self.log.info("{}-Gram model built!".format(self.n)) def generate(self): """Generate a possible message from the model""" self.log.info("Generating a possible string...") try: m = "<s>" while m.split(" ")[-1] != "<end>": m += " " + random.choice(self.getAllFromGiven(m.split(" ")[-self.n+1:])) self.log.info("Generated!") return m.replace("<s>","").replace("<end>","") except IndexError: return "Could not generate... not enough data!" def getAllFromGiven(self, given): """Get all instances of x, where (given, x) is a member of the model""" j = [] for i in self.model: if i[1] == given: j.append(i[0]) return j def getProb(self, word, given): """Get P(word|given)""" ##Find P(word | given) for i in self.model: g = listtostr_(i[1]) if i[0] == word and given == g: return i[2] return 0
class NGram: """Class for an N-Gram model of a natural language corpus""" def __init__(self, corpus, n, debug=False, model=None): self.log = Log() if debug: self.log.setLevel(self.log.DEBUG) self.n = n self.log.info("Initialising {}-Gram model...".format(n)) if model: self.model = model else: if type(corpus) == type([]): self.buildModel(corpus) else: self.buildModel([corpus]) self.log.info("{}-Gram model ready for use".format(self.n)) def buildModel(self, list_corpus): """Build a model from a list of strings""" #Pad out the message corpus = "" self.log.info("Creating N-Gram model from {} string{}".format( len(list_corpus), "s" if len(list_corpus) > 1 else "")) for i in list_corpus: #Remove punctuation i = i.replace(",", " ").replace(".", " ") corpus += " <s> " * (self.n - 1) + i + " <s> " * (self.n - 2) + " <end>" corpus = corpus.strip().lower() nlen = [] self.log.info("Splitting corpus into {} length strings".format(self.n)) corpus = [x for x in corpus.split(" ") if x != ''] for i in range(len(corpus) - 1): nlen.append(corpus[i:i + self.n]) if len(nlen) > 6000: nlen = nlen[:6000] qqq = len(nlen) self.log.info("Processing {} {}-length strings".format( len(nlen), self.n)) self.model = [] endWords = [] givenWords = [] for i in nlen: word = i[-1] given = i[:-1] if word not in endWords: endWords.append(word) if given not in givenWords: givenWords.append(given) self.log.info("Calculating probabilities...") #calculate probs percentages = range(10, 101, 10) for w in givenWords: p = givenWords.index(w) / qqq p = 100 * p if p > percentages[0]: self.log.info("{}% done...".format(p)) percentages = percentages[1:] occurances = [x for x in nlen if x[:-1] == w] for x in occurances: y = x[-1] ##endword endswith = [z for z in occurances if z[-1] == y] ##Count number of occurrences try: modelAdd = [ x[-1], w, 1.0 / (1 + len(occurances) - len(endswith)) ] except ZeroDivisionError: ##Prob must be 1 modelAdd = [x[-1], w, 1.0] self.model.append(modelAdd) self.log.info("{}-Gram model built!".format(self.n)) def generate(self): """Generate a possible message from the model""" self.log.info("Generating a possible string...") try: m = "<s>" while m.split(" ")[-1] != "<end>": m += " " + random.choice( self.getAllFromGiven(m.split(" ")[-self.n + 1:])) self.log.info("Generated!") return m.replace("<s>", "").replace("<end>", "") except IndexError: return "Could not generate... not enough data!" def getAllFromGiven(self, given): """Get all instances of x, where (given, x) is a member of the model""" j = [] for i in self.model: if i[1] == given: j.append(i[0]) return j def getProb(self, word, given): """Get P(word|given)""" ##Find P(word | given) for i in self.model: g = listtostr_(i[1]) if i[0] == word and given == g: return i[2] return 0
class CommandProcessor(threading.Thread): """Thread-based module for processing commands. Takes in commands from a queue, processes them asyncronously, outputs to another queue. ARGS: configfile -- The file to load in all of our configuration from """ def __init__(self, configfile = "command-proc.conf"): super(CommandProcessor, self).__init__() #Open a logger for superior feedback(tm) self.log = Log() #Alert the user that we're starting the processor self.log.info("Command Processor Created") self.log.newline() self.log.info("CMDPROC INIT") self.log.line() self.log.incIndent() #Set up some initial variables self.triggers = [] self.services = [] self.pipes = [] self.commands = {} self.loadedModules = ["Builtin"] #Read in the config self.log.info("Reading config...") self.config = YamlConf(configfile) self.log.info("Setting config...") #Load in the config #The getValue() or "Value" means that we have a default #So if we can't get something from the config, it defaults to something self.bot_name = self.config.getValue("bot", "name") or "Bot" self.bot_version = self.config.getValue("bot", "version") or "0.01" self.msg_prefix = self.config.getValue("bot", "msg_prefix") or "BOT: " self.command_prefix = self.config.getValue("bot", "cmd_prefix") or "!" debug = self.config.getValue("bot", "debug_logging") or False module_path = self.config.getValue("modules", "path") or "." #This means python will be able to find modules #When python looks for things, it goes down a so-called PYTHONPATH #We give it some new directories to look in self.module_path = module_path sys.path.insert(0, module_path) initial_modules = self.config.getValue("modules", "load") or [] inital_triggers = self.config.getValue("triggers") or [] self.admins = self.config.getValue("admins") self.log.info("Read config.") #callback means that we run a function once we're done #processing something, so like we can go # output = process("!hello") # callback(output) #By default it's just print, so we print the output self.callback = print #Set up the input and output queues self.log.info("Initilising command queue...") self.cmdQ = queue.Queue() self.log.info("Initilising output queue...") self.outputQ = queue.Queue() #Set up some stuff so we can tell the commandprocessor to stop #when we want to exit, we turn the Event on self.log.info("Initilising requests...") self.stopReq = threading.Event() self.stopNOW = threading.Event() #We have a few "initial modules", things to load on startup self.log.info("Setting up modules...") for i in initial_modules: self.loadModule(i) #Same goes for triggers self.log.info("Setting up triggers...") for i in inital_triggers: self.addTrigger(i, inital_triggers[i]) #If the user wants more information than usual, they can set debug to True self.log.info("Setting verbosity...") if debug: self.log.setLevel(self.log.DEBUG) #For reference: an underscore before the function name means that it only has INTERNAL use, #i.e nobody outside this class should call _addUtilityCommands() self._addUtilityCommands() #Tell the user that we've finished setting everything up self.log.line() self.log.info("FINISHED CMDPROC INIT") self.log.newline() def join(self, timeout=None): """When the owner of CommandProcessor wants us to exit, we alert the user that we've recieved the quit request, and shut everythig down """ self.log.info("QUITTING") self.log.line("Q") #Tell the processing function to stop self.stopReq.set() #Join the thread to the main process, #wait for `timeout` seconds before forcing it to. super(Thread, self).join(timeout) def addCommand(self, function_name, function_object, help=None, module="Builtin"): """Add a command to the processor ARGS: function_name: The name you wish to call the function by, i.e "func" function_object: The actual object to run when you call the command """ self.log.info("Adding command {}".format(function_name)) #Check if we've already got a command by name `function_name` if function_name in self.commands: self.log.info("Command {} already registered. Overwriting") #Make a nice little command object, for keeping track of it com = Command(function_name, function_object, help=help, module=module) #Make sure it actually worked yo if com.success: self.commands[function_name] = com else: self.log.info("Failed to add command") def removeCommand(self, function_name): """WHAT THE FEK DO YOU THINK IT DOES? MAKE CAKE? NO. IT REMOVES A COMMAND!""" #Check that we've actually got a command by that name if function_name in self.commands: #If we do, delete it! del self.commands[function_name] self.log.info("Succesfully removed {}".format(function_name)) else: #Otherwise, we can't. Because you can't delete something that doesn't exist, dummy self.log.info("Could not remove non-existent function {}".format(function_name)) def addTrigger(self, trigger_text, replacement_text): """Add a text-based trigger - will send replacement_text when trigger_text is in a given message""" #Check if we've got a trigger about that already, because we might for trigger in self.triggers: if trigger.trigger == trigger_text: #If we do, modifify it to the new trigger trigger.send_text = replacement_text return ("Modified trigger to {}".format(trigger)) #If we haven't got a trigger about `trigger_text`, #make a new one self.triggers.append(Trigger(trigger_text, replacement_text)) self.log.info("Added {}".format(self.triggers[-1])) return ("Added -- {}".format(self.triggers[-1])) def writeConfig(self): """Save the config, all currently loaded modules will be saved to init_modules""" self.log.info("Writing config...") #Add all of our triggers to a nice little dictionary, #So we can write it to a file trigs = {} for i in self.triggers: trigs[i.trigger] = i.send_text settings = { "bot" : { "name": self.bot_name, "version": self.bot_version, "msg_prefix": self.msg_prefix, "cmd_prefix": self.command_prefix, "debug_logging": False }, "modules" : { "path": self.module_path, "load": self.loadedModules }, "triggers": trigs, "ai": { "model_directory": "models" }, "admins": self.admins } self.log.info(settings) #Write the config in YAML format with open("command-proc.conf", "w") as f: f.write(pyaml.dump(settings)) self.log.info("Written") def removeTrigger(self, txt): """Remove the trigger with `trigger_text` == `txt`""" for trigger in self.triggers: if trigger.trigger == txt: self.triggers.remove(trigger) return ("Removed {}".format(trigger)) def setCallback(self, function): """Set the function to run on function complete ARGS: function (python function)""" self.callback = function def _process(self, command): """Internal process command - parse the command and execute""" self.log.info("Processing request {}...".format(command[0])) #Remove trailing and preceeding spaces #isinstance checks if command is a string or list #e.g isinstance("hello", str) is True #isinstance("hello", list) is False if isinstance(command, str): command = [command, None] if isinstance(command[0], list): command = command[0] command[0] = command[0].strip() try: #Check if it's a command or not if command[0][0] == self.command_prefix: #It's a command self._checkAgainstCommands(command) else: #We'll check it against the triggers self._checkAgainstTriggers(command) except Exception: pass def _checkAgainstCommands(self, command): command,channel = command command = command[1:] command_name,sep,args = command.partition(" ") if command_name in self.commands: #Now we've verified, go ahead and run it try: cmd = self.commands[command_name].run(args) if type(cmd) == types.GeneratorType: for i in cmd: self.output([i, channel]) else: self.output([cmd, channel]) except ArgumentFormatError: self.output("Error running {} -- Argument format error".format(command_name)) except TypeError: pass else: self.log.info("No command of name {} detected".format(command_name)) self.output("Error running {} -- Command not found".format(command_name)) def output(self, val): self.outputQ.put(val) if self.callback: self.callback(*val) def _checkAgainstTriggers(self, command): for i in self.triggers: if i.match(command[0]): self.output([i.send_text, command[1]]) def getOutput(self): try: x = self.outputQ.get(False) except queue.Empty: return None return x def run(self): """Start the thread off""" self.log.info("Command processor thread starting...") while (not self.stopReq.isSet()) or self.cmdQ.qsize() != 0: if self.stopNOW.isSet(): break try: toProcess = self.cmdQ.get(True, 0.5) self._process(toProcess) except queue.Empty: continue except Exception as e: self.log.error(e) traceback.format_exc(e) self.log.info("Stopping with qsize: {}".format(self.cmdQ.qsize())) self.log.info("Stopreq state: {}".format(self.stopReq.isSet())) self.log.info("Thread closed.") def push(self, commandstring, assoc_info = None): """Add a command to the command queue - to be processed commands ARGS: commandstring (str) - the command to process assoc_info (Any) - Things to be returned with the processed cmd""" self.log.info("Pushing command {}".format(commandstring)) self.cmdQ.put([commandstring, assoc_info]) for pipe in self.pipes: pipe.put([commandstring, assoc_info]) def exit(self, now=False): """Quit the thread, cleanly exit""" admin = 1 yield "Closing..." self.log.info("Exit request acknowledged, will exit.") self.stopReq.set() if (now): self.stopNOW.set() ##Module loading/unloading def loadModule(self, name): self.log.newline() self.log.info("LOADING MODULE {}".format(name.upper())) self.log.line() self.log.incIndent() try: ##Try loading the module as i yield("Importing {}...".format(name)) i = importlib.import_module(name) ##In case it's changed and was imported before, reload it self.log.debug("Reloading...") i = importlib.reload(i) ##Get a list of all the functions defined in the module self.log.debug("Getting functions...") funcs = dir(i) ##Don't import python's internal functions, like __name__ and __init__ x = re.compile("__[a-z]*__") z = ([("i.{}".format(y)) for y in funcs if not (x.match(y) or y[-1]=="_")]) self.log.debug("Loaded, adding functions...") self.log.incIndent() self.funcs = "Loaded functions:\n" ##Load the functions in for j in z: if type(eval(j)) == types.FunctionType: if "onimport" in j: self.log.info("Running import function...") eval(j)() elif "onexit" in j: atexit.register(eval(j)) else: self.addCommand(j.split(".")[1], eval(j), module=name) self.funcs += "!{}, ".format(j.split(".")[1]) if name not in self.loadedModules: self.loadedModules.append(name) yield(self.funcs) except ImportError as ie: self.log.error("Could not find module {}".format(name)) self.log.error(ie) except Exception as e: self.log.error("Unknown exception: {}".format(e)) def unloadModule(self, module_name): yield "Unloading module {}".format(module_name) funcs = "( " for i in self.commands: if self.commands[i].module == module_name: self.removeCommand(i) funcs += i + ", " self.loadedModules.remove(module_name) self.log.info("Unloaded {} {} )".format(module_name, funcs)) def lsmod(self): """List all currently loaded modules""" x = "Loaded modules: \n" x += "\n".join(self.loadedModules) return( x + "\n" ) def getHelp(self, cmd=None): if cmd: if cmd in self.commands: return(self.commands[cmd].getHelp()) else: return("Command {} does not exist".format(cmd)) else: #Return list of commands return "Available Commands: "+", ".join(self.commands) def listTriggers(self): return "\n".join([str(x) for x in self.triggers]) def loadService(self, srvname): self.services.append( Service( name = srvname, function = function, autostart = True ) ) def startService(self, srvname): for i in self.services: if i.name == srvname: i.start() return "Started" return "Service not found" def killService(self, srvname): for i in self.services: if i.name == srvname: i.stop() self.services.remove(i) return "Killed" return "Service not found" def lsService(self): x = [i.name for i in self.services] return "\n".join(x) def _addUtilityCommands(self): self.addCommand("lsmod", self.lsmod) self.addCommand("import", self.loadModule) self.addCommand("quit", self.exit) self.addCommand("help", self.getHelp) self.addCommand("mktrig", self.addTrigger) self.addCommand("rmtrig", self.removeTrigger) self.addCommand("lstrig", self.listTriggers) self.addCommand("writeconf", self.writeConfig) self.addCommand("loadSrv", self.loadService) self.addCommand("startSrv", self.startService) self.addCommand("killSrv", self.killService) self.addCommand("lsSrv", self.lsService)
class CommandProcessor(threading.Thread): """Thread-based module for processing commands. Takes in commands from a queue, processes them asyncronously, outputs to another queue. ARGS: configfile -- The file to load in all of our configuration from """ def __init__(self, configfile="command-proc.conf"): super(CommandProcessor, self).__init__() #Open a logger for superior feedback(tm) self.log = Log() #Alert the user that we're starting the processor self.log.info("Command Processor Created") self.log.newline() self.log.info("CMDPROC INIT") self.log.line() self.log.incIndent() #Set up some initial variables self.triggers = [] self.services = [] self.pipes = [] self.commands = {} self.loadedModules = ["Builtin"] #Read in the config self.log.info("Reading config...") self.config = YamlConf(configfile) self.log.info("Setting config...") #Load in the config #The getValue() or "Value" means that we have a default #So if we can't get something from the config, it defaults to something self.bot_name = self.config.getValue("bot", "name") or "Bot" self.bot_version = self.config.getValue("bot", "version") or "0.01" self.msg_prefix = self.config.getValue("bot", "msg_prefix") or "BOT: " self.command_prefix = self.config.getValue("bot", "cmd_prefix") or "!" debug = self.config.getValue("bot", "debug_logging") or False module_path = self.config.getValue("modules", "path") or "." #This means python will be able to find modules #When python looks for things, it goes down a so-called PYTHONPATH #We give it some new directories to look in self.module_path = module_path sys.path.insert(0, module_path) initial_modules = self.config.getValue("modules", "load") or [] inital_triggers = self.config.getValue("triggers") or [] self.admins = self.config.getValue("admins") self.log.info("Read config.") #callback means that we run a function once we're done #processing something, so like we can go # output = process("!hello") # callback(output) #By default it's just print, so we print the output self.callback = print #Set up the input and output queues self.log.info("Initilising command queue...") self.cmdQ = queue.Queue() self.log.info("Initilising output queue...") self.outputQ = queue.Queue() #Set up some stuff so we can tell the commandprocessor to stop #when we want to exit, we turn the Event on self.log.info("Initilising requests...") self.stopReq = threading.Event() self.stopNOW = threading.Event() #We have a few "initial modules", things to load on startup self.log.info("Setting up modules...") for i in initial_modules: self.loadModule(i) #Same goes for triggers self.log.info("Setting up triggers...") for i in inital_triggers: self.addTrigger(i, inital_triggers[i]) #If the user wants more information than usual, they can set debug to True self.log.info("Setting verbosity...") if debug: self.log.setLevel(self.log.DEBUG) #For reference: an underscore before the function name means that it only has INTERNAL use, #i.e nobody outside this class should call _addUtilityCommands() self._addUtilityCommands() #Tell the user that we've finished setting everything up self.log.line() self.log.info("FINISHED CMDPROC INIT") self.log.newline() def join(self, timeout=None): """When the owner of CommandProcessor wants us to exit, we alert the user that we've recieved the quit request, and shut everythig down """ self.log.info("QUITTING") self.log.line("Q") #Tell the processing function to stop self.stopReq.set() #Join the thread to the main process, #wait for `timeout` seconds before forcing it to. super(Thread, self).join(timeout) def addCommand(self, function_name, function_object, help=None, module="Builtin"): """Add a command to the processor ARGS: function_name: The name you wish to call the function by, i.e "func" function_object: The actual object to run when you call the command """ self.log.info("Adding command {}".format(function_name)) #Check if we've already got a command by name `function_name` if function_name in self.commands: self.log.info("Command {} already registered. Overwriting") #Make a nice little command object, for keeping track of it com = Command(function_name, function_object, help=help, module=module) #Make sure it actually worked yo if com.success: self.commands[function_name] = com else: self.log.info("Failed to add command") def removeCommand(self, function_name): """WHAT THE FEK DO YOU THINK IT DOES? MAKE CAKE? NO. IT REMOVES A COMMAND!""" #Check that we've actually got a command by that name if function_name in self.commands: #If we do, delete it! del self.commands[function_name] self.log.info("Succesfully removed {}".format(function_name)) else: #Otherwise, we can't. Because you can't delete something that doesn't exist, dummy self.log.info("Could not remove non-existent function {}".format( function_name)) def addTrigger(self, trigger_text, replacement_text): """Add a text-based trigger - will send replacement_text when trigger_text is in a given message""" #Check if we've got a trigger about that already, because we might for trigger in self.triggers: if trigger.trigger == trigger_text: #If we do, modifify it to the new trigger trigger.send_text = replacement_text return ("Modified trigger to {}".format(trigger)) #If we haven't got a trigger about `trigger_text`, #make a new one self.triggers.append(Trigger(trigger_text, replacement_text)) self.log.info("Added {}".format(self.triggers[-1])) return ("Added -- {}".format(self.triggers[-1])) def writeConfig(self): """Save the config, all currently loaded modules will be saved to init_modules""" self.log.info("Writing config...") #Add all of our triggers to a nice little dictionary, #So we can write it to a file trigs = {} for i in self.triggers: trigs[i.trigger] = i.send_text settings = { "bot": { "name": self.bot_name, "version": self.bot_version, "msg_prefix": self.msg_prefix, "cmd_prefix": self.command_prefix, "debug_logging": False }, "modules": { "path": self.module_path, "load": self.loadedModules }, "triggers": trigs, "ai": { "model_directory": "models" }, "admins": self.admins } self.log.info(settings) #Write the config in YAML format with open("command-proc.conf", "w") as f: f.write(pyaml.dump(settings)) self.log.info("Written") def removeTrigger(self, txt): """Remove the trigger with `trigger_text` == `txt`""" for trigger in self.triggers: if trigger.trigger == txt: self.triggers.remove(trigger) return ("Removed {}".format(trigger)) def setCallback(self, function): """Set the function to run on function complete ARGS: function (python function)""" self.callback = function def _process(self, command): """Internal process command - parse the command and execute""" self.log.info("Processing request {}...".format(command[0])) #Remove trailing and preceeding spaces #isinstance checks if command is a string or list #e.g isinstance("hello", str) is True #isinstance("hello", list) is False if isinstance(command, str): command = [command, None] if isinstance(command[0], list): command = command[0] command[0] = command[0].strip() try: #Check if it's a command or not if command[0][0] == self.command_prefix: #It's a command self._checkAgainstCommands(command) else: #We'll check it against the triggers self._checkAgainstTriggers(command) except Exception: pass def _checkAgainstCommands(self, command): command, channel = command command = command[1:] command_name, sep, args = command.partition(" ") if command_name in self.commands: #Now we've verified, go ahead and run it try: cmd = self.commands[command_name].run(args) if type(cmd) == types.GeneratorType: for i in cmd: self.output([i, channel]) else: self.output([cmd, channel]) except ArgumentFormatError: self.output("Error running {} -- Argument format error".format( command_name)) except TypeError: pass else: self.log.info( "No command of name {} detected".format(command_name)) self.output( "Error running {} -- Command not found".format(command_name)) def output(self, val): self.outputQ.put(val) if self.callback: self.callback(*val) def _checkAgainstTriggers(self, command): for i in self.triggers: if i.match(command[0]): self.output([i.send_text, command[1]]) def getOutput(self): try: x = self.outputQ.get(False) except queue.Empty: return None return x def run(self): """Start the thread off""" self.log.info("Command processor thread starting...") while (not self.stopReq.isSet()) or self.cmdQ.qsize() != 0: if self.stopNOW.isSet(): break try: toProcess = self.cmdQ.get(True, 0.5) self._process(toProcess) except queue.Empty: continue except Exception as e: self.log.error(e) traceback.format_exc(e) self.log.info("Stopping with qsize: {}".format(self.cmdQ.qsize())) self.log.info("Stopreq state: {}".format(self.stopReq.isSet())) self.log.info("Thread closed.") def push(self, commandstring, assoc_info=None): """Add a command to the command queue - to be processed commands ARGS: commandstring (str) - the command to process assoc_info (Any) - Things to be returned with the processed cmd""" self.log.info("Pushing command {}".format(commandstring)) self.cmdQ.put([commandstring, assoc_info]) for pipe in self.pipes: pipe.put([commandstring, assoc_info]) def exit(self, now=False): """Quit the thread, cleanly exit""" admin = 1 yield "Closing..." self.log.info("Exit request acknowledged, will exit.") self.stopReq.set() if (now): self.stopNOW.set() ##Module loading/unloading def loadModule(self, name): self.log.newline() self.log.info("LOADING MODULE {}".format(name.upper())) self.log.line() self.log.incIndent() try: ##Try loading the module as i yield ("Importing {}...".format(name)) i = importlib.import_module(name) ##In case it's changed and was imported before, reload it self.log.debug("Reloading...") i = importlib.reload(i) ##Get a list of all the functions defined in the module self.log.debug("Getting functions...") funcs = dir(i) ##Don't import python's internal functions, like __name__ and __init__ x = re.compile("__[a-z]*__") z = ([("i.{}".format(y)) for y in funcs if not (x.match(y) or y[-1] == "_")]) self.log.debug("Loaded, adding functions...") self.log.incIndent() self.funcs = "Loaded functions:\n" ##Load the functions in for j in z: if type(eval(j)) == types.FunctionType: if "onimport" in j: self.log.info("Running import function...") eval(j)() elif "onexit" in j: atexit.register(eval(j)) else: self.addCommand(j.split(".")[1], eval(j), module=name) self.funcs += "!{}, ".format(j.split(".")[1]) if name not in self.loadedModules: self.loadedModules.append(name) yield (self.funcs) except ImportError as ie: self.log.error("Could not find module {}".format(name)) self.log.error(ie) except Exception as e: self.log.error("Unknown exception: {}".format(e)) def unloadModule(self, module_name): yield "Unloading module {}".format(module_name) funcs = "( " for i in self.commands: if self.commands[i].module == module_name: self.removeCommand(i) funcs += i + ", " self.loadedModules.remove(module_name) self.log.info("Unloaded {} {} )".format(module_name, funcs)) def lsmod(self): """List all currently loaded modules""" x = "Loaded modules: \n" x += "\n".join(self.loadedModules) return (x + "\n") def getHelp(self, cmd=None): if cmd: if cmd in self.commands: return (self.commands[cmd].getHelp()) else: return ("Command {} does not exist".format(cmd)) else: #Return list of commands return "Available Commands: " + ", ".join(self.commands) def listTriggers(self): return "\n".join([str(x) for x in self.triggers]) def loadService(self, srvname): self.services.append( Service(name=srvname, function=function, autostart=True)) def startService(self, srvname): for i in self.services: if i.name == srvname: i.start() return "Started" return "Service not found" def killService(self, srvname): for i in self.services: if i.name == srvname: i.stop() self.services.remove(i) return "Killed" return "Service not found" def lsService(self): x = [i.name for i in self.services] return "\n".join(x) def _addUtilityCommands(self): self.addCommand("lsmod", self.lsmod) self.addCommand("import", self.loadModule) self.addCommand("quit", self.exit) self.addCommand("help", self.getHelp) self.addCommand("mktrig", self.addTrigger) self.addCommand("rmtrig", self.removeTrigger) self.addCommand("lstrig", self.listTriggers) self.addCommand("writeconf", self.writeConfig) self.addCommand("loadSrv", self.loadService) self.addCommand("startSrv", self.startService) self.addCommand("killSrv", self.killService) self.addCommand("lsSrv", self.lsService)