Exemple #1
0
 def _setup(self):
     self._log("bot: setup")
     self.bio = BotIO(verbose=False)
     self.nick = self.bio.get_nick()
     self.cmd_name = self.bio.get_cmd_name()
     self.cfg_name = self.bio.get_cfg_name()
     self.cfg_paths = self.bio.get_cfg_paths()
     self._log("bot: got nick='%s' cmd_name='%s' cfg_name='%s' cfg_paths=%s" % \
       (self.nick, self.cmd_name, self.cfg_name, self.cfg_paths))
Exemple #2
0
 def _setup(self):
   self._log("bot: setup")
   self.bio = BotIO(verbose=False)
   self.nick = self.bio.get_nick()
   self.cmd_name = self.bio.get_cmd_name()
   self.cfg_name = self.bio.get_cfg_name()
   self.cfg_paths = self.bio.get_cfg_paths()
   self._log("bot: got nick='%s' cmd_name='%s' cfg_name='%s' cfg_paths=%s" % \
     (self.nick, self.cmd_name, self.cfg_name, self.cfg_paths))
Exemple #3
0
class Bot:
    """main class for a bot instance"""
    def __init__(self, verbose=False):
        self.modules = []
        self.bio = None
        self.nick = None
        self.cmd_name = None
        self.cfg_name = None
        self.cfg_paths = None
        self.verbose = verbose
        self.connected = False
        self.rem_mods = {}

    def add_module(self, module):
        """add a module to the bot"""
        self.modules.append(module)

    def _log(self, *args):
        if self.verbose:
            t = time.time()
            millis = int(t * 1000) % 1000
            a = [
                "%s.%03d" %
                (time.strftime("%d.%m.%Y %H:%M:%S", time.localtime(t)), millis)
            ] + list(args)
            print(*a, file=sys.stderr)

    def run(self):
        """run the bot"""
        self._setup()
        self._auto_add_modules()
        self._setup_modules()
        self._setup_cmds()
        self._main_loop()

    def _setup(self):
        self._log("bot: setup")
        self.bio = BotIO(verbose=False)
        self.nick = self.bio.get_nick()
        self.cmd_name = self.bio.get_cmd_name()
        self.cfg_name = self.bio.get_cfg_name()
        self.cfg_paths = self.bio.get_cfg_paths()
        self._log("bot: got nick='%s' cmd_name='%s' cfg_name='%s' cfg_paths=%s" % \
          (self.nick, self.cmd_name, self.cfg_name, self.cfg_paths))

    def _gen_funcs(self, name, other_mod):
        # set reply function for module
        def send(args, to=None):
            self.bio.write_args(args, receivers=to)
            # internal loop back
            if to is None or self.nick in to:
                a = map(str, args)
                msg = BotIOMsg(" ".join(a), self.nick, to, False)
                msg.split_args()
                self._handle_msg(msg, other_mod)

        # set log function
        def log(*args):
            a = [name + ":"] + list(args)
            self._log(*a)

        return send, log

    def _auto_add_modules(self):
        """auto add modules from config"""
        cfg = self.bio.get_cfg()
        def_cfg = {'auto_load': None}
        mod_cfg = cfg.get_section("modules", def_cfg)
        auto_load = mod_cfg['auto_load']
        if auto_load:
            mod_list = auto_load.split(',')
            for mod in mod_list:
                # expect package.Class name
                dot = mod.rfind('.')
                if dot == -1:
                    raise RuntimeError("Invalid module name: " + mod)
                pkg = mod[:dot]
                clz = mod[dot + 1:]
                self._log("bot: auto load module:", pkg, clz)
                # get python module
                pmod = importlib.import_module(pkg)
                pdict = pmod.__dict__
                if clz not in pdict:
                    raise RuntimeError("Class '%s' not found in Module '%s'" %
                                       (pkg, clz))
                # create instance
                pclz = pdict[clz]
                my_mod = pclz()
                self.modules.append(my_mod)

    def _setup_modules(self):
        if len(self.modules) == 0:
            raise RuntimeError("No modules added!")
        self.bot_tick_interval = 1
        for m in self.modules:
            name = m.get_name()

            # other modules
            other_mod = []
            for m2 in self.modules:
                if m2 != m:
                    other_mod.append(m2)

            send, log = self._gen_funcs(name, other_mod)

            # get bot config
            cfg = self.bio.get_cfg()

            # has options?
            opts = m.get_opts()
            bo = None
            if opts is not None:

                def send_mod_event(args, to=None):
                    a = [name + ".event"] + args
                    send(a, to=to)

                cfg_name = m.get_opts_name()
                bo = BotOpts(send_mod_event, opts, cfg=cfg, cfg_name=cfg_name)

                def field_handler(field):
                    self._trigger_internal_event(BotEvent.UPDATE_FIELD,
                                                 [field],
                                                 mods=[m])

                bo.set_notifier(field_handler)
                self._log("bot: module", name, "opts:", bo.get_values())

            # setup bot
            m.nick = self.nick
            m.bot = self
            m.setup(send, log, cfg, bo)

            # get tick (after setup of bot)
            tick = m.get_tick_interval()
            self._log("bot: module", name, "tick", tick)
            if tick > 0 and tick < self.bot_tick_interval:
                self.bot_tick_interval = tick

        # report bot tick
        self._log("bot: tick", self.bot_tick_interval)

    def _setup_cmds(self):
        self.cmds = [
            BotCmd("lsmod", callee=self._cmd_lsmod),
            BotCmd("ping", callee=self._cmd_ping)
        ]
        self.events = [
            BotEvent("bot",
                     "module",
                     arg_types=(str, str),
                     callee=self._event_bot_module),
            BotEvent("bot", "end_module", callee=self._event_bot_end_module)
        ]

    def _cmd_lsmod(self, sender):
        self._log("cmd: lsmod")
        for a in self.modules:
            self._reply(["bot.event", "module",
                         a.get_name(),
                         a.get_version()],
                        to=[sender])
        self._reply(["bot.event", "end_module"], to=[sender])

    def _cmd_ping(self, sender):
        self._reply(["bot.event", "pong"], to=[sender])

    def _get_mod_set(self, sender):
        if sender in self.rem_mods:
            return self.rem_mods[sender]
        else:
            s = {}
            self.rem_mods[sender] = s
            return s

    def _event_bot_module(self, sender, args):
        mod_name = args[0]
        mod_ver = args[1]
        self._log("bot module", sender, mod_name, mod_ver)
        s = self._get_mod_set(sender)
        s[mod_name] = mod_ver

    def _event_bot_end_module(self, sender):
        self._log("bot end_module", sender)
        s = self._get_mod_set(sender)
        # post a peer mod list event
        self._trigger_internal_event(BotEvent.PEER_MOD_LIST, [sender, s])

    def _send_my_mod_list(self):
        s = {}
        for m in self.modules:
            name = m.get_name()
            ver = m.get_version()
            s[name] = ver
        self._trigger_internal_event(BotEvent.MOD_LIST, [s])

    def request_shutdown(self):
        self._log("requesting shutdown")
        self.stay = False

    def _main_loop(self):
        self._init_tick()

        # report start
        self._trigger_internal_event(BotEvent.START)

        tick_delta = self.bot_tick_interval

        self.delta_range = [tick_delta, -tick_delta]
        self.extra_range = [tick_delta, -tick_delta]
        self.show_ts = time.time()

        self.stay = True
        extra = 0
        while self.stay:
            try:
                # handle tick
                b = time.time()
                self._tick()
                e = time.time()

                # calc remaining time in interval to wait
                delta = e - b
                wait = tick_delta - delta - extra
                if wait <= 0.01:
                    wait = 0.01

                # handle incoming messages
                real_wait = self._read_dispatch_msgs(wait)
                extra = real_wait - wait

                # internal (debug) accounting
                self._account_delta(b, delta, extra)

            except KeyboardInterrupt:
                self._log("bot: Break")
                break
            except Exception as e:
                self._log("bot: ERROR:", str(e))
                traceback.print_exc(file=sys.stderr)
                break

        # report stop
        self._trigger_internal_event(BotEvent.STOP)

    def _account_delta(self, ts, delta, extra):
        if delta < self.delta_range[0]:
            self.delta_range[0] = delta
        if delta > self.delta_range[1]:
            self.delta_range[1] = delta

        if extra < self.extra_range[0]:
            self.extra_range[0] = extra
        if extra > self.extra_range[1]:
            self.extra_range[1] = extra

        show_delta = ts - self.show_ts
        if show_delta > 10:
            dmi = int(self.delta_range[0] * 1000)
            dma = int(self.delta_range[1] * 1000)
            emi = int(self.extra_range[0] * 1000)
            ema = int(self.extra_range[1] * 1000)
            self._log("dispatch delta:", dmi, dma, " extra:", emi, ema)
            self.show_ts = ts
            # reset ranges
            tick_delta = self.bot_tick_interval
            self.delta_range = [tick_delta, -tick_delta]
            self.extra_range = [tick_delta, -tick_delta]

    def _read_dispatch_msgs(self, timeout):
        start = time.time()
        end = start + timeout
        t = start
        while t < end:
            msg = self.bio.read_args(timeout=timeout)
            if msg:
                if msg.is_internal:
                    self._handle_internal_msg(msg)
                else:
                    self._handle_msg(msg, self.modules)
            tn = time.time()
            delta = tn - t
            t = tn
            timeout -= delta
            if timeout <= 0:
                break
        return t - start

    def _init_tick(self):
        ts = time.time()
        for m in self.modules:
            m.last_ts = ts

    def _tick(self):
        """call tick in modules"""
        ts = time.time()
        for m in self.modules:
            tick = m.get_tick_interval()
            if tick > 0:
                delta = ts - m.last_ts
                if delta >= tick:
                    #self._log("bot: do tick", m.name)
                    self._trigger_internal_event(BotEvent.TICK, [ts, delta],
                                                 mods=[m])
                    m.last_ts = ts

    def _reply(self, args, to=None):
        self.bio.write_args(args, receivers=to)

    def _error(self, msg, to):
        self._reply(["bot.event", "error", msg], [to])

    def _handle_internal_msg(self, msg):
        """handle internal message"""
        if msg.int_cmd == 'exit':
            self._log("exit")
            return False
        # connected?
        my_nick = self.bio.get_nick()
        msg_nick = msg.int_nick
        self._log("internal", msg_nick, msg.int_cmd)
        if msg.int_cmd == 'connected':
            if msg.int_nick == my_nick:
                if not self.connected:
                    self._log("I am connected")
                    self.connected = True
                    self._trigger_internal_event(BotEvent.CONNECT)
                    # send my mod list
                    self._send_my_mod_list()
            else:
                self._trigger_internal_event(BotEvent.PEER_CONNECT, [msg_nick])
                # request module list
                self._reply(['bot', 'lsmod'], to=[msg_nick])
        # disconnected?
        elif msg.int_cmd == 'disconnected':
            if msg.int_nick == my_nick:
                if self.connected:
                    self._log("I am disconnected")
                    self.connected = False
                    self._trigger_internal_event(BotEvent.DISCONNECT)
            else:
                self._trigger_internal_event(BotEvent.PEER_DISCONNECT,
                                             [msg_nick])
        return True

    def _handle_msg(self, msg, modules):
        # get command name
        a = msg.args
        n = len(a)
        to = msg.sender
        # no command given?
        if n < 2:
            self._error("huh?", to)
            return
        # is it a bot command?
        mod_name = a[0]
        cmd_name = a[1]
        if mod_name == 'bot':
            # check internal command
            for cmd in self.cmds:
                if cmd_name == cmd.get_name():
                    res = cmd.handle_cmd(a[1:], msg.sender)
                    if type(res) is str:
                        self._error(cmd_name + ": " + res, to)
                        return res
        # is it a module prefix
        for mod in modules:
            if mod_name == mod.get_name():
                # parse module command
                res = self._handle_mod_cmd(mod, a[1:], msg.sender)
                if type(res) is str:
                    self._error(cmd_name + ": " + res, to)
                    return res
            # is it an event?
            res = self._handle_mod_event(mod.get_events(), a, to)
            if type(res) is str:
                self._error(cmd_name + ": " + res, to)
                return res
        # check for an bot event?
        res = self._handle_mod_event(self.events, a, to)
        if type(res) is str:
            self._error(cmd_name + ": " + res, to)
            return res
        # unknown
        #self._error("Unknown command: " + cmd_name, to)

    def _handle_mod_event(self, events, args, to):
        """handle a module event"""
        if len(args) < 1:
            return False
        # check events
        if events is not None:
            for ev in events:
                if ev.mod_name == args[0] and ev.name == args[1]:
                    res = ev.handle_event(args, to)
                    if type(res) is str:
                        self._error(ev.mod_name + " " + ev.name + ": " + res,
                                    to)
                        return res

    def _handle_mod_cmd(self, mod, args, to):
        """handle a module command"""
        cmd_name = args[0]
        # check bot commands
        cmds = mod.get_commands()
        if cmds is not None:
            for c in cmds:
                if cmd_name == c.get_name():
                    return c.handle_cmd(args, to)
        # check options
        bo = mod.botopts
        if bo is not None:
            res = bo.handle_command(args, to)
            if res is not None:
                return res
        # nothing found
        return "huh? " + cmd_name

    def _trigger_internal_event(self, name, args=None, mods=None):
        if mods is None:
            mods = self.modules
        for mod in mods:
            events = mod.get_events()
            if events is not None:
                for ev in events:
                    if ev.mod_name == BotEvent.INTERNAL + ".event" and ev.name == name:
                        callee = ev.callee
                        if callee is not None:
                            if args is None:
                                callee()
                            else:
                                callee(*args)
