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]