aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVexs <[email protected]>2019-04-10 02:31:55 -0500
committerRapptz <[email protected]>2019-04-20 16:45:37 -0400
commitbb3ebc0ebc26b1275364388f13b85f204917a55e (patch)
tree2ab4074479b1dd3474542b67f2a346f48470e98c
parentMake Message.ack an actual coroutine function. (diff)
downloaddiscord.py-bb3ebc0ebc26b1275364388f13b85f204917a55e.tar.xz
discord.py-bb3ebc0ebc26b1275364388f13b85f204917a55e.zip
[commands] Add custom exception classes for built-in checks
Added: * MissingRole * BotMissingRole * MissingAnyRole * BotMissingAnyRole
-rw-r--r--discord/ext/commands/core.py132
-rw-r--r--discord/ext/commands/errors.py132
-rw-r--r--docs/ext/commands/api.rst24
3 files changed, 241 insertions, 47 deletions
diff --git a/discord/ext/commands/core.py b/discord/ext/commands/core.py
index 5d5601b2..c70ade1e 100644
--- a/discord/ext/commands/core.py
+++ b/discord/ext/commands/core.py
@@ -1302,6 +1302,15 @@ def has_role(item):
If the message is invoked in a private message context then the check will
return ``False``.
+ This check raises one of two special exceptions, :exc:`.MissingRole` if the user
+ is missing a role, or :exc:`.NoPrivateMessage` if it is used in a private message.
+ Both inherit from :exc:`.CheckFailure`.
+
+ .. versionchanged:: 1.1.0
+
+ Raise :exc:`.MissingRole` or :exc:`.NoPrivateMessage`
+ instead of generic :exc:`.CheckFailure`
+
Parameters
-----------
item: Union[:class:`int`, :class:`str`]
@@ -1310,13 +1319,15 @@ def has_role(item):
def predicate(ctx):
if not isinstance(ctx.channel, discord.abc.GuildChannel):
- return False
+ raise NoPrivateMessage()
if isinstance(item, int):
role = discord.utils.get(ctx.author.roles, id=item)
else:
role = discord.utils.get(ctx.author.roles, name=item)
- return role is not None
+ if role is None:
+ raise MissingRole(item)
+ return True
return check(predicate)
@@ -1327,6 +1338,15 @@ def has_any_role(*items):
Similar to :func:`.has_role`\, the names or IDs passed in must be exact.
+ This check raises one of two special exceptions, :exc:`.MissingAnyRole` if the user
+ is missing all roles, or :exc:`.NoPrivateMessage` if it is used in a private message.
+ Both inherit from :exc:`.CheckFailure`.
+
+ .. versionchanged:: 1.1.0
+
+ Raise :exc:`.MissingAnyRole` or :exc:`.NoPrivateMessage`
+ instead of generic :exc:`.CheckFailure`
+
Parameters
-----------
items: List[Union[:class:`str`, :class:`int`]]
@@ -1344,10 +1364,67 @@ def has_any_role(*items):
"""
def predicate(ctx):
if not isinstance(ctx.channel, discord.abc.GuildChannel):
- return False
+ raise NoPrivateMessage()
getter = functools.partial(discord.utils.get, ctx.author.roles)
- return any(getter(id=item) is not None if isinstance(item, int) else getter(name=item) is not None for item in items)
+ if any(getter(id=item) is not None if isinstance(item, int) else getter(name=item) is not None for item in items):
+ return True
+ raise MissingAnyRole(items)
+
+ return check(predicate)
+
+def bot_has_role(item):
+ """Similar to :func:`.has_role` except checks if the bot itself has the
+ role.
+
+ This check raises one of two special exceptions, :exc:`.BotMissingRole` if the bot
+ is missing the role, or :exc:`.NoPrivateMessage` if it is used in a private message.
+ Both inherit from :exc:`.CheckFailure`.
+
+ .. versionchanged:: 1.1.0
+
+ Raise :exc:`.BotMissingRole` or :exc:`.NoPrivateMessage`
+ instead of generic :exc:`.CheckFailure`
+ """
+
+ def predicate(ctx):
+ ch = ctx.channel
+ if not isinstance(ch, discord.abc.GuildChannel):
+ raise NoPrivateMessage()
+
+ me = ch.guild.me
+ if isinstance(item, int):
+ role = discord.utils.get(me.roles, id=item)
+ else:
+ role = discord.utils.get(me.roles, name=item)
+ if role is None:
+ raise BotMissingRole(item)
+ return True
+ return check(predicate)
+
+def bot_has_any_role(*items):
+ """Similar to :func:`.has_any_role` except checks if the bot itself has
+ any of the roles listed.
+
+ This check raises one of two special exceptions, :exc:`.BotMissingAnyRole` if the bot
+ is missing all roles, or :exc:`.NoPrivateMessage` if it is used in a private message.
+ Both inherit from :exc:`.CheckFailure`.
+
+ .. versionchanged:: 1.1.0
+
+ Raise :exc:`.BotMissingAnyRole` or :exc:`.NoPrivateMessage`
+ instead of generic checkfailure
+ """
+ def predicate(ctx):
+ ch = ctx.channel
+ if not isinstance(ch, discord.abc.GuildChannel):
+ raise NoPrivateMessage()
+
+ me = ch.guild.me
+ getter = functools.partial(discord.utils.get, me.roles)
+ if any(getter(id=item) is not None if isinstance(item, int) else getter(name=item) is not None for item in items):
+ return True
+ raise BotMissingAnyRole(items)
return check(predicate)
def has_permissions(**perms):
@@ -1389,36 +1466,6 @@ def has_permissions(**perms):
return check(predicate)
-def bot_has_role(item):
- """Similar to :func:`.has_role` except checks if the bot itself has the
- role.
- """
-
- def predicate(ctx):
- ch = ctx.channel
- if not isinstance(ch, discord.abc.GuildChannel):
- return False
- me = ch.guild.me
- if isinstance(item, int):
- role = discord.utils.get(me.roles, id=item)
- else:
- role = discord.utils.get(me.roles, name=item)
- return role is not None
- return check(predicate)
-
-def bot_has_any_role(*items):
- """Similar to :func:`.has_any_role` except checks if the bot itself has
- any of the roles listed.
- """
- def predicate(ctx):
- ch = ctx.channel
- if not isinstance(ch, discord.abc.GuildChannel):
- return False
- me = ch.guild.me
- getter = functools.partial(discord.utils.get, me.roles)
- return any(getter(id=item) is not None if isinstance(item, int) else getter(name=item) is not None for item in items)
- return check(predicate)
-
def bot_has_permissions(**perms):
"""Similar to :func:`.has_permissions` except checks if the bot itself has
the permissions listed.
@@ -1469,7 +1516,7 @@ def guild_only():
def predicate(ctx):
if ctx.guild is None:
- raise NoPrivateMessage('This command cannot be used in private messages.')
+ raise NoPrivateMessage()
return True
return check(predicate)
@@ -1492,9 +1539,20 @@ def is_owner():
return check(predicate)
def is_nsfw():
- """A :func:`.check` that checks if the channel is a NSFW channel."""
+ """A :func:`.check` that checks if the channel is a NSFW channel.
+
+ This check raises a special exception, :exc:`.NSFWChannelRequired`
+ that is derived from :exc:`.CheckFailure`.
+
+ .. versionchanged:: 1.1.0
+
+ Raise :exc:`.NSFWChannelRequired instead of generic :exc:`.CheckFailure`.`
+ """
def pred(ctx):
- return isinstance(ctx.channel, discord.TextChannel) and ctx.channel.is_nsfw()
+ ch = ctx.channel
+ if isinstance(ch, discord.TextChannel) and ch.is_nsfw():
+ return True
+ raise NSFWChannelRequired(ch)
return check(pred)
def cooldown(rate, per, type=BucketType.default):
diff --git a/discord/ext/commands/errors.py b/discord/ext/commands/errors.py
index b98014e3..760daf6b 100644
--- a/discord/ext/commands/errors.py
+++ b/discord/ext/commands/errors.py
@@ -29,11 +29,12 @@ from discord.errors import DiscordException
__all__ = ['CommandError', 'MissingRequiredArgument', 'BadArgument',
'PrivateMessageOnly', 'NoPrivateMessage', 'CheckFailure',
- 'CommandNotFound' ,'DisabledCommand', 'CommandInvokeError',
- 'TooManyArguments', 'UserInputError', 'CommandOnCooldown',
- 'NotOwner', 'MissingPermissions', 'BotMissingPermissions',
- 'ConversionError', 'BadUnionArgument', 'ArgumentParsingError',
- 'UnexpectedQuoteError', 'InvalidEndOfQuotedStringError',
+ 'CommandNotFound', 'DisabledCommand', 'CommandInvokeError',
+ 'TooManyArguments','UserInputError', 'CommandOnCooldown',
+ 'NotOwner', 'MissingRole', 'BotMissingRole', 'MissingAnyRole',
+ 'BotMissingAnyRole','MissingPermissions', 'BotMissingPermissions',
+ 'NSFWChannelRequired', 'ConversionError', 'BadUnionArgument',
+ 'ArgumentParsingError', 'UnexpectedQuoteError', 'InvalidEndOfQuotedStringError',
'ExpectedClosingQuoteError', 'ExtensionError', 'ExtensionAlreadyLoaded',
'ExtensionNotLoaded', 'NoEntryPointError', 'ExtensionFailed',
'ExtensionNotFound']
@@ -128,7 +129,9 @@ class NoPrivateMessage(CheckFailure):
"""Exception raised when an operation does not work in private message
contexts.
"""
- pass
+
+ def __init__(self):
+ super().__init__('This command cannot be used in private messages.')
class NotOwner(CheckFailure):
"""Exception raised when the message author is not the owner of the bot."""
@@ -167,8 +170,116 @@ class CommandOnCooldown(CommandError):
self.retry_after = retry_after
super().__init__('You are on cooldown. Try again in {:.2f}s'.format(retry_after))
+class MissingRole(CheckFailure):
+ """Exception raised when the command invoker lacks a role to run a command.
+
+ This inherits from :exc:`.CheckFailure`
+
+ .. versionadded:: 1.1.0
+
+ 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):
+ self.missing_role = missing_role
+ message = 'Role {0!r} is required to run this command.'.format(missing_role)
+ 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.0
+
+ 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):
+ self.missing_role = missing_role
+ message = 'Bot requires the role {0!r} to run this command'.format(missing_role)
+ 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.0
+
+ 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):
+ self.missing_roles = missing_roles
+
+ missing = ["'{}'".format(role) for role in missing_roles]
+
+ if len(missing) > 2:
+ fmt = '{}, or {}'.format(", ".join(missing[:-1]), missing[-1])
+ else:
+ fmt = ' or '.join(missing)
+
+ message = "You are missing at least one of the required roles: {}".format(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.0
+
+ 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):
+ self.missing_roles = missing_roles
+
+ missing = ["'{}'".format(role) for role in missing_roles]
+
+ if len(missing) > 2:
+ fmt = '{}, or {}'.format(", ".join(missing[:-1]), missing[-1])
+ else:
+ fmt = ' or '.join(missing)
+
+ message = "Bot is missing at least one of the required roles: {}".format(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.0
+
+ Parameters
+ -----------
+ channel: :class:`discord.abc.GuildChannel`
+ The channel that does not have NSFW enabled.
+ """
+ def __init__(self, channel):
+ self.channel = channel
+ super().__init__("Channel '{}' needs to be NSFW for this command to work.".format(channel))
+
class MissingPermissions(CheckFailure):
- """Exception raised when the command invoker lacks permissions to run
+ """Exception raised when the command invoker lacks permissions to run a
command.
Attributes
@@ -185,11 +296,12 @@ class MissingPermissions(CheckFailure):
fmt = '{}, and {}'.format(", ".join(missing[:-1]), missing[-1])
else:
fmt = ' and '.join(missing)
- message = 'You are missing {} permission(s) to run command.'.format(fmt)
+ message = 'You are missing {} permission(s) to run this command.'.format(fmt)
super().__init__(message, *args)
class BotMissingPermissions(CheckFailure):
- """Exception raised when the bot lacks permissions to run command.
+ """Exception raised when the bot's member lacks permissions to run a
+ command.
Attributes
-----------
@@ -205,7 +317,7 @@ class BotMissingPermissions(CheckFailure):
fmt = '{}, and {}'.format(", ".join(missing[:-1]), missing[-1])
else:
fmt = ' and '.join(missing)
- message = 'Bot requires {} permission(s) to run command.'.format(fmt)
+ message = 'Bot requires {} permission(s) to run this command.'.format(fmt)
super().__init__(message, *args)
class BadUnionArgument(UserInputError):
diff --git a/docs/ext/commands/api.rst b/docs/ext/commands/api.rst
index 166fdb1d..3400efe0 100644
--- a/docs/ext/commands/api.rst
+++ b/docs/ext/commands/api.rst
@@ -255,6 +255,9 @@ Exceptions
.. autoexception:: discord.ext.commands.BadUnionArgument
:members:
+.. autoexception:: discord.ext.commands.PrivateMessageOnly
+ :members:
+
.. autoexception:: discord.ext.commands.NoPrivateMessage
:members:
@@ -288,6 +291,21 @@ Exceptions
.. autoexception:: discord.ext.commands.BotMissingPermissions
:members:
+.. autoexception:: discord.ext.commands.MissingRole
+ :members:
+
+.. autoexception:: discord.ext.commands.BotMissingRole
+ :members:
+
+.. autoexception:: discord.ext.commands.MissingAnyRole
+ :members:
+
+.. autoexception:: discord.ext.commands.BotMissingAnyRole
+ :members:
+
+.. autoexception:: discord.ext.commands.NSFWChannelRequired
+ :members:
+
.. autoexception:: discord.ext.commands.ExtensionError
:members:
@@ -326,10 +344,16 @@ Exception Hierarchy
- :exc:`~.commands.ExpectedClosingQuoteError`
- :exc:`~.commands.CommandNotFound`
- :exc:`~.commands.CheckFailure`
+ - :exc:`~.commands.PrivateMessageOnly`
- :exc:`~.commands.NoPrivateMessage`
- :exc:`~.commands.NotOwner`
- :exc:`~.commands.MissingPermissions`
- :exc:`~.commands.BotMissingPermissions`
+ - :exc:`~.commands.MissingRole`
+ - :exc:`~.commands.BotMissingRole`
+ - :exc:`~.commands.MissingAnyRole`
+ - :exc:`~.commands.BotMissingAnyRole`
+ - :exc:`~.commands.NSFWChannelRequired`
- :exc:`~.commands.DisabledCommand`
- :exc:`~.commands.CommandInvokeError`
- :exc:`~.commands.CommandOnCooldown`