1132 lines
32 KiB
Python
1132 lines
32 KiB
Python
"""
|
|
The MIT License (MIT)
|
|
|
|
Copyright (c) 2015-present 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.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Optional, Any, TYPE_CHECKING, List, Callable, Type, Tuple, Union
|
|
|
|
from discord.errors import ClientException, DiscordException
|
|
|
|
if TYPE_CHECKING:
|
|
from inspect import Parameter
|
|
|
|
from .converter import Converter
|
|
from .context import Context
|
|
from .core import Command
|
|
from .cooldowns import Cooldown, BucketType
|
|
from .flags import Flag
|
|
from discord.abc import GuildChannel
|
|
from discord.threads import Thread
|
|
from discord.types.snowflake import Snowflake, SnowflakeList
|
|
|
|
|
|
__all__ = (
|
|
"CommandError",
|
|
"MissingRequiredArgument",
|
|
"BadArgument",
|
|
"PrivateMessageOnly",
|
|
"NoPrivateMessage",
|
|
"CheckFailure",
|
|
"CheckAnyFailure",
|
|
"CommandNotFound",
|
|
"DisabledCommand",
|
|
"CommandInvokeError",
|
|
"TooManyArguments",
|
|
"UserInputError",
|
|
"CommandOnCooldown",
|
|
"MaxConcurrencyReached",
|
|
"NotOwner",
|
|
"MessageNotFound",
|
|
"ObjectNotFound",
|
|
"MemberNotFound",
|
|
"GuildNotFound",
|
|
"UserNotFound",
|
|
"ChannelNotFound",
|
|
"ThreadNotFound",
|
|
"ChannelNotReadable",
|
|
"BadColourArgument",
|
|
"BadColorArgument",
|
|
"RoleNotFound",
|
|
"BadInviteArgument",
|
|
"EmojiNotFound",
|
|
"GuildStickerNotFound",
|
|
"PartialEmojiConversionFailure",
|
|
"BadBoolArgument",
|
|
"MissingRole",
|
|
"BotMissingRole",
|
|
"MissingAnyRole",
|
|
"BotMissingAnyRole",
|
|
"MissingPermissions",
|
|
"BotMissingPermissions",
|
|
"NSFWChannelRequired",
|
|
"ConversionError",
|
|
"BadUnionArgument",
|
|
"BadLiteralArgument",
|
|
"ArgumentParsingError",
|
|
"UnexpectedQuoteError",
|
|
"InvalidEndOfQuotedStringError",
|
|
"ExpectedClosingQuoteError",
|
|
"ExtensionError",
|
|
"ExtensionAlreadyLoaded",
|
|
"ExtensionNotLoaded",
|
|
"NoEntryPointError",
|
|
"ExtensionFailed",
|
|
"ExtensionNotFound",
|
|
"CommandRegistrationError",
|
|
"ApplicationCommandRegistrationError",
|
|
"FlagError",
|
|
"BadFlagArgument",
|
|
"MissingFlagArgument",
|
|
"TooManyFlags",
|
|
"MissingRequiredFlag",
|
|
)
|
|
|
|
|
|
class CommandError(DiscordException):
|
|
r"""The base exception type for all command related errors.
|
|
|
|
This inherits from :exc:`discord.DiscordException`.
|
|
|
|
This exception and exceptions inherited from it are handled
|
|
in a special way as they are caught and passed into a special event
|
|
from :class:`.Bot`\, :func:`.on_command_error`.
|
|
"""
|
|
|
|
def __init__(self, message: Optional[str] = None, *args: Any) -> None:
|
|
if message is not None:
|
|
# clean-up @everyone and @here mentions
|
|
m = message.replace("@everyone", "@\u200beveryone").replace("@here", "@\u200bhere")
|
|
super().__init__(m, *args)
|
|
else:
|
|
super().__init__(*args)
|
|
|
|
|
|
class ConversionError(CommandError):
|
|
"""Exception raised when a Converter class raises non-CommandError.
|
|
|
|
This inherits from :exc:`CommandError`.
|
|
|
|
Attributes
|
|
----------
|
|
converter: :class:`discord.ext.commands.Converter`
|
|
The converter that failed.
|
|
original: :exc:`Exception`
|
|
The original exception that was raised. You can also get this via
|
|
the ``__cause__`` attribute.
|
|
"""
|
|
|
|
def __init__(self, converter: Converter, original: Exception) -> None:
|
|
self.converter: Converter = converter
|
|
self.original: Exception = original
|
|
|
|
|
|
class UserInputError(CommandError):
|
|
"""The base exception type for errors that involve errors
|
|
regarding user input.
|
|
|
|
This inherits from :exc:`CommandError`.
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
class CommandNotFound(CommandError):
|
|
"""Exception raised when a command is attempted to be invoked
|
|
but no command under that name is found.
|
|
|
|
This is not raised for invalid subcommands, rather just the
|
|
initial main command that is attempted to be invoked.
|
|
|
|
This inherits from :exc:`CommandError`.
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
class MissingRequiredArgument(UserInputError):
|
|
"""Exception raised when parsing a command and a parameter
|
|
that is required is not encountered.
|
|
|
|
This inherits from :exc:`UserInputError`
|
|
|
|
Attributes
|
|
-----------
|
|
param: :class:`inspect.Parameter`
|
|
The argument that is missing.
|
|
"""
|
|
|
|
def __init__(self, param: Parameter) -> None:
|
|
self.param: Parameter = param
|
|
super().__init__(f"{param.name} is a required argument that is missing.")
|
|
|
|
|
|
class TooManyArguments(UserInputError):
|
|
"""Exception raised when the command was passed too many arguments and its
|
|
:attr:`.Command.ignore_extra` attribute was not set to ``True``.
|
|
|
|
This inherits from :exc:`UserInputError`
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
class BadArgument(UserInputError):
|
|
"""Exception raised when a parsing or conversion failure is encountered
|
|
on an argument to pass into a command.
|
|
|
|
This inherits from :exc:`UserInputError`
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
class CheckFailure(CommandError):
|
|
"""Exception raised when the predicates in :attr:`.Command.checks` have failed.
|
|
|
|
This inherits from :exc:`CommandError`
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
class CheckAnyFailure(CheckFailure):
|
|
"""Exception raised when all predicates in :func:`check_any` fail.
|
|
|
|
This inherits from :exc:`CheckFailure`.
|
|
|
|
.. versionadded:: 1.3
|
|
|
|
Attributes
|
|
------------
|
|
errors: List[:class:`CheckFailure`]
|
|
A list of errors that were caught during execution.
|
|
checks: List[Callable[[:class:`Context`], :class:`bool`]]
|
|
A list of check predicates that failed.
|
|
"""
|
|
|
|
def __init__(self, checks: List[CheckFailure], errors: List[Callable[[Context], bool]]) -> None:
|
|
self.checks: List[CheckFailure] = checks
|
|
self.errors: List[Callable[[Context], bool]] = errors
|
|
super().__init__("You do not have permission to run this command.")
|
|
|
|
|
|
class PrivateMessageOnly(CheckFailure):
|
|
"""Exception raised when an operation does not work outside of private
|
|
message contexts.
|
|
|
|
This inherits from :exc:`CheckFailure`
|
|
"""
|
|
|
|
def __init__(self, message: Optional[str] = None) -> None:
|
|
super().__init__(message or "This command can only be used in private messages.")
|
|
|
|
|
|
class NoPrivateMessage(CheckFailure):
|
|
"""Exception raised when an operation does not work in private message
|
|
contexts.
|
|
|
|
This inherits from :exc:`CheckFailure`
|
|
"""
|
|
|
|
def __init__(self, message: Optional[str] = None) -> None:
|
|
super().__init__(message or "This command cannot be used in private messages.")
|
|
|
|
|
|
class NotOwner(CheckFailure):
|
|
"""Exception raised when the message author is not the owner of the bot.
|
|
|
|
This inherits from :exc:`CheckFailure`
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
class ObjectNotFound(BadArgument):
|
|
"""Exception raised when the argument provided did not match the format
|
|
of an ID or a mention.
|
|
|
|
This inherits from :exc:`BadArgument`
|
|
|
|
.. versionadded:: 2.0
|
|
|
|
Attributes
|
|
-----------
|
|
argument: :class:`str`
|
|
The argument supplied by the caller that was not matched
|
|
"""
|
|
|
|
def __init__(self, argument: str) -> None:
|
|
self.argument: str = argument
|
|
super().__init__(f"{argument!r} does not follow a valid ID or mention format.")
|
|
|
|
|
|
class MemberNotFound(BadArgument):
|
|
"""Exception raised when the member provided was not found in the bot's
|
|
cache.
|
|
|
|
This inherits from :exc:`BadArgument`
|
|
|
|
.. versionadded:: 1.5
|
|
|
|
Attributes
|
|
-----------
|
|
argument: :class:`str`
|
|
The member supplied by the caller that was not found
|
|
"""
|
|
|
|
def __init__(self, argument: str) -> None:
|
|
self.argument: str = argument
|
|
super().__init__(f'Member "{argument}" not found.')
|
|
|
|
|
|
class GuildNotFound(BadArgument):
|
|
"""Exception raised when the guild provided was not found in the bot's cache.
|
|
|
|
This inherits from :exc:`BadArgument`
|
|
|
|
.. versionadded:: 1.7
|
|
|
|
Attributes
|
|
-----------
|
|
argument: :class:`str`
|
|
The guild supplied by the called that was not found
|
|
"""
|
|
|
|
def __init__(self, argument: str) -> None:
|
|
self.argument: str = argument
|
|
super().__init__(f'Guild "{argument}" not found.')
|
|
|
|
|
|
class UserNotFound(BadArgument):
|
|
"""Exception raised when the user provided was not found in the bot's
|
|
cache.
|
|
|
|
This inherits from :exc:`BadArgument`
|
|
|
|
.. versionadded:: 1.5
|
|
|
|
Attributes
|
|
-----------
|
|
argument: :class:`str`
|
|
The user supplied by the caller that was not found
|
|
"""
|
|
|
|
def __init__(self, argument: str) -> None:
|
|
self.argument: str = argument
|
|
super().__init__(f'User "{argument}" not found.')
|
|
|
|
|
|
class MessageNotFound(BadArgument):
|
|
"""Exception raised when the message provided was not found in the channel.
|
|
|
|
This inherits from :exc:`BadArgument`
|
|
|
|
.. versionadded:: 1.5
|
|
|
|
Attributes
|
|
-----------
|
|
argument: :class:`str`
|
|
The message supplied by the caller that was not found
|
|
"""
|
|
|
|
def __init__(self, argument: str) -> None:
|
|
self.argument: str = argument
|
|
super().__init__(f'Message "{argument}" not found.')
|
|
|
|
|
|
class ChannelNotReadable(BadArgument):
|
|
"""Exception raised when the bot does not have permission to read messages
|
|
in the channel.
|
|
|
|
This inherits from :exc:`BadArgument`
|
|
|
|
.. versionadded:: 1.5
|
|
|
|
Attributes
|
|
-----------
|
|
argument: Union[:class:`.abc.GuildChannel`, :class:`.Thread`]
|
|
The channel supplied by the caller that was not readable
|
|
"""
|
|
|
|
def __init__(self, argument: Union[GuildChannel, Thread]) -> None:
|
|
self.argument: Union[GuildChannel, Thread] = argument
|
|
super().__init__(f"Can't read messages in {argument.mention}.")
|
|
|
|
|
|
class ChannelNotFound(BadArgument):
|
|
"""Exception raised when the bot can not find the channel.
|
|
|
|
This inherits from :exc:`BadArgument`
|
|
|
|
.. versionadded:: 1.5
|
|
|
|
Attributes
|
|
-----------
|
|
argument: :class:`str`
|
|
The channel supplied by the caller that was not found
|
|
"""
|
|
|
|
def __init__(self, argument: str) -> None:
|
|
self.argument: str = argument
|
|
super().__init__(f'Channel "{argument}" not found.')
|
|
|
|
|
|
class ThreadNotFound(BadArgument):
|
|
"""Exception raised when the bot can not find the thread.
|
|
|
|
This inherits from :exc:`BadArgument`
|
|
|
|
.. versionadded:: 2.0
|
|
|
|
Attributes
|
|
-----------
|
|
argument: :class:`str`
|
|
The thread supplied by the caller that was not found
|
|
"""
|
|
|
|
def __init__(self, argument: str) -> None:
|
|
self.argument: str = argument
|
|
super().__init__(f'Thread "{argument}" not found.')
|
|
|
|
|
|
class BadColourArgument(BadArgument):
|
|
"""Exception raised when the colour is not valid.
|
|
|
|
This inherits from :exc:`BadArgument`
|
|
|
|
.. versionadded:: 1.5
|
|
|
|
Attributes
|
|
-----------
|
|
argument: :class:`str`
|
|
The colour supplied by the caller that was not valid
|
|
"""
|
|
|
|
def __init__(self, argument: str) -> None:
|
|
self.argument: str = argument
|
|
super().__init__(f'Colour "{argument}" is invalid.')
|
|
|
|
|
|
BadColorArgument = BadColourArgument
|
|
|
|
|
|
class RoleNotFound(BadArgument):
|
|
"""Exception raised when the bot can not find the role.
|
|
|
|
This inherits from :exc:`BadArgument`
|
|
|
|
.. versionadded:: 1.5
|
|
|
|
Attributes
|
|
-----------
|
|
argument: :class:`str`
|
|
The role supplied by the caller that was not found
|
|
"""
|
|
|
|
def __init__(self, argument: str) -> None:
|
|
self.argument: str = argument
|
|
super().__init__(f'Role "{argument}" not found.')
|
|
|
|
|
|
class BadInviteArgument(BadArgument):
|
|
"""Exception raised when the invite is invalid or expired.
|
|
|
|
This inherits from :exc:`BadArgument`
|
|
|
|
.. versionadded:: 1.5
|
|
|
|
Attributes
|
|
-----------
|
|
argument: :class:`str`
|
|
The invite supplied by the caller that was not found
|
|
"""
|
|
|
|
def __init__(self, argument: str) -> None:
|
|
self.argument: str = argument
|
|
super().__init__(f'Invite "{argument}" is invalid or expired.')
|
|
|
|
|
|
class EmojiNotFound(BadArgument):
|
|
"""Exception raised when the bot can not find the emoji.
|
|
|
|
This inherits from :exc:`BadArgument`
|
|
|
|
.. versionadded:: 1.5
|
|
|
|
Attributes
|
|
-----------
|
|
argument: :class:`str`
|
|
The emoji supplied by the caller that was not found
|
|
"""
|
|
|
|
def __init__(self, argument: str) -> None:
|
|
self.argument: str = argument
|
|
super().__init__(f'Emoji "{argument}" not found.')
|
|
|
|
|
|
class PartialEmojiConversionFailure(BadArgument):
|
|
"""Exception raised when the emoji provided does not match the correct
|
|
format.
|
|
|
|
This inherits from :exc:`BadArgument`
|
|
|
|
.. versionadded:: 1.5
|
|
|
|
Attributes
|
|
-----------
|
|
argument: :class:`str`
|
|
The emoji supplied by the caller that did not match the regex
|
|
"""
|
|
|
|
def __init__(self, argument: str) -> None:
|
|
self.argument: str = argument
|
|
super().__init__(f'Couldn\'t convert "{argument}" to PartialEmoji.')
|
|
|
|
|
|
class GuildStickerNotFound(BadArgument):
|
|
"""Exception raised when the bot can not find the sticker.
|
|
|
|
This inherits from :exc:`BadArgument`
|
|
|
|
.. versionadded:: 2.0
|
|
|
|
Attributes
|
|
-----------
|
|
argument: :class:`str`
|
|
The sticker supplied by the caller that was not found
|
|
"""
|
|
|
|
def __init__(self, argument: str) -> None:
|
|
self.argument: str = argument
|
|
super().__init__(f'Sticker "{argument}" not found.')
|
|
|
|
|
|
class BadBoolArgument(BadArgument):
|
|
"""Exception raised when a boolean argument was not convertable.
|
|
|
|
This inherits from :exc:`BadArgument`
|
|
|
|
.. versionadded:: 1.5
|
|
|
|
Attributes
|
|
-----------
|
|
argument: :class:`str`
|
|
The boolean argument supplied by the caller that is not in the predefined list
|
|
"""
|
|
|
|
def __init__(self, argument: str) -> None:
|
|
self.argument: str = argument
|
|
super().__init__(f"{argument} is not a recognised boolean option")
|
|
|
|
|
|
class DisabledCommand(CommandError):
|
|
"""Exception raised when the command being invoked is disabled.
|
|
|
|
This inherits from :exc:`CommandError`
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
class CommandInvokeError(CommandError):
|
|
"""Exception raised when the command being invoked raised an exception.
|
|
|
|
This inherits from :exc:`CommandError`
|
|
|
|
Attributes
|
|
-----------
|
|
original: :exc:`Exception`
|
|
The original exception that was raised. You can also get this via
|
|
the ``__cause__`` attribute.
|
|
"""
|
|
|
|
def __init__(self, e: Exception) -> None:
|
|
self.original: Exception = e
|
|
super().__init__(f"Command raised an exception: {e.__class__.__name__}: {e}")
|
|
|
|
|
|
class CommandOnCooldown(CommandError):
|
|
"""Exception raised when the command being invoked is on cooldown.
|
|
|
|
This inherits from :exc:`CommandError`
|
|
|
|
Attributes
|
|
-----------
|
|
cooldown: :class:`.Cooldown`
|
|
A class with attributes ``rate`` and ``per`` similar to the
|
|
:func:`.cooldown` decorator.
|
|
type: :class:`BucketType`
|
|
The type associated with the cooldown.
|
|
retry_after: :class:`float`
|
|
The amount of seconds to wait before you can retry again.
|
|
"""
|
|
|
|
def __init__(self, cooldown: Cooldown, retry_after: float, type: BucketType) -> None:
|
|
self.cooldown: Cooldown = cooldown
|
|
self.retry_after: float = retry_after
|
|
self.type: BucketType = type
|
|
super().__init__(f"You are on cooldown. Try again in {retry_after:.2f}s")
|
|
|
|
|
|
class MaxConcurrencyReached(CommandError):
|
|
"""Exception raised when the command being invoked has reached its maximum concurrency.
|
|
|
|
This inherits from :exc:`CommandError`.
|
|
|
|
Attributes
|
|
------------
|
|
number: :class:`int`
|
|
The maximum number of concurrent invokers allowed.
|
|
per: :class:`.BucketType`
|
|
The bucket type passed to the :func:`.max_concurrency` decorator.
|
|
"""
|
|
|
|
def __init__(self, number: int, per: BucketType) -> None:
|
|
self.number: int = number
|
|
self.per: BucketType = per
|
|
name = per.name
|
|
suffix = "per %s" % name if per.name != "default" else "globally"
|
|
plural = "%s times %s" if number > 1 else "%s time %s"
|
|
fmt = plural % (number, suffix)
|
|
super().__init__(f"Too many people are using this command. It can only be used {fmt} concurrently.")
|
|
|
|
|
|
class MissingRole(CheckFailure):
|
|
"""Exception raised when the command invoker lacks a role to run a command.
|
|
|
|
This inherits from :exc:`CheckFailure`
|
|
|
|
.. versionadded:: 1.1
|
|
|
|
Attributes
|
|
-----------
|
|
missing_role: Union[:class:`str`, :class:`int`]
|
|
The required role that is missing.
|
|
This is the parameter passed to :func:`~.commands.has_role`.
|
|
"""
|
|
|
|
def __init__(self, missing_role: Snowflake) -> None:
|
|
self.missing_role: Snowflake = missing_role
|
|
message = f"Role {missing_role!r} is required to run this command."
|
|
super().__init__(message)
|
|
|
|
|
|
class BotMissingRole(CheckFailure):
|
|
"""Exception raised when the bot's member lacks a role to run a command.
|
|
|
|
This inherits from :exc:`CheckFailure`
|
|
|
|
.. versionadded:: 1.1
|
|
|
|
Attributes
|
|
-----------
|
|
missing_role: Union[:class:`str`, :class:`int`]
|
|
The required role that is missing.
|
|
This is the parameter passed to :func:`~.commands.has_role`.
|
|
"""
|
|
|
|
def __init__(self, missing_role: Snowflake) -> None:
|
|
self.missing_role: Snowflake = missing_role
|
|
message = f"Bot requires the role {missing_role!r} to run this command"
|
|
super().__init__(message)
|
|
|
|
|
|
class MissingAnyRole(CheckFailure):
|
|
"""Exception raised when the command invoker lacks any of
|
|
the roles specified to run a command.
|
|
|
|
This inherits from :exc:`CheckFailure`
|
|
|
|
.. versionadded:: 1.1
|
|
|
|
Attributes
|
|
-----------
|
|
missing_roles: List[Union[:class:`str`, :class:`int`]]
|
|
The roles that the invoker is missing.
|
|
These are the parameters passed to :func:`~.commands.has_any_role`.
|
|
"""
|
|
|
|
def __init__(self, missing_roles: SnowflakeList) -> None:
|
|
self.missing_roles: SnowflakeList = missing_roles
|
|
|
|
missing = [f"'{role}'" for role in missing_roles]
|
|
|
|
if len(missing) > 2:
|
|
fmt = "{}, or {}".format(", ".join(missing[:-1]), missing[-1])
|
|
else:
|
|
fmt = " or ".join(missing)
|
|
|
|
message = f"You are missing at least one of the required roles: {fmt}"
|
|
super().__init__(message)
|
|
|
|
|
|
class BotMissingAnyRole(CheckFailure):
|
|
"""Exception raised when the bot's member lacks any of
|
|
the roles specified to run a command.
|
|
|
|
This inherits from :exc:`CheckFailure`
|
|
|
|
.. versionadded:: 1.1
|
|
|
|
Attributes
|
|
-----------
|
|
missing_roles: List[Union[:class:`str`, :class:`int`]]
|
|
The roles that the bot's member is missing.
|
|
These are the parameters passed to :func:`~.commands.has_any_role`.
|
|
|
|
"""
|
|
|
|
def __init__(self, missing_roles: SnowflakeList) -> None:
|
|
self.missing_roles: SnowflakeList = missing_roles
|
|
|
|
missing = [f"'{role}'" for role in missing_roles]
|
|
|
|
if len(missing) > 2:
|
|
fmt = "{}, or {}".format(", ".join(missing[:-1]), missing[-1])
|
|
else:
|
|
fmt = " or ".join(missing)
|
|
|
|
message = f"Bot is missing at least one of the required roles: {fmt}"
|
|
super().__init__(message)
|
|
|
|
|
|
class NSFWChannelRequired(CheckFailure):
|
|
"""Exception raised when a channel does not have the required NSFW setting.
|
|
|
|
This inherits from :exc:`CheckFailure`.
|
|
|
|
.. versionadded:: 1.1
|
|
|
|
Parameters
|
|
-----------
|
|
channel: Union[:class:`.abc.GuildChannel`, :class:`.Thread`]
|
|
The channel that does not have NSFW enabled.
|
|
"""
|
|
|
|
def __init__(self, channel: Union[GuildChannel, Thread]) -> None:
|
|
self.channel: Union[GuildChannel, Thread] = channel
|
|
super().__init__(f"Channel '{channel}' needs to be NSFW for this command to work.")
|
|
|
|
|
|
class MissingPermissions(CheckFailure):
|
|
"""Exception raised when the command invoker lacks permissions to run a
|
|
command.
|
|
|
|
This inherits from :exc:`CheckFailure`
|
|
|
|
Attributes
|
|
-----------
|
|
missing_permissions: List[:class:`str`]
|
|
The required permissions that are missing.
|
|
"""
|
|
|
|
def __init__(self, missing_permissions: List[str], *args: Any) -> None:
|
|
self.missing_permissions: List[str] = missing_permissions
|
|
|
|
missing = [perm.replace("_", " ").replace("guild", "server").title() for perm in missing_permissions]
|
|
|
|
if len(missing) > 2:
|
|
fmt = "{}, and {}".format(", ".join(missing[:-1]), missing[-1])
|
|
else:
|
|
fmt = " and ".join(missing)
|
|
message = f"You are missing {fmt} permission(s) to run this command."
|
|
super().__init__(message, *args)
|
|
|
|
|
|
class BotMissingPermissions(CheckFailure):
|
|
"""Exception raised when the bot's member lacks permissions to run a
|
|
command.
|
|
|
|
This inherits from :exc:`CheckFailure`
|
|
|
|
Attributes
|
|
-----------
|
|
missing_permissions: List[:class:`str`]
|
|
The required permissions that are missing.
|
|
"""
|
|
|
|
def __init__(self, missing_permissions: List[str], *args: Any) -> None:
|
|
self.missing_permissions: List[str] = missing_permissions
|
|
|
|
missing = [perm.replace("_", " ").replace("guild", "server").title() for perm in missing_permissions]
|
|
|
|
if len(missing) > 2:
|
|
fmt = "{}, and {}".format(", ".join(missing[:-1]), missing[-1])
|
|
else:
|
|
fmt = " and ".join(missing)
|
|
message = f"Bot requires {fmt} permission(s) to run this command."
|
|
super().__init__(message, *args)
|
|
|
|
|
|
class BadUnionArgument(UserInputError):
|
|
"""Exception raised when a :data:`typing.Union` converter fails for all
|
|
its associated types.
|
|
|
|
This inherits from :exc:`UserInputError`
|
|
|
|
Attributes
|
|
-----------
|
|
param: :class:`inspect.Parameter`
|
|
The parameter that failed being converted.
|
|
converters: Tuple[Type, ``...``]
|
|
A tuple of converters attempted in conversion, in order of failure.
|
|
errors: List[:class:`CommandError`]
|
|
A list of errors that were caught from failing the conversion.
|
|
"""
|
|
|
|
def __init__(self, param: Parameter, converters: Tuple[Type, ...], errors: List[CommandError]) -> None:
|
|
self.param: Parameter = param
|
|
self.converters: Tuple[Type, ...] = converters
|
|
self.errors: List[CommandError] = errors
|
|
|
|
def _get_name(x):
|
|
try:
|
|
return x.__name__
|
|
except AttributeError:
|
|
if hasattr(x, "__origin__"):
|
|
return repr(x)
|
|
return x.__class__.__name__
|
|
|
|
to_string = [_get_name(x) for x in converters]
|
|
if len(to_string) > 2:
|
|
fmt = "{}, or {}".format(", ".join(to_string[:-1]), to_string[-1])
|
|
else:
|
|
fmt = " or ".join(to_string)
|
|
|
|
super().__init__(f'Could not convert "{param.name}" into {fmt}.')
|
|
|
|
|
|
class BadLiteralArgument(UserInputError):
|
|
"""Exception raised when a :data:`typing.Literal` converter fails for all
|
|
its associated values.
|
|
|
|
This inherits from :exc:`UserInputError`
|
|
|
|
.. versionadded:: 2.0
|
|
|
|
Attributes
|
|
-----------
|
|
param: :class:`inspect.Parameter`
|
|
The parameter that failed being converted.
|
|
literals: Tuple[Any, ``...``]
|
|
A tuple of values compared against in conversion, in order of failure.
|
|
errors: List[:class:`CommandError`]
|
|
A list of errors that were caught from failing the conversion.
|
|
"""
|
|
|
|
def __init__(self, param: Parameter, literals: Tuple[Any, ...], errors: List[CommandError]) -> None:
|
|
self.param: Parameter = param
|
|
self.literals: Tuple[Any, ...] = literals
|
|
self.errors: List[CommandError] = errors
|
|
|
|
to_string = [repr(l) for l in literals]
|
|
if len(to_string) > 2:
|
|
fmt = "{}, or {}".format(", ".join(to_string[:-1]), to_string[-1])
|
|
else:
|
|
fmt = " or ".join(to_string)
|
|
|
|
super().__init__(f'Could not convert "{param.name}" into the literal {fmt}.')
|
|
|
|
|
|
class ArgumentParsingError(UserInputError):
|
|
"""An exception raised when the parser fails to parse a user's input.
|
|
|
|
This inherits from :exc:`UserInputError`.
|
|
|
|
There are child classes that implement more granular parsing errors for
|
|
i18n purposes.
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
class UnexpectedQuoteError(ArgumentParsingError):
|
|
"""An exception raised when the parser encounters a quote mark inside a non-quoted string.
|
|
|
|
This inherits from :exc:`ArgumentParsingError`.
|
|
|
|
Attributes
|
|
------------
|
|
quote: :class:`str`
|
|
The quote mark that was found inside the non-quoted string.
|
|
"""
|
|
|
|
def __init__(self, quote: str) -> None:
|
|
self.quote: str = quote
|
|
super().__init__(f"Unexpected quote mark, {quote!r}, in non-quoted string")
|
|
|
|
|
|
class InvalidEndOfQuotedStringError(ArgumentParsingError):
|
|
"""An exception raised when a space is expected after the closing quote in a string
|
|
but a different character is found.
|
|
|
|
This inherits from :exc:`ArgumentParsingError`.
|
|
|
|
Attributes
|
|
-----------
|
|
char: :class:`str`
|
|
The character found instead of the expected string.
|
|
"""
|
|
|
|
def __init__(self, char: str) -> None:
|
|
self.char: str = char
|
|
super().__init__(f"Expected space after closing quotation but received {char!r}")
|
|
|
|
|
|
class ExpectedClosingQuoteError(ArgumentParsingError):
|
|
"""An exception raised when a quote character is expected but not found.
|
|
|
|
This inherits from :exc:`ArgumentParsingError`.
|
|
|
|
Attributes
|
|
-----------
|
|
close_quote: :class:`str`
|
|
The quote character expected.
|
|
"""
|
|
|
|
def __init__(self, close_quote: str) -> None:
|
|
self.close_quote: str = close_quote
|
|
super().__init__(f"Expected closing {close_quote}.")
|
|
|
|
|
|
class ExtensionError(DiscordException):
|
|
"""Base exception for extension related errors.
|
|
|
|
This inherits from :exc:`~discord.DiscordException`.
|
|
|
|
Attributes
|
|
------------
|
|
name: :class:`str`
|
|
The extension that had an error.
|
|
"""
|
|
|
|
def __init__(self, message: Optional[str] = None, *args: Any, name: str) -> None:
|
|
self.name: str = name
|
|
message = message or f"Extension {name!r} had an error."
|
|
# clean-up @everyone and @here mentions
|
|
m = message.replace("@everyone", "@\u200beveryone").replace("@here", "@\u200bhere")
|
|
super().__init__(m, *args)
|
|
|
|
|
|
class ExtensionAlreadyLoaded(ExtensionError):
|
|
"""An exception raised when an extension has already been loaded.
|
|
|
|
This inherits from :exc:`ExtensionError`
|
|
"""
|
|
|
|
def __init__(self, name: str) -> None:
|
|
super().__init__(f"Extension {name!r} is already loaded.", name=name)
|
|
|
|
|
|
class ExtensionNotLoaded(ExtensionError):
|
|
"""An exception raised when an extension was not loaded.
|
|
|
|
This inherits from :exc:`ExtensionError`
|
|
"""
|
|
|
|
def __init__(self, name: str) -> None:
|
|
super().__init__(f"Extension {name!r} has not been loaded.", name=name)
|
|
|
|
|
|
class NoEntryPointError(ExtensionError):
|
|
"""An exception raised when an extension does not have a ``setup`` entry point function.
|
|
|
|
This inherits from :exc:`ExtensionError`
|
|
"""
|
|
|
|
def __init__(self, name: str) -> None:
|
|
super().__init__(f"Extension {name!r} has no 'setup' function.", name=name)
|
|
|
|
|
|
class ExtensionFailed(ExtensionError):
|
|
"""An exception raised when an extension failed to load during execution of the module or ``setup`` entry point.
|
|
|
|
This inherits from :exc:`ExtensionError`
|
|
|
|
Attributes
|
|
-----------
|
|
name: :class:`str`
|
|
The extension that had the error.
|
|
original: :exc:`Exception`
|
|
The original exception that was raised. You can also get this via
|
|
the ``__cause__`` attribute.
|
|
"""
|
|
|
|
def __init__(self, name: str, original: Exception) -> None:
|
|
self.original: Exception = original
|
|
msg = f"Extension {name!r} raised an error: {original.__class__.__name__}: {original}"
|
|
super().__init__(msg, name=name)
|
|
|
|
|
|
class ExtensionNotFound(ExtensionError):
|
|
"""An exception raised when an extension is not found.
|
|
|
|
This inherits from :exc:`ExtensionError`
|
|
|
|
.. versionchanged:: 1.3
|
|
Made the ``original`` attribute always None.
|
|
|
|
Attributes
|
|
-----------
|
|
name: :class:`str`
|
|
The extension that had the error.
|
|
"""
|
|
|
|
def __init__(self, name: str) -> None:
|
|
msg = f"Extension {name!r} could not be loaded."
|
|
super().__init__(msg, name=name)
|
|
|
|
|
|
class CommandRegistrationError(ClientException):
|
|
"""An exception raised when the command can't be added
|
|
because the name is already taken by a different command.
|
|
|
|
This inherits from :exc:`discord.ClientException`
|
|
|
|
.. versionadded:: 1.4
|
|
|
|
Attributes
|
|
----------
|
|
name: :class:`str`
|
|
The command name that had the error.
|
|
alias_conflict: :class:`bool`
|
|
Whether the name that conflicts is an alias of the command we try to add.
|
|
"""
|
|
|
|
def __init__(self, name: str, *, alias_conflict: bool = False) -> None:
|
|
self.name: str = name
|
|
self.alias_conflict: bool = alias_conflict
|
|
type_ = "alias" if alias_conflict else "command"
|
|
super().__init__(f"The {type_} {name} is already an existing command or alias.")
|
|
|
|
|
|
class ApplicationCommandRegistrationError(ClientException):
|
|
"""An exception raised when a command cannot be converted to an
|
|
application command.
|
|
|
|
This inherits from :exc:`discord.ClientException`
|
|
|
|
.. versionadded:: 2.0
|
|
|
|
Attributes
|
|
----------
|
|
command: :class:`Command`
|
|
The command that failed to be converted.
|
|
"""
|
|
|
|
def __init__(self, command: Command, msg: str = None) -> None:
|
|
self.command = command
|
|
super().__init__(msg or f"{command.qualified_name} failed to converted to an application command.")
|
|
|
|
|
|
class FlagError(BadArgument):
|
|
"""The base exception type for all flag parsing related errors.
|
|
|
|
This inherits from :exc:`BadArgument`.
|
|
|
|
.. versionadded:: 2.0
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
class TooManyFlags(FlagError):
|
|
"""An exception raised when a flag has received too many values.
|
|
|
|
This inherits from :exc:`FlagError`.
|
|
|
|
.. versionadded:: 2.0
|
|
|
|
Attributes
|
|
------------
|
|
flag: :class:`~discord.ext.commands.Flag`
|
|
The flag that received too many values.
|
|
values: List[:class:`str`]
|
|
The values that were passed.
|
|
"""
|
|
|
|
def __init__(self, flag: Flag, values: List[str]) -> None:
|
|
self.flag: Flag = flag
|
|
self.values: List[str] = values
|
|
super().__init__(f"Too many flag values, expected {flag.max_args} but received {len(values)}.")
|
|
|
|
|
|
class BadFlagArgument(FlagError):
|
|
"""An exception raised when a flag failed to convert a value.
|
|
|
|
This inherits from :exc:`FlagError`
|
|
|
|
.. versionadded:: 2.0
|
|
|
|
Attributes
|
|
-----------
|
|
flag: :class:`~discord.ext.commands.Flag`
|
|
The flag that failed to convert.
|
|
"""
|
|
|
|
def __init__(self, flag: Flag) -> None:
|
|
self.flag: Flag = flag
|
|
try:
|
|
name = flag.annotation.__name__
|
|
except AttributeError:
|
|
name = flag.annotation.__class__.__name__
|
|
|
|
super().__init__(f"Could not convert to {name!r} for flag {flag.name!r}")
|
|
|
|
|
|
class MissingRequiredFlag(FlagError):
|
|
"""An exception raised when a required flag was not given.
|
|
|
|
This inherits from :exc:`FlagError`
|
|
|
|
.. versionadded:: 2.0
|
|
|
|
Attributes
|
|
-----------
|
|
flag: :class:`~discord.ext.commands.Flag`
|
|
The required flag that was not found.
|
|
"""
|
|
|
|
def __init__(self, flag: Flag) -> None:
|
|
self.flag: Flag = flag
|
|
super().__init__(f"Flag {flag.name!r} is required and missing")
|
|
|
|
|
|
class MissingFlagArgument(FlagError):
|
|
"""An exception raised when a flag did not get a value.
|
|
|
|
This inherits from :exc:`FlagError`
|
|
|
|
.. versionadded:: 2.0
|
|
|
|
Attributes
|
|
-----------
|
|
flag: :class:`~discord.ext.commands.Flag`
|
|
The flag that did not get a value.
|
|
"""
|
|
|
|
def __init__(self, flag: Flag) -> None:
|
|
self.flag: Flag = flag
|
|
super().__init__(f"Flag {flag.name!r} does not have an argument")
|