Source code for glitch.plugins

import importlib.util
import inspect
import logging

log = logging.getLogger(__name__)

from .decorators import *
from ..import config, messenger
from ..utils import DatabaseDictCI

plugin_map = DatabaseDictCI()


@messenger.add('onCommand')
[docs]def process_command(event): """Process an incoming command message into plugin. :param event: """ # Strip the command code as there is no real point for it event.in_message = event.in_message[1:] message = event.in_message # Check to see if command is a command # This is sucky as we have to iterate through every plugin and every alias # This should be profiled at some point # Everything dropped to lower case just in case # Matching alias put here, longest one will be used alias_match = {} for plugin in plugin_map: for member in inspect.getmembers(plugin_map[plugin], inspect.isfunction): if hasattr(member[1], 'aliases'): for alias in member[1].aliases: if message.lower().startswith(alias.lower()): alias_match[alias.lower()] = plugin if not alias_match: log.debug("No command found: " + message) event.out_message = "I have no command by that name" messenger.send('send', event) return try: alias = max(alias_match) except: log.critical("Critical error parsing command") log.critical("Alias match: " + str(alias_match)) log.critical("Message: " + message) plugin = alias_match[alias] # This is our new method! But can be streamlined for member in inspect.getmembers(plugin_map[plugin], inspect.isfunction): if hasattr(member[1], 'aliases'): if alias in member[1].aliases: function = member[1] break # Test permissions if event.in_source.check_permission(plugin, function.__name__): event.in_message = message[len(alias):].lstrip() messenger.send(alias, event) else: event.out_message = "You are not authorized for this command." messenger.send('send', event)
[docs]def get_command_functions(plugin): """Returns a list of functions of functions that have a command bound to them from a given plugin.""" functions = [] for i in inspect.getmembers(plugin, inspect.isfunction): if hasattr(i[1], 'aliases'): if i[1].aliases: functions.append(i[1]) return functions
[docs]def get_config(plugin_name): """Convenience function for an otherwise wordy dict lookup.""" return config['plugin_config'][plugin_name]
@messenger.add('load all plugins') def load_all_plugins(): # Unloading anything we have # Inhibit the dynamic view to allow mutability during iteration for plugin in list(plugin_map.keys()): unload_plugin(plugin) stop = False for plugin_name in config['plugins']: if load_plugin(plugin_name): stop = True if stop: messenger.send('quitAll', "!!!!ATTENTION!!!! There have been critical updates to " + config["name"] + " config file and the bot will now shut down. Please review configuration before restarting.") @messenger.add('load plugin')
[docs]def load_plugin(plugin_name) -> bool: """Loads system plugins first followed by any user installed plugins in the bot directory.""" spec = importlib.util.find_spec('glitch.plugins.' + plugin_name) if not spec: log.error("Plugin %s not found in system paths.", plugin_name) return False try: plugin = importlib.import_module('glitch.plugins.' + plugin_name) plugin_map[plugin_name] = plugin reboot = False if hasattr(plugin, 'CONFIG') and hasattr(plugin, 'CONFIG_VERSION'): reboot = manage_plugin_config(plugin_name, plugin.CONFIG, plugin.CONFIG_VERSION) if reboot: from ..import save_config save_config() log.info("Plugin %s loaded.", plugin_name) return reboot except Exception as e: log.critical("Error when loading %s.", plugin_name) log.exception(e)
[docs]def manage_plugin_config(plugin_name, plugin_config, config_version): """Manage plugin config in bot.yaml This function has the very complicated task on managing and updating the main config with the plugin config. The goal is to preserve user config in the event of a plugin config update. Plugin config version requires a bot config version of equal or greater value. Less than means the config must be updated. Returns if bot needs reboot or not TODO: Does not update nested config expansions. Find a better way? """ # Step 1: Check for config in db and add if not # Possible issue when added with add plugin command, recheck if 'plugin_config' not in config: config['plugin_config'] = {} if plugin_name not in config['plugin_config']: config['plugin_config'][plugin_name] = plugin_config config['plugin_config'][plugin_name]['version'] = config_version return True # Step 2: Check for version mismatch, if not then we do not need to update. if config_version < get_config(plugin_name)['version']: log.critical(plugin_name + ": !!!!ATTENTION!!!! Plugin version less than config version. " + "Fix version mismatch before restarting bot!") return True if config_version == get_config(plugin_name)['version']: return False # Step 3: Iterate though config and see if we added anything plugin_keys = list(plugin_config.keys()) plugin_keys2 = list(plugin_keys) for key in plugin_keys: if key in get_config(plugin_name): plugin_keys2.pop(plugin_keys2.index(key)) else: plugin_keys2.pop(plugin_keys2.index(key)) get_config(plugin_name)[key] = plugin_config[key] log.warning(plugin_name + ": Added config option: " + key) if plugin_keys2: log.warning(plugin_name + ": Config update complete. Please compare between " + "bot config file and plugin documentation to verify syntax did not change.") log.warning(plugin_name + str(plugin_keys2)) # Step 4: Find depreciated keys difference = set(list(plugin_config.keys())) - set(plugin_keys) if difference: log.warning( plugin_name + ": The following plugin config keys are now depriciated. Please manually remove them from the bot config file.") log.warning(plugin_name + str(difference)) get_config(plugin_name)["version"] = config_version return True
[docs]def unload_plugin(plugin): """Unload target plugin, remove maps from the messenger.""" for i in inspect.getmembers(plugin, inspect.isfunction): if hasattr(i[1], 'aliases'): for alias in i[1].aliases: messenger.delete(alias, i[1]) for i in inspect.getmembers(plugin, inspect.isfunction): if hasattr(i[1], 'events'): for event in i[1].events: messenger.delete(event, i[1]) del plugin_map[plugin]