Пример #1
0
class FourChan:
    """Wrapper class to py4chan - 4chan API"""
    def __init__(self, board):
        self.log = Log()
        self.log.info("Getting /{}/...".format(board))
        try:
            self.board = chan.Board(board)
            self.log.info("/{}/ was fetched successfully".format(board))
        except Exception as e:
            self.log.error("Failed to fetch /{}/ -- {}".format(board, e))

    def getPosts(self, limit=10):
        self.log.info("Getting posts - limit of {}".format(limit))
        return self.board.get_all_thread_ids()[:limit + 1]

    def getTitles(self):
        self.log.info("Getting thread titles...")
        q = []
        for x in self.getPosts():
            b = self.board.get_thread(x).posts
            q += [self._scrub(y.comment) for y in b]
        self.log.info("Got titles of threads successfully")
        return [x for x in q if x != '']

    def _scrub(self, post):
        """Clean up the posts, remove all HTML and formatting"""
        scrubs = ["<span class=\"quote\">", "<br>", "</span>", "</s>"]

        for i in scrubs:
            post = post.replace(i, " ")
        post = post.replace("&gt;", ">").replace("&#039;",
                                                 "'").replace("&quot;", "\"")
        post = post.replace("&lt;", "<")
        q = re.compile("<\/?[A-Za-z0-9\/\=\#\"\': ]*>")
        for w in q.findall(post):
            post = post.replace(w, "")
        postnums = re.compile(">?[0-9]{4,}")
        for w in postnums.findall(post):
            post = post.replace(w, "")
        return post.replace(">\n", "").replace("\n", "")
Пример #2
0
class YamlConf:
    """Wrapper class to pYaml - useful for more complex configurations"""
    def __init__(self, filename):
        self.log = Log()
        self.log.info("Opening YAML config file '{}'".format(filename))
        try:
            with open(filename) as f:
                self.data = yaml.load(f.read())
        except yaml.YAMLError as e:
            self.log.error("File '{}' cannot be parsed!".format(filename))
            self.log.error("{}".format(e))
        self.log.info("{} YAML parsed succesfully".format(filename))

    def getValue(self, *keys):
        """Search through our data and find the specified key or subkey"""

        data = self.data
        ##Iterate through the key heirarchy
        for i in keys:
            try:
                data = data.get(i)
            except KeyError:
                self.log.error("Key {} not found".format(
                    self._formatKey(keys)))
                return None
        return data

    def _formatKey(self, keys):
        """Pretty print a key heirarchy"""

        form = ""
        for key in keys:
            form += "{} -> ".format(key)
        return form[:-3]
Пример #3
0
class FourChan:
  """Wrapper class to py4chan - 4chan API"""

  def __init__(self, board):
    self.log = Log()
    self.log.info("Getting /{}/...".format(board))  
    try:
      self.board = chan.Board(board)
      self.log.info("/{}/ was fetched successfully".format(board))
    except Exception as e:
      self.log.error("Failed to fetch /{}/ -- {}".format(board, e))

  def getPosts(self,limit=10):
    self.log.info("Getting posts - limit of {}".format(limit))
    return self.board.get_all_thread_ids()[:limit+1]

  def getTitles(self):
    self.log.info("Getting thread titles...")
    q = []
    for x in self.getPosts():
      b = self.board.get_thread(x).posts
      q += [self._scrub(y.comment) for y in b]
    self.log.info("Got titles of threads successfully")
    return [x for x in q if x != '']

  def _scrub(self, post):
    """Clean up the posts, remove all HTML and formatting"""
    scrubs = ["<span class=\"quote\">", "<br>", "</span>","</s>"]
    
    for i in scrubs:
      post = post.replace(i, " ")
    post = post.replace("&gt;", ">").replace("&#039;", "'").replace("&quot;", "\"")
    post = post.replace("&lt;", "<")
    q = re.compile("<\/?[A-Za-z0-9\/\=\#\"\': ]*>")
    for w in q.findall(post):
      post = post.replace(w, "")
    postnums = re.compile(">?[0-9]{4,}")
    for w in postnums.findall(post):
      post = post.replace(w, "")
    return post.replace(">\n","").replace("\n", "")
Пример #4
0
          x = "  function() {{\n".format("update", self.breaks,self.breaks);
          self.breaks += 1
          return """\n  {0}.time.events.add(Phaser.Timer.SECOND * {1}, {3}\n""".format(args.game, int(tokens[1]), "{}_{}".format("update", self.breaks-1),x)
        return "{}.{}({})".format(obj, tokens[0], ",".join(tokens[1:]))

    def tokenise(self, info):
      return info.split(" ")

    def __repr__(self):
      return str(self.phaseinfo)

try:
  with open(args.filename, "r") as f:
    data = f.read()
except FileNotFoundError:
  log.error("Could not find file {} -- Make sure it exists".format(args.filename))

ai = AI(args.game, obj, data.split("\n"))

updatefunc = """\n setup_{0}_ai: function(object) {{
  {0} = object;
}},

update:function(){{
  if (!this.alive) {{
  var index = Math.floor(Math.random() * this.phases.length);
  var func = this.phases[index]
  
  func();  
}}}},""".format(obj, args.varname)
Пример #5
0
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)
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)