Add CommandConverter and CogConverter + add usage in Core (#5037)

* add commands, cog converter

* properly use type_checking

* make core commands use command converter

* update commands to use cogconverter

* fix undefined variable name, style

* Update redbot/core/commands/converter.py

Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com>

* Update redbot/core/commands/converter.py

Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com>

* Update redbot/core/core_commands.py

Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com>

* Update redbot/core/core_commands.py

Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com>

* Update redbot/core/core_commands.py

Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com>

* Update redbot/core/core_commands.py

Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com>

* Update redbot/core/core_commands.py

Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com>

* update cog argument names

* update documentation arg names

* update more docs

* This new Sphinx is annoying about this...

* I'm questioning my skills

* Fix name error in `[p]embedset showsettings` when command is not given

* Do not use the new cog converter in `[p]command enablecog`

This is needed so that a cog that isn't loaded but was disabled
can be enabled back.

Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com>
This commit is contained in:
PhenoM4n4n 2021-09-07 14:43:29 -07:00 committed by GitHub
parent 63fd7cc95f
commit e5b236fb1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 95 additions and 115 deletions

View File

@ -383,7 +383,7 @@ command defaultdisablecog
.. code-block:: none
[p]command defaultdisablecog <cogname>
[p]command defaultdisablecog <cog>
**Description**
@ -400,7 +400,7 @@ To override it, use ``[p]command enablecog`` on the servers you want to allow us
- ``[p]command defaultdisablecog ModLog``
**Arguments:**
- ``<cogname>`` - The name of the cog to make disabled by default. Must be title-case.
- ``<cog>`` - The name of the cog to make disabled by default. Must be title-case.
.. _core-command-command-defaultenablecog:
@ -414,7 +414,7 @@ command defaultenablecog
.. code-block:: none
[p]command defaultenablecog <cogname>
[p]command defaultenablecog <cog>
**Description**
@ -431,7 +431,7 @@ To override it, use ``[p]command disablecog`` on the servers you want to disallo
- ``[p]command defaultenablecog ModLog``
**Arguments:**
- ``<cogname>`` - The name of the cog to make enabled by default. Must be title-case.
- ``<cog>`` - The name of the cog to make enabled by default. Must be title-case.
.. _core-command-command-disable:
@ -519,7 +519,7 @@ command disablecog
.. code-block:: none
[p]command disablecog <cogname>
[p]command disablecog <cog>
**Description**
@ -533,7 +533,7 @@ Disable a cog in this server.
- ``[p]command disablecog ModLog``
**Arguments:**
- ``<cogname>`` - The name of the cog to disable on this server. Must be title-case.
- ``<cog>`` - The name of the cog to disable on this server. Must be title-case.
.. _core-command-command-disabledmsg:
@ -651,7 +651,7 @@ command enablecog
.. code-block:: none
[p]command enablecog <cogname>
[p]command enablecog <cog>
**Description**
@ -665,7 +665,7 @@ Enable a cog in this server.
- ``[p]command enablecog ModLog``
**Arguments:**
- ``<cogname>`` - The name of the cog to enable on this server. Must be title-case.
- ``<cog>`` - The name of the cog to enable on this server. Must be title-case.
.. _core-command-command-listdisabled:
@ -902,7 +902,7 @@ embedset command
.. code-block:: none
[p]embedset command <command_name> [enabled]
[p]embedset command <command> [enabled]
**Description**
@ -935,7 +935,7 @@ embedset command global
.. code-block:: none
[p]embedset command global <command_name> [enabled]
[p]embedset command global <command> [enabled]
**Description**
@ -965,7 +965,7 @@ embedset command server
.. code-block:: none
[p]embedset command server <command_name> [enabled]
[p]embedset command server <command> [enabled]
.. tip:: Alias: ``embedset command guild``
@ -1057,7 +1057,7 @@ embedset showsettings
.. code-block:: none
[p]embedset showsettings [command_name]
[p]embedset showsettings [command]
**Description**
@ -1071,7 +1071,7 @@ Provide a command name to check for command specific embed settings.
- ``[p]embedset showsettings "ignore list"`` - Checking subcommands requires quotes.
**Arguments:**
- ``[command_name]`` - Checks this command for command specific embed settings.
- ``[command]`` - Checks this command for command specific embed settings.
.. _core-command-embedset-user:

View File

@ -30,6 +30,8 @@ from .converter import (
NoParseOptional as NoParseOptional,
UserInputOptional as UserInputOptional,
Literal as Literal,
CogConverter as CogConverter,
CommandConverter as CommandConverter,
)
from .errors import (
ConversionFailure as ConversionFailure,

View File

@ -45,6 +45,8 @@ __all__ = [
"parse_relativedelta",
"parse_timedelta",
"Literal",
"CommandConverter",
"CogConverter",
]
_ = Translator("commands.converter", __file__)
@ -526,3 +528,29 @@ if not TYPE_CHECKING:
return cls(k)
else:
return cls((k,))
if TYPE_CHECKING:
CommandConverter = dpy_commands.Command
CogConverter = dpy_commands.Cog
else:
class CommandConverter(dpy_commands.Converter):
"""Converts a command name to the matching `redbot.core.commands.Command` object."""
async def convert(self, ctx: "Context", argument: str):
arg = argument.strip()
command = ctx.bot.get_command(arg)
if not command:
raise BadArgument(_('Command "{arg}" not found.').format(arg=arg))
return command
class CogConverter(dpy_commands.Converter):
"""Converts a cog name to the matching `redbot.core.commands.Cog` object."""
async def convert(self, ctx: "Context", argument: str):
arg = argument.strip()
cog = ctx.bot.get_cog(arg)
if not cog:
raise BadArgument(_('Cog "{arg}" not found.').format(arg=arg))
return cog

View File

@ -49,6 +49,7 @@ from .utils.chat_formatting import (
inline,
pagify,
)
from .commands import CommandConverter, CogConverter
from .commands.requires import PrivilegeLevel
_entities = {
@ -1139,7 +1140,9 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
"""
@embedset.command(name="showsettings")
async def embedset_showsettings(self, ctx: commands.Context, command_name: str = None) -> None:
async def embedset_showsettings(
self, ctx: commands.Context, command: CommandConverter = None
) -> None:
"""
Show the current embed settings.
@ -1151,17 +1154,10 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
- `[p]embedset showsettings "ignore list"` - Checking subcommands requires quotes.
**Arguments:**
- `[command_name]` - Checks this command for command specific embed settings.
- `[command]` - Checks this command for command specific embed settings.
"""
if command_name is not None:
command_obj: Optional[commands.Command] = ctx.bot.get_command(command_name)
if command_obj is None:
await ctx.send(
_("I couldn't find that command. Please note that it is case sensitive.")
)
return
# qualified name might be different if alias was passed to this command
command_name = command_obj.qualified_name
command_name = command and command.qualified_name
text = _("Embed settings:\n\n")
global_default = await self.bot._config.embeds()
@ -1251,7 +1247,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
@checks.guildowner_or_permissions(administrator=True)
@embedset.group(name="command", invoke_without_command=True)
async def embedset_command(
self, ctx: commands.Context, command_name: str, enabled: bool = None
self, ctx: commands.Context, command: CommandConverter, enabled: bool = None
) -> None:
"""
Sets a command's embed setting.
@ -1273,9 +1269,9 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
"""
# Select the scope based on the author's privileges
if await ctx.bot.is_owner(ctx.author):
await self.embedset_command_global(ctx, command_name, enabled)
await self.embedset_command_global(ctx, command, enabled)
else:
await self.embedset_command_guild(ctx, command_name, enabled)
await self.embedset_command_guild(ctx, command, enabled)
def _check_if_command_requires_embed_links(self, command_obj: commands.Command) -> None:
for command in itertools.chain((command_obj,), command_obj.parents):
@ -1291,7 +1287,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
@commands.is_owner()
@embedset_command.command(name="global")
async def embedset_command_global(
self, ctx: commands.Context, command_name: str, enabled: bool = None
self, ctx: commands.Context, command: CommandConverter, enabled: bool = None
):
"""
Sets a command's embed setting globally.
@ -1310,15 +1306,9 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
**Arguments:**
- `[enabled]` - Whether to use embeds for this command. Leave blank to reset to default.
"""
command_obj: Optional[commands.Command] = ctx.bot.get_command(command_name)
if command_obj is None:
await ctx.send(
_("I couldn't find that command. Please note that it is case sensitive.")
)
return
self._check_if_command_requires_embed_links(command_obj)
self._check_if_command_requires_embed_links(command)
# qualified name might be different if alias was passed to this command
command_name = command_obj.qualified_name
command_name = command.qualified_name
if enabled is None:
await self.bot._config.custom("COMMAND", command_name, 0).embeds.clear()
@ -1342,7 +1332,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
@commands.guild_only()
@embedset_command.command(name="server", aliases=["guild"])
async def embedset_command_guild(
self, ctx: commands.GuildContext, command_name: str, enabled: bool = None
self, ctx: commands.GuildContext, command: CommandConverter, enabled: bool = None
):
"""
Sets a commmand's embed setting for the current server.
@ -1361,15 +1351,9 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
**Arguments:**
- `[enabled]` - Whether to use embeds for this command. Leave blank to reset to default.
"""
command_obj: Optional[commands.Command] = ctx.bot.get_command(command_name)
if command_obj is None:
await ctx.send(
_("I couldn't find that command. Please note that it is case sensitive.")
)
return
self._check_if_command_requires_embed_links(command_obj)
self._check_if_command_requires_embed_links(command)
# qualified name might be different if alias was passed to this command
command_name = command_obj.qualified_name
command_name = command.qualified_name
if enabled is None:
await self.bot._config.custom("COMMAND", command_name, ctx.guild.id).embeds.clear()
@ -4159,7 +4143,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
@checks.is_owner()
@command_manager.command(name="defaultdisablecog")
async def command_default_disable_cog(self, ctx: commands.Context, *, cogname: str):
async def command_default_disable_cog(self, ctx: commands.Context, *, cog: CogConverter):
"""Set the default state for a cog as disabled.
This will disable the cog for all servers by default.
@ -4172,11 +4156,9 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
- `[p]command defaultdisablecog ModLog`
**Arguments:**
- `<cogname>` - The name of the cog to make disabled by default. Must be title-case.
- `<cog>` - The name of the cog to make disabled by default. Must be title-case.
"""
cog = self.bot.get_cog(cogname)
if not cog:
return await ctx.send(_("Cog with the given name doesn't exist."))
cogname = cog.qualified_name
if isinstance(cog, commands.commands._RuleDropper):
return await ctx.send(_("You can't disable this cog by default."))
await self.bot._disabled_cog_cache.default_disable(cogname)
@ -4184,7 +4166,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
@checks.is_owner()
@command_manager.command(name="defaultenablecog")
async def command_default_enable_cog(self, ctx: commands.Context, *, cogname: str):
async def command_default_enable_cog(self, ctx: commands.Context, *, cog: CogConverter):
"""Set the default state for a cog as enabled.
This will re-enable the cog for all servers by default.
@ -4197,17 +4179,15 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
- `[p]command defaultenablecog ModLog`
**Arguments:**
- `<cogname>` - The name of the cog to make enabled by default. Must be title-case.
- `<cog>` - The name of the cog to make enabled by default. Must be title-case.
"""
cog = self.bot.get_cog(cogname)
if not cog:
return await ctx.send(_("Cog with the given name doesn't exist."))
cogname = cog.qualified_name
await self.bot._disabled_cog_cache.default_enable(cogname)
await ctx.send(_("{cogname} has been set as enabled by default.").format(cogname=cogname))
@commands.guild_only()
@command_manager.command(name="disablecog")
async def command_disable_cog(self, ctx: commands.Context, *, cogname: str):
async def command_disable_cog(self, ctx: commands.Context, *, cog: CogConverter):
"""Disable a cog in this server.
Note: This will only work on loaded cogs, and must reference the title-case cog name.
@ -4217,11 +4197,9 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
- `[p]command disablecog ModLog`
**Arguments:**
- `<cogname>` - The name of the cog to disable on this server. Must be title-case.
- `<cog>` - The name of the cog to disable on this server. Must be title-case.
"""
cog = self.bot.get_cog(cogname)
if not cog:
return await ctx.send(_("Cog with the given name doesn't exist."))
cogname = cog.qualified_name
if isinstance(cog, commands.commands._RuleDropper):
return await ctx.send(_("You can't disable this cog as you would lock yourself out."))
if await self.bot._disabled_cog_cache.disable_cog_in_guild(cogname, ctx.guild.id):
@ -4232,7 +4210,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
)
@commands.guild_only()
@command_manager.command(name="enablecog")
@command_manager.command(name="enablecog", usage="<cog>")
async def command_enable_cog(self, ctx: commands.Context, *, cogname: str):
"""Enable a cog in this server.
@ -4243,7 +4221,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
- `[p]command enablecog ModLog`
**Arguments:**
- `<cogname>` - The name of the cog to enable on this server. Must be title-case.
- `<cog>` - The name of the cog to enable on this server. Must be title-case.
"""
if await self.bot._disabled_cog_cache.enable_cog_in_guild(cogname, ctx.guild.id):
await ctx.send(_("{cogname} has been enabled in this guild.").format(cogname=cogname))
@ -4251,7 +4229,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
# putting this here allows enabling a cog that isn't loaded but was disabled.
cog = self.bot.get_cog(cogname)
if not cog:
return await ctx.send(_("Cog with the given name doesn't exist."))
return await ctx.send(_('Cog "{arg}" not found.').format(arg=cogname))
await ctx.send(
_("{cogname} was not disabled (nothing to do).").format(cogname=cogname)
@ -4342,7 +4320,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
await ctx.send_interactive(paged)
@command_manager.group(name="disable", invoke_without_command=True)
async def command_disable(self, ctx: commands.Context, *, command: str):
async def command_disable(self, ctx: commands.Context, *, command: CommandConverter):
"""
Disable a command.
@ -4364,7 +4342,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
@checks.is_owner()
@command_disable.command(name="global")
async def command_disable_global(self, ctx: commands.Context, *, command: str):
async def command_disable_global(self, ctx: commands.Context, *, command: CommandConverter):
"""
Disable a command globally.
@ -4375,39 +4353,32 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
**Arguments:**
- `<command>` - The command to disable globally.
"""
command_obj: Optional[commands.Command] = ctx.bot.get_command(command)
if command_obj is None:
await ctx.send(
_("I couldn't find that command. Please note that it is case sensitive.")
)
return
if self.command_manager in command_obj.parents or self.command_manager == command_obj:
if self.command_manager in command.parents or self.command_manager == command:
await ctx.send(
_("The command to disable cannot be `command` or any of its subcommands.")
)
return
if isinstance(command_obj, commands.commands._RuleDropper):
if isinstance(command, commands.commands._RuleDropper):
await ctx.send(
_("This command is designated as being always available and cannot be disabled.")
)
return
async with ctx.bot._config.disabled_commands() as disabled_commands:
if command not in disabled_commands:
disabled_commands.append(command_obj.qualified_name)
if command.qualified_name not in disabled_commands:
disabled_commands.append(command.qualified_name)
if not command_obj.enabled:
if not command.enabled:
await ctx.send(_("That command is already disabled globally."))
return
command_obj.enabled = False
command.enabled = False
await ctx.tick()
@commands.guild_only()
@command_disable.command(name="server", aliases=["guild"])
async def command_disable_guild(self, ctx: commands.Context, *, command: str):
async def command_disable_guild(self, ctx: commands.Context, *, command: CommandConverter):
"""
Disable a command in this server only.
@ -4418,34 +4389,27 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
**Arguments:**
- `<command>` - The command to disable for the current server.
"""
command_obj: Optional[commands.Command] = ctx.bot.get_command(command)
if command_obj is None:
await ctx.send(
_("I couldn't find that command. Please note that it is case sensitive.")
)
return
if self.command_manager in command_obj.parents or self.command_manager == command_obj:
if self.command_manager in command.parents or self.command_manager == command:
await ctx.send(
_("The command to disable cannot be `command` or any of its subcommands.")
)
return
if isinstance(command_obj, commands.commands._RuleDropper):
if isinstance(command, commands.commands._RuleDropper):
await ctx.send(
_("This command is designated as being always available and cannot be disabled.")
)
return
if command_obj.requires.privilege_level > await PrivilegeLevel.from_ctx(ctx):
if command.requires.privilege_level > await PrivilegeLevel.from_ctx(ctx):
await ctx.send(_("You are not allowed to disable that command."))
return
async with ctx.bot._config.guild(ctx.guild).disabled_commands() as disabled_commands:
if command not in disabled_commands:
disabled_commands.append(command_obj.qualified_name)
if command.qualified_name not in disabled_commands:
disabled_commands.append(command.qualified_name)
done = command_obj.disable_in(ctx.guild)
done = command.disable_in(ctx.guild)
if not done:
await ctx.send(_("That command is already disabled in this server."))
@ -4453,7 +4417,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
await ctx.tick()
@command_manager.group(name="enable", invoke_without_command=True)
async def command_enable(self, ctx: commands.Context, *, command: str):
async def command_enable(self, ctx: commands.Context, *, command: CommandConverter):
"""Enable a command.
If you're the bot owner, this will try to enable a globally disabled command by default.
@ -4473,7 +4437,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
@commands.is_owner()
@command_enable.command(name="global")
async def command_enable_global(self, ctx: commands.Context, *, command: str):
async def command_enable_global(self, ctx: commands.Context, *, command: CommandConverter):
"""
Enable a command globally.
@ -4484,27 +4448,20 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
**Arguments:**
- `<command>` - The command to enable globally.
"""
command_obj: Optional[commands.Command] = ctx.bot.get_command(command)
if command_obj is None:
await ctx.send(
_("I couldn't find that command. Please note that it is case sensitive.")
)
return
async with ctx.bot._config.disabled_commands() as disabled_commands:
with contextlib.suppress(ValueError):
disabled_commands.remove(command_obj.qualified_name)
disabled_commands.remove(command.qualified_name)
if command_obj.enabled:
if command.enabled:
await ctx.send(_("That command is already enabled globally."))
return
command_obj.enabled = True
command.enabled = True
await ctx.tick()
@commands.guild_only()
@command_enable.command(name="server", aliases=["guild"])
async def command_enable_guild(self, ctx: commands.Context, *, command: str):
async def command_enable_guild(self, ctx: commands.Context, *, command: CommandConverter):
"""
Enable a command in this server.
@ -4515,22 +4472,15 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
**Arguments:**
- `<command>` - The command to enable for the current server.
"""
command_obj: Optional[commands.Command] = ctx.bot.get_command(command)
if command_obj is None:
await ctx.send(
_("I couldn't find that command. Please note that it is case sensitive.")
)
return
if command_obj.requires.privilege_level > await PrivilegeLevel.from_ctx(ctx):
if command.requires.privilege_level > await PrivilegeLevel.from_ctx(ctx):
await ctx.send(_("You are not allowed to enable that command."))
return
async with ctx.bot._config.guild(ctx.guild).disabled_commands() as disabled_commands:
with contextlib.suppress(ValueError):
disabled_commands.remove(command_obj.qualified_name)
disabled_commands.remove(command.qualified_name)
done = command_obj.enable_in(ctx.guild)
done = command.enable_in(ctx.guild)
if not done:
await ctx.send(_("That command is already enabled in this server."))