diff --git a/discord/app_commands/errors.py b/discord/app_commands/errors.py index be5c5f601..a053113c6 100644 --- a/discord/app_commands/errors.py +++ b/discord/app_commands/errors.py @@ -38,6 +38,7 @@ __all__ = ( 'CommandAlreadyRegistered', 'CommandSignatureMismatch', 'CommandNotFound', + 'MaxCommandsReached', 'NoPrivateMessage', 'MissingRole', 'MissingAnyRole', @@ -332,6 +333,39 @@ class CommandNotFound(AppCommandError): super().__init__(f'Application command {name!r} not found') +class MaxCommandsReached(AppCommandError): + """An exception raised when the maximum number of application commands was reached + either globally or in a guild. + + This inherits from :exc:`~discord.app_commands.AppCommandError`. + + .. versionadded:: 2.0 + + Attributes + ------------ + type: :class:`~discord.AppCommandType` + The type of command that reached the limit. + guild_id: Optional[:class:`int`] + The guild ID that reached the limit or ``None`` if it was global. + limit: :class:`int` + The limit that was hit. + """ + + def __init__(self, guild_id: Optional[int], limit: int, type: AppCommandType = AppCommandType.chat_input): + self.guild_id: Optional[int] = guild_id + self.limit: int = limit + self.type: AppCommandType = type + + lookup = { + AppCommandType.chat_input: 'slash commands', + AppCommandType.message: 'message context menu commands', + AppCommandType.user: 'user context menu commands', + } + desc = lookup.get(type, 'application commands') + ns = 'globally' if self.guild_id is None else f'for guild ID {self.guild_id}' + super().__init__(f'maximum number of {desc} exceeded {limit} {ns}') + + class CommandSignatureMismatch(AppCommandError): """An exception raised when an application command from Discord has a different signature from the one provided in the code. This happens because your command definition differs diff --git a/discord/app_commands/tree.py b/discord/app_commands/tree.py index e5ba210fa..206956a8b 100644 --- a/discord/app_commands/tree.py +++ b/discord/app_commands/tree.py @@ -55,6 +55,7 @@ from .errors import ( CommandAlreadyRegistered, CommandNotFound, CommandSignatureMismatch, + MaxCommandsReached, ) from ..errors import ClientException from ..enums import AppCommandType, InteractionType @@ -194,7 +195,7 @@ class CommandTree(Generic[ClientT]): Raises -------- - ValueError + MaxCommandsReached The maximum number of commands was reached for that guild. This is currently 100 for slash commands and 5 for context menu commands. """ @@ -206,7 +207,7 @@ class CommandTree(Generic[ClientT]): mapping.update(self._global_commands) if len(mapping) > 100: - raise ValueError('maximum number of slash commands exceeded (100)') + raise MaxCommandsReached(guild_id=guild.id, limit=100) ctx_menu: Dict[Tuple[str, Optional[int], int], ContextMenu] = { (name, guild.id, cmd_type): cmd @@ -218,7 +219,7 @@ class CommandTree(Generic[ClientT]): for cmd_type, count in counter.items(): if count > 5: as_enum = AppCommandType(cmd_type) - raise ValueError(f'maximum number of context menu commands exceeded (5) for type {as_enum!s}') + raise MaxCommandsReached(guild_id=guild.id, limit=5, type=as_enum) self._context_menus.update(ctx_menu) self._guild_commands[guild.id] = mapping @@ -261,7 +262,7 @@ class CommandTree(Generic[ClientT]): TypeError The application command passed is not a valid application command. Or, ``guild`` and ``guilds`` were both given. - ValueError + MaxCommandsReached The maximum number of commands was reached globally or for that guild. This is currently 100 for slash commands and 5 for context menu commands. """ @@ -284,7 +285,7 @@ class CommandTree(Generic[ClientT]): total = sum(1 for _, g, t in self._context_menus if g == guild_id and t == type) if total + found > 5: - raise ValueError('maximum number of context menu commands exceeded (5)') + raise MaxCommandsReached(guild_id=guild_id, limit=5, type=AppCommandType(type)) data[key] = command if guild_ids is None: @@ -315,7 +316,7 @@ class CommandTree(Generic[ClientT]): if found and not override: raise CommandAlreadyRegistered(name, guild_id) if len(commands) + found > 100: - raise ValueError(f'maximum number of slash commands exceeded (100) for guild_id {guild_id}') + raise MaxCommandsReached(guild_id=guild_id, limit=100) # Actually add the command now that it has been verified to be okay. for guild_id in guild_ids: @@ -326,7 +327,7 @@ class CommandTree(Generic[ClientT]): if found and not override: raise CommandAlreadyRegistered(name, None) if len(self._global_commands) + found > 100: - raise ValueError('maximum number of global slash commands exceeded (100)') + raise MaxCommandsReached(guild_id=None, limit=100) self._global_commands[name] = root @overload diff --git a/docs/interactions/api.rst b/docs/interactions/api.rst index 74c577dae..1fb74500c 100644 --- a/docs/interactions/api.rst +++ b/docs/interactions/api.rst @@ -575,6 +575,9 @@ Exceptions .. autoexception:: discord.app_commands.CommandOnCooldown :members: +.. autoexception:: discord.app_commands.MaxCommandsReached + :members: + .. autoexception:: discord.app_commands.CommandAlreadyRegistered :members: @@ -600,6 +603,7 @@ Exception Hierarchy - :exc:`~discord.app_commands.MissingPermissions` - :exc:`~discord.app_commands.BotMissingPermissions` - :exc:`~discord.app_commands.CommandOnCooldown` + - :exc:`~discord.app_commands.MaxCommandsReached` - :exc:`~discord.app_commands.CommandAlreadyRegistered` - :exc:`~discord.app_commands.CommandSignatureMismatch` - :exc:`~discord.app_commands.CommandNotFound`