Files
2023-04-26 22:16:57 +02:00

162 lines
5.7 KiB
Python

from glob import glob
from os import path
from discord.ext import commands
import yaml
"""
Not the prettiest this, works by replacing any found aliases in a command string with the actual command names.
Allows per guild aliases of subcommands without doing anything to discord.py itself. Suggestions of better/correct
ways of doing this appreciated.
"""
class Aliaser:
def __init__(self, localization_folder, default_lang):
self.localization_folder = path.realpath(localization_folder)
self.default_lang = default_lang
self.index_localizations()
self.load_localizations()
def _gen_alias_dict(self, commands):
"""Reverses the direction of a dict of commans→aliases to be alias→command."""
inverted = {}
subcommands = {}
for cmd, properties in commands.items():
if isinstance(properties, list):
for alias in properties:
inverted[alias] = cmd
else:
for alias in properties['aliases']:
inverted[alias] = cmd
if properties.get('sub_commands', None):
subcommands[cmd] = self._gen_alias_dict(properties['sub_commands'])
inverted['sub_commands'] = subcommands
return inverted
def index_localizations(self):
self.localization_table = {}
for folder in glob(path.join(self.localization_folder, "*/")):
if 'global' in folder:
continue
folder_base = path.basename(path.dirname(folder))
self.localization_table[folder_base] = False
def load_localizations(self):
for lang in self.localization_table.keys():
with open(path.join(self.localization_folder, lang, "commands.yaml"), "r", encoding='utf-8') as f:
data = yaml.load(f, Loader=yaml.SafeLoader)
self.localization_table[lang] = {'aliases': data, 'commands': self._gen_alias_dict(data)}
def convert_alias(self, locale, default=None, parents=None):
if parents is None:
parents = []
try:
locale = self.localization_table[locale]
except KeyError:
locale = self.localization_table[self.default_lang]
# Traverse through the alias tree as dictated by the parents list
def traverse(alias_tree, parents, alias):
# Return the command when no more parents exist
if not parents:
return alias_tree.get(alias, None)
parent = parents.pop(0)
sub_commands = alias_tree.get('sub_commands', None)
if not sub_commands:
return None
alias_tree = sub_commands.get(parent, None)
if not alias_tree:
return None
return traverse(alias_tree, parents, alias)
alias_tree = locale['commands']
command = traverse(alias_tree, parents, default)
if command:
return command
return default
def get_cmd_help(self, locale, command=None, parents=None):
"""Fetches the command info dictionary."""
if parents is None:
parents = []
locale = self.localization_table[locale]
def traverse(command_tree, parents, command):
# Return the command when no more parents exist
if not parents:
return command_tree.get(command, [])
parent = command_tree.get(parents.pop(0), None)
if not parent:
return None
command_tree = parent.get('sub_commands', [])
if not command_tree:
return None
return traverse(command_tree, parents, command)
command_tree = locale['aliases']
if command:
return traverse(command_tree, parents, command)
return command_tree
def get_command(self, ctx):
"""Get a top level command."""
if not ctx.prefix:
ctx.command = None
return ctx
ctx.view.undo()
ctx.invoker = ctx.view.buffer[ctx.view.index:ctx.view.end]
alias = ctx.view.get_word()
command = self.convert_alias(ctx.locale, alias)
ctx.invoked_with = command
ctx.command = ctx.bot.all_commands.get(command)
if ctx.command and isinstance(ctx.command, commands.GroupMixin):
ctx = self.get_subcommand(ctx, ctx.command, [str(ctx.command)])
return ctx
def _replace_command(self, view, index, alias, command):
buf = view.buffer
changed = buf[index:]
view.buffer = buf[:index] + changed.replace(alias, command, 1)
return view
def get_subcommand(self, ctx, group=None, parents=None):
"""Recursicely replaces all subcommand aliases with subcommands."""
if parents is None:
parents = []
view = ctx.view
prev = view.previous
idx = view.index
# get the alias used
view.skip_ws()
alias = view.get_word()
sub_command = self.convert_alias(ctx.locale, alias, parents.copy())
parents.append(sub_command)
# Replace the alias in the command
self._replace_command(view, idx, alias, sub_command)
# Update the view to account for difference in length
strdiff = len(sub_command) - len(alias)
view.end += strdiff
view.index += strdiff
# Get the subcommand
if group:
sub = group.all_commands.get(sub_command, None)
else:
sub = ctx.bot.all_commands.get(sub_command, None)
# Translate any subsubcommands
if sub and isinstance(sub, commands.GroupMixin):
self.get_subcommand(ctx, sub, parents)
# Reset the view indexes
view.index = idx
view.previous = prev
return ctx