def _initialize(bot): bot.spawn_lock = asyncio.Lock() config = bot.get_config_option("spawn") if not config: return cmds = config.get("commands") # override the load logic and register our commands directly for cmd in cmds: command.register(_spawn, admin=True, final=True, name=cmd) logger.info("spawn - %s", ", ".join(['*' + cmd for cmd in cmds])) plugins.register_admin_command(list(cmds))
def _initialize(bot): bot.spawn_lock = asyncio.Lock() config = bot.get_config_option("spawn") if not config: return cmds = config.get("commands") # override the load logic and register our commands directly get_location = False for cmd, cnf in cmds.items(): command.register(_spawn, admin=True, final=True, name=cmd) if cnf.get("allow_location"): get_location = True logger.info("spawn - %s", ", ".join(['*' + cmd for cmd in cmds])) plugins.register_admin_command(list(cmds)) if get_location: global _MAP_MATCH _MAP_MATCH = re.compile(config.get("map_regex", _MAP_REGEX), re.IGNORECASE|re.MULTILINE) plugins.register_handler(_location_handler, type="message")
def load(bot, module_path, module_name=None): """loads a single plugin-like object as identified by module_path, and initialise it""" if module_name is None: module_name = module_path.split(".")[-1] if module_path in tracking.list: raise RuntimeError("{} already loaded".format(module_path)) tracking.start({"module": module_name, "module.path": module_path}) try: if module_path in sys.modules: importlib.reload(sys.modules[module_path]) logger.debug("reloading {}".format(module_path)) else: importlib.import_module(module_path) logger.debug("importing {}".format(module_path)) except Exception as e: logger.exception( "EXCEPTION during plugin import: {}".format(module_path)) return if hasattr(sys.modules[module_path], 'hangups'): logger.info("{} has legacy hangups reference".format(module_name)) setattr(sys.modules[module_path], 'hangups', hangups_shim) public_functions = [ o for o in getmembers(sys.modules[module_path], isfunction) ] candidate_commands = [] """pass 1: run optional callable: _initialise, _initialize * performs house-keeping tasks (e.g. migration, tear-up, pre-init, etc) * registers user and/or admin commands """ available_commands = False # default: ALL try: for function_name, the_function in public_functions: if function_name == "_initialise" or function_name == "_initialize": """accepted function signatures: CURRENT version >= 2.4 | function() version >= 2.4 | function(bot) - parameter must be named "bot" LEGACY version <= 2.4 | function(handlers, bot) ancient | function(handlers) """ _expected = list(inspect.signature(the_function).parameters) if len(_expected) == 0: the_function() _return = [] elif len(_expected) == 1 and _expected[0] == "bot": the_function(bot) _return = [] else: try: # legacy support, pre-2.4 logger.info( "[LEGACY] upgrade {1}.{0}(handlers, bot) to {0}(bot) and use bot._handlers internally" .format(the_function.__name__, module_path)) _return = the_function(bot._handlers, bot) except TypeError as e: # DEPRECATED: ancient plugins logger.warning( "[DEPRECATED] upgrade {1}.{0}(handlers) to {0}(bot) and use bot._handlers internally" .format(the_function.__name__, module_path)) _return = the_function(bot._handlers) if type(_return) is list: available_commands = _return elif function_name.startswith("_"): pass else: candidate_commands.append((function_name, the_function)) if available_commands is False: # implicit init, legacy support: assume all candidate_commands are user-available register_user_command([ function_name for function_name, function in candidate_commands ]) elif available_commands is []: # explicit init, no user-available commands pass else: # explicit init, legacy support: _initialise() returned user-available commands register_user_command(available_commands) except Exception as e: logger.exception( "EXCEPTION during plugin init: {}".format(module_path)) return # skip this, attempt next plugin """ pass 2: register filtered functions tracking.current() and the CommandDispatcher registers might be out of sync if a combination of decorators and register_user_command/register_admin_command is used since decorators execute immediately upon import """ plugin_tracking = tracking.current() explicit_admin_commands = plugin_tracking["commands"]["admin"] all_commands = plugin_tracking["commands"]["all"] registered_commands = [] for function_name, the_function in candidate_commands: if function_name in all_commands: is_admin = False text_function_name = function_name if function_name in explicit_admin_commands: is_admin = True text_function_name = "*" + text_function_name command.register(the_function, admin=is_admin, final=True) registered_commands.append(text_function_name) if registered_commands: logger.info("{} - {}".format(module_name, ", ".join(registered_commands))) else: logger.info("{} - no commands".format(module_name)) tracking.end() return True
def load(bot, module_path, module_name=None): """loads a single plugin-like object as identified by module_path, and initialise it""" if module_name is None: module_name = module_path.split(".")[-1] if module_path in tracking.list: raise RuntimeError("{} already loaded".format(module_path)) tracking.start({ "module": module_name, "module.path": module_path }) try: if module_path in sys.modules: importlib.reload(sys.modules[module_path]) logger.debug("reloading {}".format(module_path)) else: importlib.import_module(module_path) logger.debug("importing {}".format(module_path)) except Exception as e: logger.exception("EXCEPTION during plugin import: {}".format(module_path)) return public_functions = [o for o in getmembers(sys.modules[module_path], isfunction)] candidate_commands = [] """pass 1: run optional callable: _initialise, _initialize * performs house-keeping tasks (e.g. migration, tear-up, pre-init, etc) * registers user and/or admin commands """ available_commands = False # default: ALL try: for function_name, the_function in public_functions: if function_name == "_initialise" or function_name == "_initialize": """accepted function signatures: CURRENT version >= 2.4 | function() version >= 2.4 | function(bot) - parameter must be named "bot" LEGACY version <= 2.4 | function(handlers, bot) ancient | function(handlers) """ _expected = list(inspect.signature(the_function).parameters) if len(_expected) == 0: the_function() _return = [] elif len(_expected) == 1 and _expected[0] == "bot": the_function(bot) _return = [] else: try: # legacy support, pre-2.4 logger.info("[LEGACY] upgrade {1}.{0}(handlers, bot) to {0}(bot) and use bot._handlers internally" .format(the_function.__name__, module_path)) _return = the_function(bot._handlers, bot) except TypeError as e: # DEPRECATED: ancient plugins logger.warning("[DEPRECATED] upgrade {1}.{0}(handlers) to {0}(bot) and use bot._handlers internally" .format(the_function.__name__, module_path)) _return = the_function(bot._handlers) if type(_return) is list: available_commands = _return elif function_name.startswith("_"): pass else: candidate_commands.append((function_name, the_function)) if available_commands is False: # implicit init, legacy support: assume all candidate_commands are user-available register_user_command([function_name for function_name, function in candidate_commands]) elif available_commands is []: # explicit init, no user-available commands pass else: # explicit init, legacy support: _initialise() returned user-available commands register_user_command(available_commands) except Exception as e: logger.exception("EXCEPTION during plugin init: {}".format(module_path)) return # skip this, attempt next plugin """ pass 2: register filtered functions tracking.current() and the CommandDispatcher registers might be out of sync if a combination of decorators and register_user_command/register_admin_command is used since decorators execute immediately upon import """ plugin_tracking = tracking.current() explicit_admin_commands = plugin_tracking["commands"]["admin"] all_commands = plugin_tracking["commands"]["all"] registered_commands = [] for function_name, the_function in candidate_commands: if function_name in all_commands: is_admin = False text_function_name = function_name if function_name in explicit_admin_commands: is_admin = True text_function_name = "*" + text_function_name command.register(the_function, admin=is_admin, final=True) registered_commands.append(text_function_name) if registered_commands: logger.info("{} - {}".format(module_name, ", ".join(registered_commands))) else: logger.info("{} - no commands".format(module_name)) tracking.end() return True
async def load(bot, module_path, module_name=None): """loads a single plugin-like object as identified by module_path Args: bot: HangupsBot instance module_path: string, python import style relative to the main script module_name: string, custom name Returns: boolean, True if the plugin was loaded successfully Raises: AssertionError: the plugin is already loaded """ module_name = module_name or module_path.split(".")[-1] assert module_path not in tracking.list await tracking.start({"module": module_name, "module.path": module_path}) if not load_module(module_path): tracking.end() return False setattr(sys.modules[module_path], 'print', utils.print_to_logger) if hasattr(sys.modules[module_path], "hangups_shim"): logger.info("%s has legacy hangups reference", module_name) public_functions = list(getmembers(sys.modules[module_path], isfunction)) candidate_commands = [] # run optional callable _initialise or _initialize and cature try: for function_name, the_function in public_functions: if function_name not in ("_initialise", "_initialize"): if not function_name.startswith("_"): # skip private functions candidate_commands.append( (function_name.lower(), the_function)) continue # accepted function signatures: # coro/function() # coro/function(bot) - parameter must be named "bot" expected = list(inspect.signature(the_function).parameters) if len(expected) > 1 or (expected and expected[0] != "bot"): # plugin not updated since v2.4 logger.warning("%s of %s does not comply with the current " "initialize standard!", function_name, module_path) continue result = the_function(bot) if expected else the_function() if asyncio.iscoroutinefunction(the_function): await result except: # capture all Exceptions # pylint: disable=bare-except logger.exception("error on plugin init: %s", module_path) return False # register filtered functions # tracking.current and the CommandDispatcher might be out of sync if a # combination of decorators and register_{admin, user}_command # is used since decorators execute immediately upon import plugin_tracking = tracking.current explicit_admin_commands = plugin_tracking["commands"]["admin"] all_commands = plugin_tracking["commands"]["all"] registered_commands = [] for function_name, the_function in candidate_commands: if function_name not in all_commands: continue is_admin = False text_function_name = function_name if function_name in explicit_admin_commands: is_admin = True text_function_name = "*" + text_function_name command.register(the_function, admin=is_admin, final=True) registered_commands.append(text_function_name) logger.debug("%s - %s", module_name, ", ".join(registered_commands) or "no commands") tracking.end() return True
def _load_plugins(self): plugin_list = self.get_config_option('plugins') if plugin_list is None: print(_("HangupsBot: config.plugins is not defined, using ALL")) plugin_path = os.path.dirname(os.path.realpath(sys.argv[0])) + os.sep + "plugins" plugin_list = [ os.path.splitext(f)[0] # take only base name (no extension)... for f in os.listdir(plugin_path) # ...by iterating through each node in the plugin_path... if os.path.isfile(os.path.join(plugin_path,f)) and not f.startswith(("_", ".")) # ...that does not start with _ . and f.endswith(".py")] # ...and must end with .py for module in plugin_list: module_path = "plugins.{}".format(module) try: exec("import {}".format(module_path)) except Exception as e: message = "{} @ {}".format(e, module_path) print(_("EXCEPTION during plugin import: {}").format(message)) logging.exception(message) continue print(_("plugin: {}").format(module)) public_functions = [o for o in getmembers(sys.modules[module_path], isfunction)] candidate_commands = [] """ pass 1: run _initialise()/_initialize() and filter out "hidden" functions legacy notice: older plugins will return a list of user-available functions via _initialise/_initialize(). this LEGACY behaviour will continue to be supported. however, it is HIGHLY RECOMMENDED to use register_user_command(<LIST command_names>) and register_admin_command(<LIST command_names>) for better security """ available_commands = False # default: ALL try: self._handlers.plugin_preinit_stats((module, module_path)) for function_name, the_function in public_functions: if function_name == "_initialise" or function_name == "_initialize": try: _return = the_function(self._handlers, bot=self) except TypeError as e: # implement legacy support for plugins that don't support the bot reference _return = the_function(self._handlers) if type(_return) is list: available_commands = _return elif function_name.startswith("_"): pass else: candidate_commands.append((function_name, the_function)) if available_commands is False: # implicit init, legacy support: assume all candidate_commands are user-available self._handlers.register_user_command([function_name for function_name, function in candidate_commands]) elif available_commands is []: # explicit init, no user-available commands pass else: # explicit init, legacy support: _initialise() returned user-available commands self._handlers.register_user_command(available_commands) except Exception as e: message = "{} @ {}".format(e, module_path) print(_("EXCEPTION during plugin init: {}").format(message)) logging.exception(message) continue # skip this, attempt next plugin """ pass 2: register filtered functions """ plugin_tracking = self._handlers.plugin_get_stats() explicit_admin_commands = plugin_tracking["commands"]["admin"] all_commands = plugin_tracking["commands"]["all"] registered_commands = [] for function_name, the_function in candidate_commands: if function_name in all_commands: command.register(the_function) text_function_name = function_name if function_name in explicit_admin_commands: text_function_name = "*" + text_function_name registered_commands.append(text_function_name) if registered_commands: print(_("added: {}").format(", ".join(registered_commands))) self._handlers.all_plugins_loaded()