diff --git a/discord/ext/commands/converter.py b/discord/ext/commands/converter.py index 740ca2f43..5740a188e 100644 --- a/discord/ext/commands/converter.py +++ b/discord/ext/commands/converter.py @@ -74,6 +74,7 @@ __all__ = ( 'StoreChannelConverter', 'ThreadConverter', 'GuildChannelConverter', + 'GuildStickerConverter', 'clean_content', 'Greedy', 'run_converters', @@ -823,6 +824,45 @@ class PartialEmojiConverter(Converter[discord.PartialEmoji]): raise PartialEmojiConversionFailure(argument) +class GuildStickerConverter(IDConverter[discord.GuildSticker]): + """Converts to a :class:`~discord.GuildSticker`. + + All lookups are done for the local guild first, if available. If that lookup + fails, then it checks the client's global cache. + + The lookup strategy is as follows (in order): + + 1. Lookup by ID. + 3. Lookup by name + + .. versionadded:: 2.0 + """ + + async def convert(self, ctx: Context, argument: str) -> discord.GuildSticker: + match = self._get_id_match(argument) + result = None + bot = ctx.bot + guild = ctx.guild + + if match is None: + # Try to get the sticker by name. Try local guild first. + if guild: + result = discord.utils.get(guild.stickers, name=argument) + + if result is None: + result = discord.utils.get(bot.stickers, name=argument) + else: + sticker_id = int(match.group(1)) + + # Try to look up sticker by id. + result = bot.get_sticker(sticker_id) + + if result is None: + raise GuildStickerNotFound(argument) + + return result + + class clean_content(Converter[str]): """Converts the argument to mention scrubbed version of said content. @@ -1012,6 +1052,7 @@ CONVERTER_MAPPING: Dict[Type[Any], Any] = { discord.StoreChannel: StoreChannelConverter, discord.Thread: ThreadConverter, discord.abc.GuildChannel: GuildChannelConverter, + discord.GuildSticker: GuildStickerConverter, } diff --git a/discord/ext/commands/errors.py b/discord/ext/commands/errors.py index 0f0185be3..5a581d51e 100644 --- a/discord/ext/commands/errors.py +++ b/discord/ext/commands/errors.py @@ -54,6 +54,7 @@ __all__ = ( 'RoleNotFound', 'BadInviteArgument', 'EmojiNotFound', + 'GuildStickerNotFound', 'PartialEmojiConversionFailure', 'BadBoolArgument', 'MissingRole', @@ -431,6 +432,22 @@ class PartialEmojiConversionFailure(BadArgument): self.argument = 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): + self.argument = argument + super().__init__(f'Sticker "{argument}" not found.') + class BadBoolArgument(BadArgument): """Exception raised when a boolean argument was not convertable. diff --git a/docs/ext/commands/api.rst b/docs/ext/commands/api.rst index 27d782306..efbd3d8fe 100644 --- a/docs/ext/commands/api.rst +++ b/docs/ext/commands/api.rst @@ -411,6 +411,9 @@ Converters .. autoclass:: discord.ext.commands.ThreadConverter :members: +.. autoclass:: discord.ext.commands.GuildStickerConverter + :members: + .. autoclass:: discord.ext.commands.clean_content :members: @@ -536,6 +539,9 @@ Exceptions .. autoexception:: discord.ext.commands.PartialEmojiConversionFailure :members: +.. autoexception:: discord.ext.commands.GuildStickerNotFound + :members: + .. autoexception:: discord.ext.commands.BadBoolArgument :members: @@ -618,6 +624,7 @@ Exception Hierarchy - :exc:`~.commands.RoleNotFound` - :exc:`~.commands.BadInviteArgument` - :exc:`~.commands.EmojiNotFound` + - :exc:`~.commands.GuildStickerNotFound` - :exc:`~.commands.PartialEmojiConversionFailure` - :exc:`~.commands.BadBoolArgument` - :exc:`~.commands.ThreadNotFound`