mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-06-04 11:02:37 +00:00
parent
27c6d2c923
commit
3527203e07
@ -14,7 +14,7 @@ from .bot import Bot, AutoShardedBot, when_mentioned, when_mentioned_or
|
||||
from .context import Context
|
||||
from .core import *
|
||||
from .errors import *
|
||||
from .formatter import HelpFormatter, Paginator
|
||||
from .help import *
|
||||
from .converter import *
|
||||
from .cooldowns import *
|
||||
from .cog import *
|
||||
|
@ -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
|
||||
class _DefaultRepr:
|
||||
def __repr__(self):
|
||||
return '<default-help-command>'
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
|
@ -810,27 +810,15 @@ class Command(_BaseCommand):
|
||||
@property
|
||||
def signature(self):
|
||||
"""Returns a POSIX-like signature useful for help command output."""
|
||||
result = []
|
||||
parent = self.full_parent_name
|
||||
|
||||
if len(self.aliases) > 0:
|
||||
aliases = '|'.join(self.aliases)
|
||||
fmt = '[%s|%s]' % (self.name, aliases)
|
||||
if parent:
|
||||
fmt = parent + ' ' + fmt
|
||||
result.append(fmt)
|
||||
else:
|
||||
name = self.name if not parent else parent + ' ' + self.name
|
||||
result.append(name)
|
||||
|
||||
if self.usage is not None:
|
||||
result.append(self.usage)
|
||||
return ' '.join(result)
|
||||
return self.usage
|
||||
|
||||
|
||||
params = self.clean_params
|
||||
if not params:
|
||||
return ' '.join(result)
|
||||
return ''
|
||||
|
||||
result = []
|
||||
for name, param in params.items():
|
||||
greedy = isinstance(param.annotation, converters._Greedy)
|
||||
|
||||
|
@ -1,352 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2019 Rapptz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import inspect
|
||||
import discord.utils
|
||||
|
||||
from .core import GroupMixin, Command
|
||||
from .errors import CommandError
|
||||
# from discord.iterators import _FilteredAsyncIterator
|
||||
|
||||
# help -> shows info of bot on top/bottom and lists subcommands
|
||||
# help command -> shows detailed info of command
|
||||
# help command <subcommand chain> -> same as above
|
||||
|
||||
# <description>
|
||||
|
||||
# <command signature with aliases>
|
||||
|
||||
# <long doc>
|
||||
|
||||
# Cog:
|
||||
# <command> <shortdoc>
|
||||
# <command> <shortdoc>
|
||||
# Other Cog:
|
||||
# <command> <shortdoc>
|
||||
# No Category:
|
||||
# <command> <shortdoc>
|
||||
|
||||
# Type <prefix>help command for more info on a command.
|
||||
# You can also type <prefix>help category for more info on a category.
|
||||
|
||||
class Paginator:
|
||||
"""A class that aids in paginating code blocks for Discord messages.
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
prefix: :class:`str`
|
||||
The prefix inserted to every page. e.g. three backticks.
|
||||
suffix: :class:`str`
|
||||
The suffix appended at the end of every page. e.g. three backticks.
|
||||
max_size: :class:`int`
|
||||
The maximum amount of codepoints allowed in a page.
|
||||
"""
|
||||
def __init__(self, prefix='```', suffix='```', max_size=2000):
|
||||
self.prefix = prefix
|
||||
self.suffix = suffix
|
||||
self.max_size = max_size - len(suffix)
|
||||
self._current_page = [prefix]
|
||||
self._count = len(prefix) + 1 # prefix + newline
|
||||
self._pages = []
|
||||
|
||||
def add_line(self, line='', *, empty=False):
|
||||
"""Adds a line to the current page.
|
||||
|
||||
If the line exceeds the :attr:`max_size` then an exception
|
||||
is raised.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
line: str
|
||||
The line to add.
|
||||
empty: bool
|
||||
Indicates if another empty line should be added.
|
||||
|
||||
Raises
|
||||
------
|
||||
RuntimeError
|
||||
The line was too big for the current :attr:`max_size`.
|
||||
"""
|
||||
if len(line) > self.max_size - len(self.prefix) - 2:
|
||||
raise RuntimeError('Line exceeds maximum page size %s' % (self.max_size - len(self.prefix) - 2))
|
||||
|
||||
if self._count + len(line) + 1 > self.max_size:
|
||||
self.close_page()
|
||||
|
||||
self._count += len(line) + 1
|
||||
self._current_page.append(line)
|
||||
|
||||
if empty:
|
||||
self._current_page.append('')
|
||||
self._count += 1
|
||||
|
||||
def close_page(self):
|
||||
"""Prematurely terminate a page."""
|
||||
self._current_page.append(self.suffix)
|
||||
self._pages.append('\n'.join(self._current_page))
|
||||
self._current_page = [self.prefix]
|
||||
self._count = len(self.prefix) + 1 # prefix + newline
|
||||
|
||||
@property
|
||||
def pages(self):
|
||||
"""Returns the rendered list of pages."""
|
||||
# we have more than just the prefix in our current page
|
||||
if len(self._current_page) > 1:
|
||||
self.close_page()
|
||||
return self._pages
|
||||
|
||||
def __repr__(self):
|
||||
fmt = '<Paginator prefix: {0.prefix} suffix: {0.suffix} max_size: {0.max_size} count: {0._count}>'
|
||||
return fmt.format(self)
|
||||
|
||||
class HelpFormatter:
|
||||
"""The default base implementation that handles formatting of the help
|
||||
command.
|
||||
|
||||
To override the behaviour of the formatter, :meth:`~.HelpFormatter.format`
|
||||
should be overridden. A number of utility functions are provided for use
|
||||
inside that method.
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
show_hidden: :class:`bool`
|
||||
Dictates if hidden commands should be shown in the output.
|
||||
Defaults to ``False``.
|
||||
show_check_failure: :class:`bool`
|
||||
Dictates if commands that have their :attr:`.Command.checks` failed
|
||||
shown. Defaults to ``False``.
|
||||
width: :class:`int`
|
||||
The maximum number of characters that fit in a line.
|
||||
Defaults to 80.
|
||||
commands_heading: :class:`str`
|
||||
The command list's heading string used when the help command is invoked with a category name.
|
||||
Useful for i18n. Defaults to ``"Commands:"``
|
||||
no_category: :class:`str`
|
||||
The string used when there is a command which does not belong to any category(cog).
|
||||
Useful for i18n. Defaults to ``"No Category"``
|
||||
"""
|
||||
def __init__(self, show_hidden=False, show_check_failure=False, width=80,
|
||||
commands_heading="Commands:", no_category="No Category"):
|
||||
self.width = width
|
||||
self.show_hidden = show_hidden
|
||||
self.show_check_failure = show_check_failure
|
||||
self.commands_heading = commands_heading
|
||||
self.no_category = no_category
|
||||
|
||||
def has_subcommands(self):
|
||||
""":class:`bool`: Specifies if the command has subcommands."""
|
||||
return isinstance(self.command, GroupMixin)
|
||||
|
||||
def is_bot(self):
|
||||
""":class:`bool`: Specifies if the command being formatted is the bot itself."""
|
||||
return self.command is self.context.bot
|
||||
|
||||
def is_cog(self):
|
||||
""":class:`bool`: Specifies if the command being formatted is actually a cog."""
|
||||
return not self.is_bot() and not isinstance(self.command, Command)
|
||||
|
||||
def shorten(self, text):
|
||||
"""Shortens text to fit into the :attr:`width`."""
|
||||
if len(text) > self.width:
|
||||
return text[:self.width - 3] + '...'
|
||||
return text
|
||||
|
||||
@property
|
||||
def max_name_size(self):
|
||||
""":class:`int`: Returns the largest name length of a command or if it has subcommands
|
||||
the largest subcommand name."""
|
||||
try:
|
||||
commands = self.command.all_commands if not self.is_cog() else self.context.bot.all_commands
|
||||
if commands:
|
||||
return max(map(lambda c: discord.utils._string_width(c.name) if self.show_hidden or not c.hidden else 0, commands.values()))
|
||||
return 0
|
||||
except AttributeError:
|
||||
return len(self.command.name)
|
||||
|
||||
@property
|
||||
def clean_prefix(self):
|
||||
"""The cleaned up invoke prefix. i.e. mentions are ``@name`` instead of ``<@id>``."""
|
||||
user = self.context.guild.me if self.context.guild else self.context.bot.user
|
||||
# this breaks if the prefix mention is not the bot itself but I
|
||||
# consider this to be an *incredibly* strange use case. I'd rather go
|
||||
# for this common use case rather than waste performance for the
|
||||
# odd one.
|
||||
return self.context.prefix.replace(user.mention, '@' + user.display_name)
|
||||
|
||||
def get_command_signature(self):
|
||||
"""Retrieves the signature portion of the help page."""
|
||||
prefix = self.clean_prefix
|
||||
cmd = self.command
|
||||
return prefix + cmd.signature
|
||||
|
||||
def get_ending_note(self):
|
||||
"""Returns help command's ending note. This is mainly useful to override for i18n purposes."""
|
||||
command_name = self.context.invoked_with
|
||||
return "Type {0}{1} command for more info on a command.\n" \
|
||||
"You can also type {0}{1} category for more info on a category.".format(self.clean_prefix, command_name)
|
||||
|
||||
async def filter_command_list(self):
|
||||
"""Returns a filtered list of commands based on the two attributes
|
||||
provided, :attr:`show_check_failure` and :attr:`show_hidden`.
|
||||
Also filters based on if :meth:`~.HelpFormatter.is_cog` is valid.
|
||||
|
||||
Returns
|
||||
--------
|
||||
iterable
|
||||
An iterable with the filter being applied. The resulting value is
|
||||
a (key, value) :class:`tuple` of the command name and the command itself.
|
||||
"""
|
||||
|
||||
def sane_no_suspension_point_predicate(tup):
|
||||
cmd = tup[1]
|
||||
if self.is_cog():
|
||||
# filter commands that don't exist to this cog.
|
||||
if cmd.cog is not self.command:
|
||||
return False
|
||||
|
||||
if cmd.hidden and not self.show_hidden:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
async def predicate(tup):
|
||||
if sane_no_suspension_point_predicate(tup) is False:
|
||||
return False
|
||||
|
||||
cmd = tup[1]
|
||||
try:
|
||||
return await cmd.can_run(self.context)
|
||||
except CommandError:
|
||||
return False
|
||||
|
||||
iterator = self.command.all_commands.items() if not self.is_cog() else self.context.bot.all_commands.items()
|
||||
if self.show_check_failure:
|
||||
return filter(sane_no_suspension_point_predicate, iterator)
|
||||
|
||||
# Gotta run every check and verify it
|
||||
ret = []
|
||||
for elem in iterator:
|
||||
valid = await predicate(elem)
|
||||
if valid:
|
||||
ret.append(elem)
|
||||
|
||||
return ret
|
||||
|
||||
def _add_subcommands_to_page(self, max_width, commands):
|
||||
for name, command in commands:
|
||||
if name in command.aliases:
|
||||
# skip aliases
|
||||
continue
|
||||
width_gap = discord.utils._string_width(name) - len(name)
|
||||
entry = ' {0:<{width}} {1}'.format(name, command.short_doc, width=max_width-width_gap)
|
||||
shortened = self.shorten(entry)
|
||||
self._paginator.add_line(shortened)
|
||||
|
||||
async def format_help_for(self, context, command_or_bot):
|
||||
"""Formats the help page and handles the actual heavy lifting of how
|
||||
the help command looks like. To change the behaviour, override the
|
||||
:meth:`~.HelpFormatter.format` method.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
context: :class:`.Context`
|
||||
The context of the invoked help command.
|
||||
command_or_bot: :class:`.Command` or :class:`.Bot`
|
||||
The bot or command that we are getting the help of.
|
||||
|
||||
Returns
|
||||
--------
|
||||
list
|
||||
A paginated output of the help command.
|
||||
"""
|
||||
self.context = context
|
||||
self.command = command_or_bot
|
||||
return await self.format()
|
||||
|
||||
async def format(self):
|
||||
"""Handles the actual behaviour involved with formatting.
|
||||
|
||||
To change the behaviour, this method should be overridden.
|
||||
|
||||
Returns
|
||||
--------
|
||||
list
|
||||
A paginated output of the help command.
|
||||
"""
|
||||
self._paginator = Paginator()
|
||||
|
||||
# we need a padding of ~80 or so
|
||||
|
||||
description = self.command.description if not self.is_cog() else inspect.getdoc(self.command)
|
||||
|
||||
if description:
|
||||
# <description> portion
|
||||
self._paginator.add_line(description, empty=True)
|
||||
|
||||
if isinstance(self.command, Command):
|
||||
# <signature portion>
|
||||
signature = self.get_command_signature()
|
||||
self._paginator.add_line(signature, empty=True)
|
||||
|
||||
# <long doc> section
|
||||
if self.command.help:
|
||||
self._paginator.add_line(self.command.help, empty=True)
|
||||
|
||||
# end it here if it's just a regular command
|
||||
if not self.has_subcommands():
|
||||
self._paginator.close_page()
|
||||
return self._paginator.pages
|
||||
|
||||
max_width = self.max_name_size
|
||||
|
||||
def category(tup):
|
||||
cog = tup[1].cog_name
|
||||
# we insert the zero width space there to give it approximate
|
||||
# last place sorting position.
|
||||
return cog + ':' if cog is not None else '\u200b' + self.no_category + ':'
|
||||
|
||||
filtered = await self.filter_command_list()
|
||||
if self.is_bot():
|
||||
data = sorted(filtered, key=category)
|
||||
for category, commands in itertools.groupby(data, key=category):
|
||||
# there simply is no prettier way of doing this.
|
||||
commands = sorted(commands)
|
||||
if len(commands) > 0:
|
||||
self._paginator.add_line(category)
|
||||
|
||||
self._add_subcommands_to_page(max_width, commands)
|
||||
else:
|
||||
filtered = sorted(filtered)
|
||||
if filtered:
|
||||
self._paginator.add_line(self.commands_heading)
|
||||
self._add_subcommands_to_page(max_width, filtered)
|
||||
|
||||
# add the ending note
|
||||
self._paginator.add_line()
|
||||
ending_note = self.get_ending_note()
|
||||
self._paginator.add_line(ending_note)
|
||||
return self._paginator.pages
|
1144
discord/ext/commands/help.py
Normal file
1144
discord/ext/commands/help.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -94,13 +94,21 @@ Cogs
|
||||
|
||||
.. _ext_commands_api_formatters:
|
||||
|
||||
Formatters
|
||||
-----------
|
||||
Help Commands
|
||||
-----------------
|
||||
|
||||
.. autoclass:: discord.ext.commands.Paginator
|
||||
.. autoclass:: discord.ext.commands.HelpCommand
|
||||
:members:
|
||||
|
||||
.. autoclass:: discord.ext.commands.HelpFormatter
|
||||
.. autoclass:: discord.ext.commands.DefaultHelpCommand
|
||||
:members:
|
||||
:exclude-members: send_bot_help, send_cog_help, send_group_help, send_command_help, prepare_help_command
|
||||
|
||||
.. autoclass:: discord.ext.commands.MinimalHelpCommand
|
||||
:members:
|
||||
:exclude-members: send_bot_help, send_cog_help, send_group_help, send_command_help, prepare_help_command
|
||||
|
||||
.. autoclass:: discord.ext.commands.Paginator
|
||||
:members:
|
||||
|
||||
.. _ext_commands_api_checks:
|
||||
|
@ -936,6 +936,64 @@ The error handlers, either :meth:`.Command.error` or :func:`.on_command_error`,
|
||||
have been re-ordered to use the :class:`~ext.commands.Context` as its first parameter to be consistent with other events
|
||||
and commands.
|
||||
|
||||
HelpFormatter and Help Command Changes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The :class:`~.commands.HelpFormatter` class has been removed. It has been replaced with a :class:`~.commands.HelpCommand` class. This class now stores all the command handling and processing of the help command.
|
||||
|
||||
The help command is now stored in the :attr:`.Bot.help_command` attribute. As an added extension, you can disable the help command completely by assigning the attribute to ``None`` or passing it at ``__init__`` as ``help_command=None``.
|
||||
|
||||
The new interface allows the help command to be customised through special methods that can be overridden.
|
||||
|
||||
- :meth:`.HelpCommand.send_bot_help`
|
||||
- Called when the user requested for help with the entire bot.
|
||||
- :meth:`.HelpCommand.send_cog_help`
|
||||
- Called when the user requested for help with a specific cog.
|
||||
- :meth:`.HelpCommand.send_group_help`
|
||||
- Called when the user requested for help with a :class:`~.commands.Group`
|
||||
- :meth:`.HelpCommand.send_command_help`
|
||||
- Called when the user requested for help with a :class:`~.commands.Command`
|
||||
- :meth:`.HelpCommand.get_destination`
|
||||
- Called to know where to send the help messages. Useful for deciding whether to DM or not.
|
||||
- :meth:`.HelpCommand.command_not_found`
|
||||
- A function (or coroutine) that returns a presentable no command found string.
|
||||
- :meth:`.HelpCommand.subcommand_not_found`
|
||||
- A function (or coroutine) that returns a string when a subcommand is not found.
|
||||
- :meth:`.HelpCommand.send_error_message`
|
||||
- A coroutine that gets passed the result of :meth:`.HelpCommand.command_not_found` and :meth:`.HelpCommand.subcommand_not_found`.
|
||||
- By default it just sends the message. But you can, for example, override it to put it in an embed.
|
||||
- :meth:`.HelpCommand.on_help_command_error`
|
||||
- The :ref:`error handler <ext_commands_error_handler>` for the help command if you want to add one.
|
||||
- :meth:`.HelpCommand.prepare_help_command`
|
||||
- A coroutine that is called right before the help command processing is done.
|
||||
|
||||
Certain subclasses can implement more customisable methods.
|
||||
|
||||
The old ``HelpFormatter`` was replaced with :class:`~.commands.DefaultHelpCommand`\, which implements all of the logic of the old help command. The customisable methods can be found in the accompanying documentation.
|
||||
|
||||
The library now provides a new more minimalistic :class:`~.commands.HelpCommand` implementation that doesn't take as much space, :class:`~.commands.MinimalHelpCommand`. The customisable methods can also be found in the accompanying documentation.
|
||||
|
||||
A frequent request was if you could associate a help command with a cog. The new design allows for dynamically changing of cog through binding it to the :attr:`.HelpCommand.cog` attribute. After this assignment the help command will pretend to be part of the cog and everything should work as expected. When the cog is unloaded then the help command will be "unbound" from the cog.
|
||||
|
||||
For example, to implement a :class:`~.commands.HelpCommand` in a cog, the following snippet can be used.
|
||||
|
||||
.. code-block:: python3
|
||||
|
||||
class MyHelpCommand(commands.MinimalHelpCommand):
|
||||
def get_command_signature(self, command):
|
||||
return '{0.context.clean_prefix}{1.qualified_name} {1.signature}'.format(self, command)
|
||||
|
||||
class MyCog(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self._original_help_command = bot.help_command
|
||||
bot.help_command = MyHelpCommand()
|
||||
bot.help_command.cog = self
|
||||
|
||||
def cog_unload(self):
|
||||
self.bot.help_command = self._original_help_command
|
||||
|
||||
For more information, check out the relevant :ref:`documentation <ext_commands_help_command>`.
|
||||
|
||||
Cog Changes
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user