############## #games.ai # #Parser for # #Phaser.io AI# ############## import json import argparse import re from floatingutils.log import Log log = Log() log.debug("Initialising Parser...") parser = argparse.ArgumentParser(description='Parse a .ai file into JSON') parser.add_argument("filename", help="The .ai file to process") parser.add_argument('--game', default="game", help='The name of your \'game\' instance - usually just game') parser.add_argument('--obj', help="The game of your game object that the AI will apply to") parser.add_argument("--varname", help="The variable name to be output", default="ai") parser.add_argument("--out", default="ai.out", help="The output filename") log.debug("Parser ready") args = parser.parse_args() log.info("Processing {}".format(args.filename)) log.info("Using game instance {}".format(args.game)) obj = args.obj or "obj"
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 Conf: """Class for parsing simple (key sep value) format files""" def __init__(self, filename, sep=":"): self.log = Log() self.filename = filename self.log.info("Reading {}".format(filename)) self.config = {} self.sep = sep self._readFile() self._extractConfig() self.log.info("Succesfully parsed {}".format(filename)) def _readFile(self): """Read in the configuration file""" self.log.debug("Opening file {}".format(self.filename)) ##Open the config file try: f = open(self.filename, "r") except FileNotFoundError: self.log.error("File '{}' not found!".format(self.filename)) ##Read in each line, ignoring empty lines self.text = [x[:-1] for x in f.readlines() if x[:-1] != ""] self.log.debug("File read succesfully") ##Close the file f.close() self.log.debug("{} closed".format(self.filename)) def writeFile(self, alternateFile=None): """Write the changed configuration back to a file""" self.log.info("Writing config back to file") filename = self.filename ##Just in case the user wants to change location if alternateFile: filename = alternateFile self.log.info("Writing to {}".format(filename)) try: with open(filename, "w") as f: for i in sorted(self.config): f.write("{}:{}\n".format(i, self.config[i])) except Exception as e: self.log.warning("An error occurred -- {}".format(e)) return 1 self.log.debug("{} Written succesfully".format(filename)) return 0 def _extractConfig(self): """Get all the key value pairs from the config file""" ##Keep track of line of debug purposes lineno = 1 for i in self.text: ##Split the line by the seperator setting, sep, value = i.partition(self.sep) if setting in self.config: ##If we've already seen that key before self.log.warning("Duplicate setting '{}' (Line {})".format( setting, lineno)) else: ##Check for empty setting name if setting == "": self.log.warning( "Empty setting name (Line {})".format(lineno)) else: ##Check for empty value if value == "": self.log.warning( "Empty setting value '{}'(Line {})".format( setting, lineno)) else: ##It all looks good self.config[setting] = value lineno += 1 def getValue(self, key): """Get the value associated with a key""" if key in self.config: return self.config[key] else: ##If we can't find the key self.log.error("Setting '{}' not found!".format(key)) return 0 def getData(self, key): """Get the parsed value of a key - for lists and dicts""" x = self.getValue(key) return eval(x) def setValue(self, key, value): """Change the value of a setting""" if key == "": self.log.warning( "Non-empty keys only please! (value thrown: {})".format(value)) return False else: self.config[key] = value return True
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)