diff options
| author | Rapptz <[email protected]> | 2019-03-15 05:54:23 -0400 |
|---|---|---|
| committer | Rapptz <[email protected]> | 2019-03-15 05:54:23 -0400 |
| commit | 3527203e0786ebd2d81a91868628ae78ed1e8df6 (patch) | |
| tree | 575391b03008355a1edcdb851f02e69e4bbff7c3 /discord/ext/commands/bot.py | |
| parent | [commands] Add Cog.description to get the clean docstring. (diff) | |
| download | discord.py-3527203e0786ebd2d81a91868628ae78ed1e8df6.tar.xz discord.py-3527203e0786ebd2d81a91868628ae78ed1e8df6.zip | |
[commands] Redesign HelpFormatter into HelpCommand
Part of #1938
Diffstat (limited to 'discord/ext/commands/bot.py')
| -rw-r--r-- | discord/ext/commands/bot.py | 147 |
1 files changed, 40 insertions, 107 deletions
diff --git a/discord/ext/commands/bot.py b/discord/ext/commands/bot.py index 694b466d..efa375d5 100644 --- a/discord/ext/commands/bot.py +++ b/discord/ext/commands/bot.py @@ -38,7 +38,7 @@ from .core import GroupMixin, Command from .view import StringView from .context import Context from .errors import CommandNotFound, CommandError -from .formatter import HelpFormatter +from .help import HelpCommand, DefaultHelpCommand from .cog import Cog def when_mentioned(bot, msg): @@ -84,71 +84,17 @@ def when_mentioned_or(*prefixes): return inner -_mentions_transforms = { - '@everyone': '@\u200beveryone', - '@here': '@\u200bhere' -} - -_mention_pattern = re.compile('|'.join(_mentions_transforms.keys())) - def _is_submodule(parent, child): return parent == child or child.startswith(parent + ".") -async def _default_help_command(ctx, *commands : str): - """Shows this message.""" - bot = ctx.bot - destination = ctx.message.author if bot.pm_help else ctx.message.channel - - def repl(obj): - return _mentions_transforms.get(obj.group(0), '') - - # help by itself just lists our own commands. - if len(commands) == 0: - pages = await bot.formatter.format_help_for(ctx, bot) - elif len(commands) == 1: - # try to see if it is a cog name - name = _mention_pattern.sub(repl, commands[0]) - command = None - if name in bot.cogs: - command = bot.cogs[name] - else: - command = bot.all_commands.get(name) - if command is None: - await destination.send(bot.command_not_found.format(name)) - return - - pages = await bot.formatter.format_help_for(ctx, command) - else: - name = _mention_pattern.sub(repl, commands[0]) - command = bot.all_commands.get(name) - if command is None: - await destination.send(bot.command_not_found.format(name)) - return - - for key in commands[1:]: - try: - key = _mention_pattern.sub(repl, key) - command = command.all_commands.get(key) - if command is None: - await destination.send(bot.command_not_found.format(key)) - return - except AttributeError: - await destination.send(bot.command_has_no_subcommands.format(command, key)) - return - - pages = await bot.formatter.format_help_for(ctx, command) +class _DefaultRepr: + def __repr__(self): + return '<default-help-command>' - if bot.pm_help is None: - characters = sum(map(len, pages)) - # modify destination based on length of pages. - if characters > 1000: - destination = ctx.message.author - - for page in pages: - await destination.send(page) +_default = _DefaultRepr() class BotBase(GroupMixin): - def __init__(self, command_prefix, formatter=None, description=None, pm_help=False, **options): + def __init__(self, command_prefix, help_command=_default, description=None, **options): super().__init__(**options) self.command_prefix = command_prefix self.extra_events = {} @@ -158,32 +104,19 @@ class BotBase(GroupMixin): self._check_once = [] self._before_invoke = None self._after_invoke = None + self._help_command = None self.description = inspect.cleandoc(description) if description else '' - self.pm_help = pm_help self.owner_id = options.get('owner_id') - self.command_not_found = options.pop('command_not_found', 'No command called "{}" found.') - self.command_has_no_subcommands = options.pop('command_has_no_subcommands', 'Command {0.name} has no subcommands.') if options.pop('self_bot', False): self._skip_check = lambda x, y: x != y else: self._skip_check = lambda x, y: x == y - self.help_attrs = options.pop('help_attrs', {}) - - if 'name' not in self.help_attrs: - self.help_attrs['name'] = 'help' - - if formatter is not None: - if not isinstance(formatter, HelpFormatter): - raise discord.ClientException('Formatter must be a subclass of HelpFormatter') - self.formatter = formatter + if help_command is _default: + self.help_command = DefaultHelpCommand() else: - self.formatter = HelpFormatter() - - # pay no mind to this ugliness. - help_cmd = Command(_default_help_command, **self.help_attrs) - self.add_command(help_cmd) + self.help_command = help_command # internal helpers @@ -577,6 +510,9 @@ class BotBase(GroupMixin): if cog is None: return + help_command = self._help_command + if help_command and help_command.cog is cog: + help_command.cog = None cog._eject(self) # extensions @@ -685,6 +621,27 @@ class BotBase(GroupMixin): if _is_submodule(lib_name, module): del sys.modules[module] + # help command stuff + + @property + def help_command(self): + return self._help_command + + @help_command.setter + def help_command(self, value): + if value is not None: + if not isinstance(value, HelpCommand): + raise discord.ClientException('help_command must be a subclass of HelpCommand') + if self._help_command is not None: + self._help_command._remove_from_bot(self) + self._help_command = value + value._add_to_bot(self) + elif self._help_command is not None: + self._help_command._remove_from_bot(self) + self._help_command = None + else: + self._help_command = None + # command processing async def get_prefix(self, message): @@ -899,40 +856,16 @@ class Bot(BotBase, discord.Client): Whether the commands should be case insensitive. Defaults to ``False``. This attribute does not carry over to groups. You must set it to every group if you require group commands to be case insensitive as well. - description : :class:`str` + description: :class:`str` The content prefixed into the default help message. - self_bot : :class:`bool` + self_bot: :class:`bool` If ``True``, the bot will only listen to commands invoked by itself rather than ignoring itself. If ``False`` (the default) then the bot will ignore itself. This cannot be changed once initialised. - formatter : :class:`.HelpFormatter` - The formatter used to format the help message. By default, it uses - the :class:`.HelpFormatter`. Check it for more info on how to override it. - If you want to change the help command completely (add aliases, etc) then - a call to :meth:`~.Bot.remove_command` with 'help' as the argument would do the - trick. - pm_help : Optional[:class:`bool`] - A tribool that indicates if the help command should PM the user instead of - sending it to the channel it received it from. If the boolean is set to - ``True``, then all help output is PM'd. If ``False``, none of the help - output is PM'd. If ``None``, then the bot will only PM when the help - message becomes too long (dictated by more than 1000 characters). - Defaults to ``False``. - help_attrs : :class:`dict` - A dictionary of options to pass in for the construction of the help command. - This allows you to change the command behaviour without actually changing - the implementation of the command. The attributes will be the same as the - ones passed in the :class:`.Command` constructor. Note that ``pass_context`` - will always be set to ``True`` regardless of what you pass in. - command_not_found : :class:`str` - The format string used when the help command is invoked with a command that - is not found. Useful for i18n. Defaults to ``"No command called {} found."``. - The only format argument is the name of the command passed. - command_has_no_subcommands : :class:`str` - The format string used when the help command is invoked with requests for a - subcommand but the command does not have any subcommands. Defaults to - ``"Command {0.name} has no subcommands."``. The first format argument is the - :class:`.Command` attempted to get a subcommand and the second is the name. + help_command: Optional[:class:`.HelpCommand`] + The help command implementation to use. This can be dynamically + set at runtime. To remove the help command pass ``None``. For more + information on implementing a help command, see :ref:`ext_commands_help_command`. owner_id: Optional[:class:`int`] The ID that owns the bot. If this is not set and is then queried via :meth:`.is_owner` then it is fetched automatically using |