mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 03:08:55 -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:
|
||||
|
||||
.. 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()
|
||||
|
||||
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]:
|
||||
com = super().get_command(name)
|
||||
assert com is None or isinstance(com, commands.Command)
|
||||
@ -786,12 +841,18 @@ class RedBase(
|
||||
return await super().start(*args, **kwargs)
|
||||
|
||||
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.
|
||||
"""
|
||||
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:
|
||||
"""
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
# Warning: The implementation below touches several private attributes.
|
||||
# While this implementation will be updated, and public interfaces maintained, derived classes
|
||||
# should not assume these private attributes are version safe, and use the provided HelpSettings
|
||||
# class for these settings.
|
||||
# While this implementation will be updated, and public interfaces maintained,
|
||||
# derived classes should not assume these private attributes are version safe,
|
||||
# and use the provided HelpSettings class for these settings.
|
||||
|
||||
# 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
|
||||
# with our needs for per-context help settings
|
||||
# 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
|
||||
# 3rd party cogs down the road.
|
||||
|
||||
# Note: 3rd party help must not remove the copyright notice
|
||||
|
||||
import abc
|
||||
import asyncio
|
||||
from collections import namedtuple
|
||||
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.chat_formatting import box, pagify
|
||||
|
||||
__all__ = ["red_help", "RedHelpFormatter", "HelpSettings"]
|
||||
__all__ = ["red_help", "RedHelpFormatter", "HelpSettings", "HelpFormatterABC"]
|
||||
|
||||
_ = Translator("Help", __file__)
|
||||
|
||||
@ -65,6 +61,11 @@ EMPTY_STRING = "\N{ZERO WIDTH SPACE}"
|
||||
class HelpSettings:
|
||||
"""
|
||||
A representation of help settings.
|
||||
|
||||
.. warning::
|
||||
|
||||
This class is provisional.
|
||||
|
||||
"""
|
||||
|
||||
page_char_limit: int = 1000
|
||||
@ -78,8 +79,10 @@ class HelpSettings:
|
||||
|
||||
# Contrib Note: This is intentional to not accept the bot object
|
||||
# There are plans to allow guild and user specific help settings
|
||||
# Adding a non-context based method now would involve a breaking change later.
|
||||
# At a later date, more methods should be exposed for non-context based creation.
|
||||
# Adding a non-context based method now would involve a breaking
|
||||
# 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
|
||||
# current state of these settings on the bot object.
|
||||
@ -102,23 +105,72 @@ class NoSubCommand(Exception):
|
||||
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
|
||||
|
||||
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
|
||||
formatter selector as well as an API for cogs to register/un-register a formatter with the bot.
|
||||
While this exists as a class for easy partial overriding,
|
||||
most implementations should not need or want a shared state.
|
||||
|
||||
When implementing your own formatter, at minimum you must provide an implementation of
|
||||
`send_help` with identical signature.
|
||||
.. warning::
|
||||
|
||||
While this exists as a class for easy partial overriding, most implementations
|
||||
should not need or want a shared state.
|
||||
This class is documented but may receive changes between
|
||||
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.
|
||||
|
||||
@ -724,4 +776,4 @@ async def red_help(ctx: Context, *, thing_to_get_help_for: str = None):
|
||||
(Help) you know I need someone
|
||||
(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 (
|
||||
__version__,
|
||||
version_info as red_version_info,
|
||||
VersionInfo,
|
||||
checks,
|
||||
commands,
|
||||
errors,
|
||||
@ -2080,6 +2079,34 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
"""Manage settings for the help command."""
|
||||
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")
|
||||
async def helpset_usemenus(self, ctx: commands.Context, use_menus: bool = None):
|
||||
"""
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user