Exemple #4
0
class Bot:
  """main class for a bot instance"""
  def __init__(self, verbose=False):
    self.modules = []
    self.bio = None
    self.nick = None
    self.cmd_name = None
    self.cfg_name = None
    self.cfg_paths = None
    self.verbose = verbose
    self.connected = False
    self.rem_mods = {}

  def add_module(self, module):
    """add a module to the bot"""
    self.modules.append(module)

  def _log(self, *args):
    if self.verbose:
      t = time.time()
      millis = int(t * 1000) % 1000
      a = ["%s.%03d" % (time.strftime("%d.%m.%Y %H:%M:%S", time.localtime(t)), millis)] + list(args)
      print(*a,file=sys.stderr)

  def run(self):
    """run the bot"""
    self._setup()
    self._auto_add_modules()
    self._setup_modules()
    self._setup_cmds()
    self._main_loop()

  def _setup(self):
    self._log("bot: setup")
    self.bio = BotIO(verbose=False)
    self.nick = self.bio.get_nick()
    self.cmd_name = self.bio.get_cmd_name()
    self.cfg_name = self.bio.get_cfg_name()
    self.cfg_paths = self.bio.get_cfg_paths()
    self._log("bot: got nick='%s' cmd_name='%s' cfg_name='%s' cfg_paths=%s" % \
      (self.nick, self.cmd_name, self.cfg_name, self.cfg_paths))

  def _gen_funcs(self, name, other_mod):
    # set reply function for module
    def send(args, to=None):
      self.bio.write_args(args, receivers=to)
      # internal loop back
      if to is None or self.nick in to:
        a = map(str, args)
        msg = BotIOMsg(" ".join(a), self.nick, to, False)
        msg.split_args()
        self._handle_msg(msg, other_mod)

    # set log function
    def log(*args):
      a = [name + ":"] + list(args)
      self._log(*a)

    return send, log

  def _auto_add_modules(self):
    """auto add modules from config"""
    cfg = self.bio.get_cfg()
    def_cfg = {
      'auto_load' : None
    }
    mod_cfg = cfg.get_section("modules", def_cfg)
    auto_load = mod_cfg['auto_load']
    if auto_load:
      mod_list = auto_load.split(',')
      for mod in mod_list:
        # expect package.Class name
        dot = mod.rfind('.')
        if dot == -1:
          raise RuntimeError("Invalid module name: " + mod)
        pkg = mod[:dot]
        clz = mod[dot+1:]
        self._log("bot: auto load module:", pkg, clz)
        # get python module
        pmod = importlib.import_module(pkg)
        pdict = pmod.__dict__
        if clz not in pdict:
          raise RuntimeError("Class '%s' not found in Module '%s'" % (pkg, clz))
        # create instance
        pclz = pdict[clz]
        my_mod = pclz()
        self.modules.append(my_mod)

  def _setup_modules(self):
    if len(self.modules) == 0:
      raise RuntimeError("No modules added!")
    self.bot_tick_interval = 1
    for m in self.modules:
      name = m.get_name()

      # other modules
      other_mod = []
      for m2 in self.modules:
        if m2 != m:
          other_mod.append(m2)

      send, log = self._gen_funcs(name, other_mod)

      # get bot config
      cfg = self.bio.get_cfg()

      # has options?
      opts = m.get_opts()
      bo = None
      if opts is not None:

        def send_mod_event(args, to=None):
          a = [name + ".event"] + args
          send(a, to=to)

        cfg_name = m.get_opts_name()
        bo = BotOpts(send_mod_event, opts, cfg=cfg, cfg_name=cfg_name)

        def field_handler(field):
          self._trigger_internal_event(BotEvent.UPDATE_FIELD, [field], mods=[m])

        bo.set_notifier(field_handler)
        self._log("bot: module",name,"opts:", bo.get_values())

      # setup bot
      m.nick = self.nick
      m.bot = self
      m.setup(send, log, cfg, bo)

      # get tick (after setup of bot)
      tick = m.get_tick_interval()
      self._log("bot: module",name,"tick",tick)
      if tick > 0 and tick < self.bot_tick_interval:
        self.bot_tick_interval = tick

    # report bot tick
    self._log("bot: tick", self.bot_tick_interval)

  def _setup_cmds(self):
    self.cmds = [
      BotCmd("lsmod",callee=self._cmd_lsmod),
      BotCmd("ping",callee=self._cmd_ping)
    ]
    self.events = [
      BotEvent("bot","module",arg_types=(str,str),callee=self._event_bot_module),
      BotEvent("bot","end_module",callee=self._event_bot_end_module)
    ]

  def _cmd_lsmod(self, sender):
    self._log("cmd: lsmod")
    for a in self.modules:
      self._reply(["bot.event", "module", a.get_name(), a.get_version()], to=[sender])
    self._reply(["bot.event", "end_module"], to=[sender])

  def _cmd_ping(self, sender):
    self._reply(["bot.event", "pong"], to=[sender])

  def _get_mod_set(self, sender):
    if sender in self.rem_mods:
      return self.rem_mods[sender]
    else:
      s = {}
      self.rem_mods[sender] = s
      return s

  def _event_bot_module(self, sender, args):
    mod_name = args[0]
    mod_ver = args[1]
    self._log("bot module", sender, mod_name, mod_ver)
    s = self._get_mod_set(sender)
    s[mod_name] = mod_ver

  def _event_bot_end_module(self, sender):
    self._log("bot end_module", sender)
    s = self._get_mod_set(sender)
    # post a peer mod list event
    self._trigger_internal_event(BotEvent.PEER_MOD_LIST, [sender, s])

  def _send_my_mod_list(self):
    s = {}
    for m in self.modules:
      name = m.get_name()
      ver = m.get_version()
      s[name] = ver
    self._trigger_internal_event(BotEvent.MOD_LIST, [s])

  def request_shutdown(self):
    self._log("requesting shutdown")
    self.stay = False

  def _main_loop(self):
    self._init_tick()

    # report start
    self._trigger_internal_event(BotEvent.START)

    tick_delta = self.bot_tick_interval

    self.delta_range = [tick_delta, -tick_delta]
    self.extra_range = [tick_delta, -tick_delta]
    self.show_ts = time.time()

    self.stay = True
    extra = 0
    while self.stay:
      try:
        # handle tick
        b = time.time()
        self._tick()
        e = time.time()

        # calc remaining time in interval to wait
        delta = e - b
        wait = tick_delta - delta - extra
        if wait <= 0.01:
          wait = 0.01

        # handle incoming messages
        real_wait = self._read_dispatch_msgs(wait)
        extra = real_wait - wait

        # internal (debug) accounting
        self._account_delta(b, delta, extra)

      except KeyboardInterrupt:
        self._log("bot: Break")
        break
      except Exception as e:
        self._log("bot: ERROR:", str(e))
        traceback.print_exc(file=sys.stderr)
        break

    # report stop
    self._trigger_internal_event(BotEvent.STOP)

  def _account_delta(self, ts, delta, extra):
    if delta < self.delta_range[0]:
      self.delta_range[0] = delta
    if delta > self.delta_range[1]:
      self.delta_range[1] = delta

    if extra < self.extra_range[0]:
      self.extra_range[0] = extra
    if extra > self.extra_range[1]:
      self.extra_range[1] = extra

    show_delta = ts - self.show_ts
    if show_delta > 10:
      dmi = int(self.delta_range[0] * 1000)
      dma = int(self.delta_range[1] * 1000)
      emi = int(self.extra_range[0] * 1000)
      ema = int(self.extra_range[1] * 1000)
      self._log("dispatch delta:", dmi, dma, " extra:", emi, ema)
      self.show_ts = ts
      # reset ranges
      tick_delta = self.bot_tick_interval
      self.delta_range = [tick_delta, -tick_delta]
      self.extra_range = [tick_delta, -tick_delta]

  def _read_dispatch_msgs(self, timeout):
    start = time.time()
    end = start + timeout
    t = start
    while t < end:
      msg = self.bio.read_args(timeout=timeout)
      if msg:
        if msg.is_internal:
          self._handle_internal_msg(msg)
        else:
          self._handle_msg(msg, self.modules)
      tn = time.time()
      delta = tn - t
      t = tn
      timeout -= delta
      if timeout <= 0:
        break
    return t - start

  def _init_tick(self):
    ts = time.time()
    for m in self.modules:
      m.last_ts = ts

  def _tick(self):
    """call tick in modules"""
    ts = time.time()
    for m in self.modules:
      tick = m.get_tick_interval()
      if tick > 0:
        delta = ts - m.last_ts
        if delta >= tick:
          #self._log("bot: do tick", m.name)
          self._trigger_internal_event(BotEvent.TICK, [ts, delta], mods=[m])
          m.last_ts = ts

  def _reply(self, args, to=None):
    self.bio.write_args(args, receivers=to)

  def _error(self, msg, to):
    self._reply(["bot.event", "error", msg], [to])

  def _handle_internal_msg(self, msg):
    """handle internal message"""
    if msg.int_cmd == 'exit':
      self._log("exit")
      return False
    # connected?
    my_nick = self.bio.get_nick()
    msg_nick = msg.int_nick
    self._log("internal", msg_nick, msg.int_cmd)
    if msg.int_cmd == 'connected':
      if msg.int_nick == my_nick:
        if not self.connected:
          self._log("I am connected")
          self.connected = True
          self._trigger_internal_event(BotEvent.CONNECT)
          # send my mod list
          self._send_my_mod_list()
      else:
        self._trigger_internal_event(BotEvent.PEER_CONNECT, [msg_nick])
        # request module list
        self._reply(['bot','lsmod'], to=[msg_nick])
    # disconnected?
    elif msg.int_cmd == 'disconnected':
      if msg.int_nick == my_nick:
        if self.connected:
          self._log("I am disconnected")
          self.connected = False
          self._trigger_internal_event(BotEvent.DISCONNECT)
      else:
        self._trigger_internal_event(BotEvent.PEER_DISCONNECT, [msg_nick])
    return True

  def _handle_msg(self, msg, modules):
    # get command name
    a = msg.args
    n = len(a)
    to = msg.sender
    # no command given?
    if n < 2:
      self._error("huh?", to)
      return
    # is it a bot command?
    mod_name = a[0]
    cmd_name = a[1]
    if mod_name == 'bot':
      # check internal command
      for cmd in self.cmds:
        if cmd_name == cmd.get_name():
          res = cmd.handle_cmd(a[1:], msg.sender)
          if type(res) is str:
            self._error(cmd_name + ": " + res, to)
            return res
    # is it a module prefix
    for mod in modules:
      if mod_name == mod.get_name():
        # parse module command
        res = self._handle_mod_cmd(mod, a[1:], msg.sender)
        if type(res) is str:
          self._error(cmd_name + ": " + res, to)
          return res
      # is it an event?
      res = self._handle_mod_event(mod.get_events(), a, to)
      if type(res) is str:
        self._error(cmd_name + ": " + res, to)
        return res
    # check for an bot event?
    res = self._handle_mod_event(self.events, a, to)
    if type(res) is str:
      self._error(cmd_name + ": " + res, to)
      return res
    # unknown
    #self._error("Unknown command: " + cmd_name, to)

  def _handle_mod_event(self, events, args, to):
    """handle a module event"""
    if len(args) < 1:
      return False
    # check events
    if events is not None:
      for ev in events:
        if ev.mod_name == args[0] and ev.name == args[1]:
            res = ev.handle_event(args, to)
            if type(res) is str:
              self._error(ev.mod_name + " " + ev.name + ": " + res, to)
              return res

  def _handle_mod_cmd(self, mod, args, to):
    """handle a module command"""
    cmd_name = args[0]
    # check bot commands
    cmds = mod.get_commands()
    if cmds is not None:
      for c in cmds:
        if cmd_name == c.get_name():
          return c.handle_cmd(args, to)
    # check options
    bo = mod.botopts
    if bo is not None:
      res = bo.handle_command(args, to)
      if res is not None:
        return res
    # nothing found
    return "huh? " + cmd_name

  def _trigger_internal_event(self, name, args=None, mods=None):
    if mods is None:
      mods = self.modules
    for mod in mods:
      events = mod.get_events()
      if events is not None:
        for ev in events:
          if ev.mod_name == BotEvent.INTERNAL + ".event" and ev.name == name:
            callee = ev.callee
            if callee is not None:
              if args is None:
                callee()
              else:
                callee(*args)