mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 11:18:54 -05:00
Add a provisional API for replacing the help formatter (#4011)
* Adds an API for replacing the help formatter * Apply suggestions from code review Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com> * add note about provisionality Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com>
This commit is contained in:
parent
6cef336417
commit
4f808306ba
@ -44,3 +44,17 @@ extend functionalities used throughout the bot, as outlined below.
|
|||||||
:no-undoc-members:
|
:no-undoc-members:
|
||||||
|
|
||||||
.. autoclass:: APIToken
|
.. autoclass:: APIToken
|
||||||
|
|
||||||
|
******************
|
||||||
|
Help Functionality
|
||||||
|
******************
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
The content in this section is provisional and may change
|
||||||
|
without prior notice or warning. Updates to this will be communicated
|
||||||
|
on `this issue <https://github.com/Cog-Creators/Red-DiscordBot/issues/4084>`_
|
||||||
|
|
||||||
|
|
||||||
|
.. automodule:: redbot.core.commands.help
|
||||||
|
:members:
|
||||||
|
|||||||
@ -208,6 +208,61 @@ class RedBase(
|
|||||||
|
|
||||||
self._deletion_requests: MutableMapping[int, asyncio.Lock] = weakref.WeakValueDictionary()
|
self._deletion_requests: MutableMapping[int, asyncio.Lock] = weakref.WeakValueDictionary()
|
||||||
|
|
||||||
|
def set_help_formatter(self, formatter: commands.help.HelpFormatterABC):
|
||||||
|
"""
|
||||||
|
Set's Red's help formatter.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
This method is provisional.
|
||||||
|
|
||||||
|
|
||||||
|
The formatter must implement all methods in
|
||||||
|
``commands.help.HelpFormatterABC``
|
||||||
|
|
||||||
|
Cogs which set a help formatter should inform users of this.
|
||||||
|
Users should not use multiple cogs which set a help formatter.
|
||||||
|
|
||||||
|
This should specifically be an instance of a formatter.
|
||||||
|
This allows cogs to pass a config object or similar to the
|
||||||
|
formatter prior to the bot using it.
|
||||||
|
|
||||||
|
See ``commands.help.HelpFormatterABC`` for more details.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
RuntimeError
|
||||||
|
If the default formatter has already been replaced
|
||||||
|
TypeError
|
||||||
|
If given an invalid formatter
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(formatter, commands.help.HelpFormatterABC):
|
||||||
|
raise TypeError(
|
||||||
|
"Help formatters must inherit from `commands.help.HelpFormatterABC` "
|
||||||
|
"and implement the required interfaces."
|
||||||
|
)
|
||||||
|
|
||||||
|
# do not switch to isinstance, we want to know that this has not been overriden,
|
||||||
|
# even with a subclass.
|
||||||
|
if type(self._help_formatter) is commands.help.RedHelpFormatter:
|
||||||
|
self._help_formatter = formatter
|
||||||
|
else:
|
||||||
|
raise RuntimeError("The formatter has already been overriden.")
|
||||||
|
|
||||||
|
def reset_help_formatter(self):
|
||||||
|
"""
|
||||||
|
Resets Red's help formatter.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
This method is provisional.
|
||||||
|
|
||||||
|
|
||||||
|
This exists for use in ``cog_unload`` for cogs which replace the formatter
|
||||||
|
as well as for a rescue command in core_commands.
|
||||||
|
|
||||||
|
"""
|
||||||
|
self._help_formatter = commands.help.RedHelpFormatter()
|
||||||
|
|
||||||
def get_command(self, name: str) -> Optional[commands.Command]:
|
def get_command(self, name: str) -> Optional[commands.Command]:
|
||||||
com = super().get_command(name)
|
com = super().get_command(name)
|
||||||
assert com is None or isinstance(com, commands.Command)
|
assert com is None or isinstance(com, commands.Command)
|
||||||
@ -786,12 +841,18 @@ class RedBase(
|
|||||||
return await super().start(*args, **kwargs)
|
return await super().start(*args, **kwargs)
|
||||||
|
|
||||||
async def send_help_for(
|
async def send_help_for(
|
||||||
self, ctx: commands.Context, help_for: Union[commands.Command, commands.GroupMixin, str]
|
self,
|
||||||
|
ctx: commands.Context,
|
||||||
|
help_for: Union[commands.Command, commands.GroupMixin, str],
|
||||||
|
*,
|
||||||
|
from_help_command: bool = False,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Invokes Red's helpformatter for a given context and object.
|
Invokes Red's helpformatter for a given context and object.
|
||||||
"""
|
"""
|
||||||
return await self._help_formatter.send_help(ctx, help_for)
|
return await self._help_formatter.send_help(
|
||||||
|
ctx, help_for, from_help_command=from_help_command
|
||||||
|
)
|
||||||
|
|
||||||
async def embed_requested(self, channel, user, command=None) -> bool:
|
async def embed_requested(self, channel, user, command=None) -> bool:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -1,13 +1,10 @@
|
|||||||
# Warning: The implementation below touches several private attributes.
|
# Warning: The implementation below touches several private attributes.
|
||||||
# While this implementation will be updated, and public interfaces maintained, derived classes
|
# While this implementation will be updated, and public interfaces maintained,
|
||||||
# should not assume these private attributes are version safe, and use the provided HelpSettings
|
# derived classes should not assume these private attributes are version safe,
|
||||||
# class for these settings.
|
# and use the provided HelpSettings class for these settings.
|
||||||
|
|
||||||
# This is a full replacement of discord.py's help command
|
# This is a full replacement of discord.py's help command
|
||||||
#
|
#
|
||||||
# At a later date, there should be things added to support extra formatter
|
|
||||||
# registration from 3rd party cogs.
|
|
||||||
#
|
|
||||||
# This exists due to deficiencies in discord.py which conflict
|
# This exists due to deficiencies in discord.py which conflict
|
||||||
# with our needs for per-context help settings
|
# with our needs for per-context help settings
|
||||||
# see https://github.com/Rapptz/discord.py/issues/2123
|
# see https://github.com/Rapptz/discord.py/issues/2123
|
||||||
@ -30,8 +27,7 @@
|
|||||||
# Additionally, this gives our users a bit more customization options including by
|
# Additionally, this gives our users a bit more customization options including by
|
||||||
# 3rd party cogs down the road.
|
# 3rd party cogs down the road.
|
||||||
|
|
||||||
# Note: 3rd party help must not remove the copyright notice
|
import abc
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
@ -48,7 +44,7 @@ from ..utils.mod import mass_purge
|
|||||||
from ..utils._internal_utils import fuzzy_command_search, format_fuzzy_results
|
from ..utils._internal_utils import fuzzy_command_search, format_fuzzy_results
|
||||||
from ..utils.chat_formatting import box, pagify
|
from ..utils.chat_formatting import box, pagify
|
||||||
|
|
||||||
__all__ = ["red_help", "RedHelpFormatter", "HelpSettings"]
|
__all__ = ["red_help", "RedHelpFormatter", "HelpSettings", "HelpFormatterABC"]
|
||||||
|
|
||||||
_ = Translator("Help", __file__)
|
_ = Translator("Help", __file__)
|
||||||
|
|
||||||
@ -65,6 +61,11 @@ EMPTY_STRING = "\N{ZERO WIDTH SPACE}"
|
|||||||
class HelpSettings:
|
class HelpSettings:
|
||||||
"""
|
"""
|
||||||
A representation of help settings.
|
A representation of help settings.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This class is provisional.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
page_char_limit: int = 1000
|
page_char_limit: int = 1000
|
||||||
@ -78,8 +79,10 @@ class HelpSettings:
|
|||||||
|
|
||||||
# Contrib Note: This is intentional to not accept the bot object
|
# Contrib Note: This is intentional to not accept the bot object
|
||||||
# There are plans to allow guild and user specific help settings
|
# There are plans to allow guild and user specific help settings
|
||||||
# Adding a non-context based method now would involve a breaking change later.
|
# Adding a non-context based method now would involve a breaking
|
||||||
# At a later date, more methods should be exposed for non-context based creation.
|
# change later.
|
||||||
|
# At a later date, more methods should be exposed for
|
||||||
|
# non-context based creation.
|
||||||
#
|
#
|
||||||
# This is also why we aren't just caching the
|
# This is also why we aren't just caching the
|
||||||
# current state of these settings on the bot object.
|
# current state of these settings on the bot object.
|
||||||
@ -102,23 +105,72 @@ class NoSubCommand(Exception):
|
|||||||
self.not_found = not_found
|
self.not_found = not_found
|
||||||
|
|
||||||
|
|
||||||
class RedHelpFormatter:
|
class HelpFormatterABC(abc.ABC):
|
||||||
|
"""
|
||||||
|
Describes the required interface of a help formatter.
|
||||||
|
|
||||||
|
Additional notes for 3rd party developers are included in this class.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
You may define __init__ however you want
|
||||||
|
(such as to include config),
|
||||||
|
Red will not initialize a formatter for you,
|
||||||
|
and must be passed an initialized formatter.
|
||||||
|
|
||||||
|
If you want to use Red's existing settings, use ``HelpSettings.from_context``
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This class is documented but provisional with expected changes.
|
||||||
|
|
||||||
|
In the future, this class will receive changes to support
|
||||||
|
invoking the help command without context.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
async def send_help(
|
||||||
|
self, ctx: Context, help_for: HelpTarget = None, *, from_help_command: bool = False
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
This is (currently) the only method you must implement.
|
||||||
|
|
||||||
|
This method should handle any and all errors which may arise.
|
||||||
|
|
||||||
|
The types subclasses must handle are defined as ``HelpTarget``
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class RedHelpFormatter(HelpFormatterABC):
|
||||||
"""
|
"""
|
||||||
Red's help implementation
|
Red's help implementation
|
||||||
|
|
||||||
This is intended to be overridable in parts to only change some behavior.
|
This is intended to be overridable in parts to only change some behavior.
|
||||||
|
|
||||||
While currently, there is a global formatter, later plans include a context specific
|
While this exists as a class for easy partial overriding,
|
||||||
formatter selector as well as an API for cogs to register/un-register a formatter with the bot.
|
most implementations should not need or want a shared state.
|
||||||
|
|
||||||
When implementing your own formatter, at minimum you must provide an implementation of
|
.. warning::
|
||||||
`send_help` with identical signature.
|
|
||||||
|
|
||||||
While this exists as a class for easy partial overriding, most implementations
|
This class is documented but may receive changes between
|
||||||
should not need or want a shared state.
|
versions without warning as needed.
|
||||||
|
The supported way to modify help is to write a separate formatter.
|
||||||
|
|
||||||
|
The primary reason for this class being documented is to allow
|
||||||
|
the opaque use of the class as a fallback, as any method in base
|
||||||
|
class which is intended for use will be present and implemented here.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This class may use various internal methods which are not safe to
|
||||||
|
use in third party code.
|
||||||
|
The internal methods used here may change,
|
||||||
|
with this class being updated at the same time.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
async def send_help(self, ctx: Context, help_for: HelpTarget = None):
|
async def send_help(
|
||||||
|
self, ctx: Context, help_for: HelpTarget = None, *, from_help_command: bool = False
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
This delegates to other functions.
|
This delegates to other functions.
|
||||||
|
|
||||||
@ -724,4 +776,4 @@ async def red_help(ctx: Context, *, thing_to_get_help_for: str = None):
|
|||||||
(Help) you know I need someone
|
(Help) you know I need someone
|
||||||
(Help!)
|
(Help!)
|
||||||
"""
|
"""
|
||||||
await ctx.bot.send_help_for(ctx, thing_to_get_help_for)
|
await ctx.bot.send_help_for(ctx, thing_to_get_help_for, from_help_command=True)
|
||||||
|
|||||||
@ -27,7 +27,6 @@ from redbot.core.data_manager import storage_type
|
|||||||
from . import (
|
from . import (
|
||||||
__version__,
|
__version__,
|
||||||
version_info as red_version_info,
|
version_info as red_version_info,
|
||||||
VersionInfo,
|
|
||||||
checks,
|
checks,
|
||||||
commands,
|
commands,
|
||||||
errors,
|
errors,
|
||||||
@ -2080,6 +2079,34 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
|||||||
"""Manage settings for the help command."""
|
"""Manage settings for the help command."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@helpset.command(name="resetformatter")
|
||||||
|
async def helpset_resetformatter(self, ctx: commands.Context):
|
||||||
|
""" This resets [botname]'s help formatter to the default formatter """
|
||||||
|
|
||||||
|
ctx.bot.reset_help_formatter()
|
||||||
|
await ctx.send(
|
||||||
|
_(
|
||||||
|
"The help formatter has been reset. "
|
||||||
|
"This will not prevent cogs from modifying help, "
|
||||||
|
"you may need to remove a cog if this has been an issue."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@helpset.command(name="resetsettings")
|
||||||
|
async def helpset_resetsettings(self, ctx: commands.Context):
|
||||||
|
"""
|
||||||
|
This resets [botname]'s help settings to their defaults.
|
||||||
|
|
||||||
|
This may not have an impact when using custom formatters from 3rd party cogs
|
||||||
|
"""
|
||||||
|
await ctx.bot._config.help.clear()
|
||||||
|
await ctx.send(
|
||||||
|
_(
|
||||||
|
"The help settings have been reset to their defaults. "
|
||||||
|
"This may not have an impact when using 3rd party help formatters."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@helpset.command(name="usemenus")
|
@helpset.command(name="usemenus")
|
||||||
async def helpset_usemenus(self, ctx: commands.Context, use_menus: bool = None):
|
async def helpset_usemenus(self, ctx: commands.Context, use_menus: bool = None):
|
||||||
"""
|
"""
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user