From 661d053f211bedd1728c3ca54e6ad161bc5bc640 Mon Sep 17 00:00:00 2001 From: Hackermon <60828015+hackermondev@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:05:23 -0400 Subject: [PATCH 01/49] add discord developers server --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index 263fcd9355..b6e291e101 100644 --- a/README.rst +++ b/README.rst @@ -110,4 +110,5 @@ Links - `Documentation `_ - `Official Discord Server `_ +- `Discord Developers `_ - `Discord API `_ From af4d6cafed48bd445c7d449807d4ea03770142b1 Mon Sep 17 00:00:00 2001 From: "Swas.py" <61446939+CodeWithSwastik@users.noreply.github.com> Date: Thu, 2 Sep 2021 20:46:00 +0530 Subject: [PATCH 02/49] Update README.rst --- README.rst | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/README.rst b/README.rst index 263fcd9355..87933c7a10 100644 --- a/README.rst +++ b/README.rst @@ -72,22 +72,20 @@ Quick Example import discord - class MyClient(discord.Client): - async def on_ready(self): - print("Logged on as", self.user) - - async def on_message(self, message): - # don't respond to ourselves - if message.author == self.user: - return - - if message.content == "ping": - await message.channel.send("pong") - - client = MyClient() - client.run("token") + bot = discord.Bot() + + @bot.slash_command() + async def hello(ctx, name: str = None): + name = name or ctx.author.name + await ctx.send(f"Hello {name}!") + + @bot.user_command(name="Say Hello") + async def hi(ctx, user): + await ctx.send(f"{ctx.author.mention} says hello to {user.name}!") + + bot.run("token") -Bot Example +Normal Commands Example ~~~~~~~~~~~~~ .. code:: py From 1c21f64b6726b3461dd78ae79bf985716d1e5129 Mon Sep 17 00:00:00 2001 From: Dorukyum Date: Fri, 3 Sep 2021 15:44:50 +0300 Subject: [PATCH 03/49] Make invalid token type raise TypeError --- discord/client.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/discord/client.py b/discord/client.py index b6198d1090..20e9a629b5 100644 --- a/discord/client.py +++ b/discord/client.py @@ -460,6 +460,8 @@ async def login(self, token: str) -> None: Raises ------ + TypeError + The token was in invalid type. :exc:`.LoginFailure` The wrong credentials are passed. :exc:`.HTTPException` @@ -467,6 +469,8 @@ async def login(self, token: str) -> None: usually when it isn't 200 or the known incorrect credentials passing status code. """ + if not isinstance(token, str): + raise TypeError(f"token must be of type str, not {token.__class__.__name__}") _log.info('logging in using static token') From 7c4b4933fe9a091d1cdc5bc7214dc2b5de9191c9 Mon Sep 17 00:00:00 2001 From: Middledot <78228142+Middledot@users.noreply.github.com> Date: Sat, 4 Sep 2021 18:41:57 -0400 Subject: [PATCH 04/49] Move Extension Errors to discord.errors --- discord/ext/commands/errors.py | 82 ---------------------------------- 1 file changed, 82 deletions(-) diff --git a/discord/ext/commands/errors.py b/discord/ext/commands/errors.py index 938343857d..01ff482dc3 100644 --- a/discord/ext/commands/errors.py +++ b/discord/ext/commands/errors.py @@ -86,12 +86,6 @@ 'UnexpectedQuoteError', 'InvalidEndOfQuotedStringError', 'ExpectedClosingQuoteError', - 'ExtensionError', - 'ExtensionAlreadyLoaded', - 'ExtensionNotLoaded', - 'NoEntryPointError', - 'ExtensionFailed', - 'ExtensionNotFound', 'CommandRegistrationError', 'FlagError', 'BadFlagArgument', @@ -818,82 +812,6 @@ 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. From f261b84124e6732382e2b4aca6689932a6687e10 Mon Sep 17 00:00:00 2001 From: Middledot <78228142+Middledot@users.noreply.github.com> Date: Sat, 4 Sep 2021 18:45:49 -0400 Subject: [PATCH 05/49] Add extension errors from ext.commands.errors --- discord/errors.py | 82 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/discord/errors.py b/discord/errors.py index 852c0462b7..5dec1703b4 100644 --- a/discord/errors.py +++ b/discord/errors.py @@ -52,6 +52,12 @@ 'ConnectionClosed', 'PrivilegedIntentsRequired', 'InteractionResponded', + 'ExtensionError', + 'ExtensionAlreadyLoaded', + 'ExtensionNotLoaded', + 'NoEntryPointError', + 'ExtensionFailed', + 'ExtensionNotFound' ) @@ -275,3 +281,79 @@ class InteractionResponded(ClientException): def __init__(self, interaction: Interaction): self.interaction: Interaction = interaction super().__init__('This interaction has already been responded to before') + +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) From ba46224c958ba9f34516c183a63f86e8b6f9ed27 Mon Sep 17 00:00:00 2001 From: Izhar Ahmad <54180221+nerdguyahmad@users.noreply.github.com> Date: Sun, 5 Sep 2021 17:30:22 +0500 Subject: [PATCH 06/49] Improve message for `ExtensionNotFound` --- discord/errors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/errors.py b/discord/errors.py index 5dec1703b4..70a4bc27c5 100644 --- a/discord/errors.py +++ b/discord/errors.py @@ -355,5 +355,5 @@ class ExtensionNotFound(ExtensionError): The extension that had the error. """ def __init__(self, name: str) -> None: - msg = f'Extension {name!r} could not be loaded.' + msg = f'Extension {name!r} could not be found.' super().__init__(msg, name=name) From 64736070e9950f70021d09ed7c49c32ea11c430e Mon Sep 17 00:00:00 2001 From: Izhar Ahmad <54180221+nerdguyahmad@users.noreply.github.com> Date: Sun, 5 Sep 2021 19:34:51 +0500 Subject: [PATCH 07/49] Add `remove_image` and `remove_thumbnail` methods. --- discord/embeds.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/discord/embeds.py b/discord/embeds.py index 7033a10e78..c1d528cd5a 100644 --- a/discord/embeds.py +++ b/discord/embeds.py @@ -424,6 +424,22 @@ def set_image(self: E, *, url: MaybeEmpty[Any]) -> E: return self + def remove_image(self): + """Removes the embed's image. + + This function returns the class instance to allow for fluent-style + chaining. + + .. versionadded:: 2.0 + """ + try: + del self._image + except AttributeError: + pass + + return self + + @property def thumbnail(self) -> _EmbedMediaProxy: """Returns an ``EmbedProxy`` denoting the thumbnail contents. @@ -466,6 +482,21 @@ def set_thumbnail(self: E, *, url: MaybeEmpty[Any]) -> E: return self + def remove_thumbnail(self): + """Removes the embed's thumbnail. + + This function returns the class instance to allow for fluent-style + chaining. + + .. versionadded:: 2.0 + """ + try: + del self._thumbnail + except AttributeError: + pass + + return self + @property def video(self) -> _EmbedVideoProxy: """Returns an ``EmbedProxy`` denoting the video contents. From 60072da5006fe41f7e3317c36e7973af365c8947 Mon Sep 17 00:00:00 2001 From: Izhar Ahmad <54180221+nerdguyahmad@users.noreply.github.com> Date: Sun, 5 Sep 2021 19:36:54 +0500 Subject: [PATCH 08/49] Typehint new methods --- discord/embeds.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/embeds.py b/discord/embeds.py index c1d528cd5a..0ee29981e8 100644 --- a/discord/embeds.py +++ b/discord/embeds.py @@ -424,7 +424,7 @@ def set_image(self: E, *, url: MaybeEmpty[Any]) -> E: return self - def remove_image(self): + def remove_image(self: E) -> E: """Removes the embed's image. This function returns the class instance to allow for fluent-style @@ -482,7 +482,7 @@ def set_thumbnail(self: E, *, url: MaybeEmpty[Any]) -> E: return self - def remove_thumbnail(self): + def remove_thumbnail(self: E) -> E: """Removes the embed's thumbnail. This function returns the class instance to allow for fluent-style From 7e8c3e91d85131e8d09d0e33b6258e60742f402c Mon Sep 17 00:00:00 2001 From: Izhar Ahmad <54180221+nerdguyahmad@users.noreply.github.com> Date: Mon, 6 Sep 2021 09:55:29 +0500 Subject: [PATCH 09/49] Add missing Bot.run() line --- examples/app_commands/slash_options.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/app_commands/slash_options.py b/examples/app_commands/slash_options.py index 586733c0eb..afc5883afc 100644 --- a/examples/app_commands/slash_options.py +++ b/examples/app_commands/slash_options.py @@ -15,3 +15,5 @@ async def hello( age: Option(int, "Enter your age", required=False, default=18), ): await ctx.send(f"Hello {name}") + +bot.run('TOKEN') From d119c0d1bef7d628381604faf0b5c35da229992d Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 6 Sep 2021 11:29:19 +0530 Subject: [PATCH 10/49] Use slash_command in examples --- examples/app_commands/slash_basic.py | 13 ++++++++----- examples/app_commands/slash_options.py | 6 +----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/examples/app_commands/slash_basic.py b/examples/app_commands/slash_basic.py index 283f2f52e9..6196f1b406 100644 --- a/examples/app_commands/slash_basic.py +++ b/examples/app_commands/slash_basic.py @@ -2,24 +2,27 @@ bot = discord.Bot() -# If you use commands.Bot, @bot.slash_command should be used for -# slash commands. You can use @bot.slash_command with discord.Bot aswell +# Note: If you want you can use commands.Bot as well +# Use discord.Bot if you don't want prefixed message commands +# With discord.Bot you can use @bot.command as an alias +# of @bot.slash_command but this is overriden by commands.Bot -@bot.command(guild_ids=[...]) # create a slash command for the supplied guilds + +@bot.slash_command(guild_ids=[...]) # create a slash command for the supplied guilds async def hello(ctx): """Say hello to the bot""" # the command description can be supplied as the docstring await ctx.send(f"Hello {ctx.author}!") -@bot.command( +@bot.slash_command( name="hi" ) # Not passing in guild_ids creates a global slash command (might take an hour to register) async def global_command(ctx, num: int): # Takes one integer parameter await ctx.send(f"This is a global command, {num}!") -@bot.command(guild_ids=[...]) +@bot.slash_command(guild_ids=[...]) async def joined( ctx, member: discord.Member = None ): # Passing a default value makes the argument optional diff --git a/examples/app_commands/slash_options.py b/examples/app_commands/slash_options.py index afc5883afc..4e8919c0d6 100644 --- a/examples/app_commands/slash_options.py +++ b/examples/app_commands/slash_options.py @@ -3,11 +3,7 @@ bot = discord.Bot() -# If you use commands.Bot, @bot.slash_command should be used for -# slash commands. You can use @bot.slash_command with discord.Bot aswell - - -@bot.command(guild_ids=[...]) +@bot.slash_command(guild_ids=[...]) async def hello( ctx, name: Option(str, "Enter your name"), From 5dbc432034e65ffdc45a665b974f56a08867ee0a Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 6 Sep 2021 11:59:56 +0530 Subject: [PATCH 11/49] Rephrase comment in example --- examples/app_commands/slash_basic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/app_commands/slash_basic.py b/examples/app_commands/slash_basic.py index 6196f1b406..abb2b89d59 100644 --- a/examples/app_commands/slash_basic.py +++ b/examples/app_commands/slash_basic.py @@ -2,7 +2,7 @@ bot = discord.Bot() -# Note: If you want you can use commands.Bot as well +# Note: If you want you can use commands.Bot instead of discord.Bot # Use discord.Bot if you don't want prefixed message commands # With discord.Bot you can use @bot.command as an alias From 6d00c9567e4fed84acec18549ed347b90349dfd4 Mon Sep 17 00:00:00 2001 From: Lucas Hardt Date: Mon, 6 Sep 2021 17:25:47 +0200 Subject: [PATCH 12/49] Fix Components getting added to children instead of Items --- discord/ui/view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/ui/view.py b/discord/ui/view.py index acabd20c13..670f05eff5 100644 --- a/discord/ui/view.py +++ b/discord/ui/view.py @@ -402,7 +402,7 @@ def refresh(self, components: List[Component]): item = _component_to_item(component) if not item.is_dispatchable(): continue - children.append(component) + children.append(item) else: older.refresh_component(component) children.append(older) From e6bdfeb0727fa78c874b85df4a6844602f18552b Mon Sep 17 00:00:00 2001 From: Pop Rosian Date: Tue, 7 Sep 2021 00:07:21 +0300 Subject: [PATCH 13/49] Removed redundant type declaration of input_type argument of class Option in discord.app --- discord/app/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/app/commands.py b/discord/app/commands.py index 5b5cc7f48c..4685c7eb94 100644 --- a/discord/app/commands.py +++ b/discord/app/commands.py @@ -169,7 +169,7 @@ async def invoke(self, ctx: InteractionContext) -> None: class Option: def __init__( - self, input_type: SlashCommandOptionType, /, description: str, **kwargs + self, input_type, /, description: str, **kwargs ) -> None: self.name: Optional[str] = kwargs.pop("name", None) self.description = description From 6e8b2fe050d775ef08a44c50727e8714d2663202 Mon Sep 17 00:00:00 2001 From: Izhar Ahmad <54180221+nerdguyahmad@users.noreply.github.com> Date: Tue, 7 Sep 2021 09:05:04 +0500 Subject: [PATCH 14/49] Add Color.nitro_pink --- discord/colour.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/discord/colour.py b/discord/colour.py index 2833e6225b..baf40838a3 100644 --- a/discord/colour.py +++ b/discord/colour.py @@ -325,5 +325,13 @@ def yellow(cls: Type[CT]) -> CT: """ return cls(0xFEE75C) + @classmethod + def nitro_pink(cls, Type: [CT]) -> CT: + """A factory method that returns a :class:`Color` with a value of ``0xf47fff`. + + .. versionadded:: 2.0 + """ + return cls(0xf47fff) + Color = Colour From eb49f07c0ea3f3b59c45de2e1407e20d1c90334d Mon Sep 17 00:00:00 2001 From: Izhar Ahmad <54180221+nerdguyahmad@users.noreply.github.com> Date: Tue, 7 Sep 2021 15:57:17 +0500 Subject: [PATCH 15/49] Add basic `RawThreadDeleteEvent` --- discord/raw_models.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/discord/raw_models.py b/discord/raw_models.py index cda754d1b9..e9960ba506 100644 --- a/discord/raw_models.py +++ b/discord/raw_models.py @@ -276,3 +276,32 @@ def __init__(self, data: IntegrationDeleteEvent) -> None: self.application_id: Optional[int] = int(data['application_id']) except KeyError: self.application_id: Optional[int] = None + +class RawThreadDeleteEvent(_RawReprMixin): + """Represents the payload for :func:`on_raw_thread_delete` event. + + .. versionadded:: 2.0 + + Attributes + ---------- + + thread_id: :class:`int` + The ID of the thread that was deleted. + thread_type: :class:`discord.ChannelType` + The channel type of the deleted thread. + guild_id: :class:`int` + The ID of the guild the deleted thread belonged to. + parent_id: :class:`int` + The ID of the channel the thread belonged to. + thread: :class:`Optional[discord.Thread]` + The thread that was deleted. This may be ``None`` if deleted thread is not found in internal cache. + """ + # TODO: Typehint data as RawThreadDeleteEvent when added to types.raw_models + def __init__(self, data) -> None: + self.thread_id: int = data.get('id') + self.thread_type: ChannelType = try_enum(ChannelType, data.get('type')) + self.guild_id: int = data.get('guild_id') + self.parent_id: int = data.get('parent_id') + self.thread: Thread = None + + From 6fdc10737f58bb4fb3851fe399c9d485dc9a1e59 Mon Sep 17 00:00:00 2001 From: Izhar Ahmad <54180221+nerdguyahmad@users.noreply.github.com> Date: Tue, 7 Sep 2021 16:00:01 +0500 Subject: [PATCH 16/49] Add missing import & Fix typehint of thread --- discord/raw_models.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/discord/raw_models.py b/discord/raw_models.py index e9960ba506..39620dd331 100644 --- a/discord/raw_models.py +++ b/discord/raw_models.py @@ -26,6 +26,8 @@ from typing import TYPE_CHECKING, Optional, Set, List +from .enums import ChannelType, try_enum + if TYPE_CHECKING: from .types.raw_models import ( MessageDeleteEvent, @@ -293,7 +295,7 @@ class RawThreadDeleteEvent(_RawReprMixin): The ID of the guild the deleted thread belonged to. parent_id: :class:`int` The ID of the channel the thread belonged to. - thread: :class:`Optional[discord.Thread]` + thread: Optional[:class:`discord.Thread`] The thread that was deleted. This may be ``None`` if deleted thread is not found in internal cache. """ # TODO: Typehint data as RawThreadDeleteEvent when added to types.raw_models @@ -302,6 +304,6 @@ def __init__(self, data) -> None: self.thread_type: ChannelType = try_enum(ChannelType, data.get('type')) self.guild_id: int = data.get('guild_id') self.parent_id: int = data.get('parent_id') - self.thread: Thread = None + self.thread: Optional[Thread] = None From df6710686cc67287afd2e63128b3f9fd776c7365 Mon Sep 17 00:00:00 2001 From: Izhar Ahmad <54180221+nerdguyahmad@users.noreply.github.com> Date: Tue, 7 Sep 2021 16:08:07 +0500 Subject: [PATCH 17/49] [types] Add ThreadDeleteEvent to raw models --- discord/types/raw_models.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/discord/types/raw_models.py b/discord/types/raw_models.py index 3c45b299c1..1a6ed8e934 100644 --- a/discord/types/raw_models.py +++ b/discord/types/raw_models.py @@ -85,3 +85,10 @@ class _IntegrationDeleteEventOptional(TypedDict, total=False): class IntegrationDeleteEvent(_IntegrationDeleteEventOptional): id: Snowflake guild_id: Snowflake + +class ThreadDeleteEvent(TypedDict, total=False): + thread_id: Snowflake + thread_type: int + guild_id: Snowflake + parent_id: Snowflake + From 337ca557b07ffeca33c36c1d2ac364251b12cc48 Mon Sep 17 00:00:00 2001 From: Izhar Ahmad <54180221+nerdguyahmad@users.noreply.github.com> Date: Tue, 7 Sep 2021 16:13:43 +0500 Subject: [PATCH 18/49] Remove usage of .get() and typehint data. --- discord/raw_models.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/discord/raw_models.py b/discord/raw_models.py index 39620dd331..244091b1c6 100644 --- a/discord/raw_models.py +++ b/discord/raw_models.py @@ -36,7 +36,8 @@ MessageUpdateEvent, ReactionClearEvent, ReactionClearEmojiEvent, - IntegrationDeleteEvent + IntegrationDeleteEvent, + ThreadDeleteEvent, ) from .message import Message from .partial_emoji import PartialEmoji @@ -51,6 +52,7 @@ 'RawReactionClearEvent', 'RawReactionClearEmojiEvent', 'RawIntegrationDeleteEvent', + 'RawThreadDeleteEvent', ) @@ -298,12 +300,12 @@ class RawThreadDeleteEvent(_RawReprMixin): thread: Optional[:class:`discord.Thread`] The thread that was deleted. This may be ``None`` if deleted thread is not found in internal cache. """ - # TODO: Typehint data as RawThreadDeleteEvent when added to types.raw_models - def __init__(self, data) -> None: - self.thread_id: int = data.get('id') - self.thread_type: ChannelType = try_enum(ChannelType, data.get('type')) - self.guild_id: int = data.get('guild_id') - self.parent_id: int = data.get('parent_id') + + def __init__(self, data: ThreadDeleteEvent) -> None: + self.thread_id: int = data['id'] + self.thread_type: ChannelType = try_enum(ChannelType, data['type']) + self.guild_id: int = data['guild_id'] + self.parent_id: int = data['parent_id'] self.thread: Optional[Thread] = None From ce87a0d5de3da83d856584629fb54368bdeeac0b Mon Sep 17 00:00:00 2001 From: Izhar Ahmad <54180221+nerdguyahmad@users.noreply.github.com> Date: Tue, 7 Sep 2021 16:21:33 +0500 Subject: [PATCH 19/49] Dispatch `raw_thread_delete` --- discord/state.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/discord/state.py b/discord/state.py index 2534e7aac4..5c1a5ec08c 100644 --- a/discord/state.py +++ b/discord/state.py @@ -850,12 +850,18 @@ def parse_thread_update(self, data) -> None: def parse_thread_delete(self, data) -> None: guild_id = int(data['guild_id']) guild = self._get_guild(guild_id) + if guild is None: _log.debug('THREAD_DELETE referencing an unknown guild ID: %s. Discarding', guild_id) return - thread_id = int(data['id']) - thread = guild.get_thread(thread_id) + raw = RawThreadDeleteEvent(data) + thread = guild.get_thread(raw.thread_id) + raw.thread = thread + + self.dispatch('raw_thread_delete', raw) + + if thread is not None: guild._remove_thread(thread) # type: ignore self.dispatch('thread_delete', thread) From 5ee36c6064f607a7373ab0764e52d749680839a2 Mon Sep 17 00:00:00 2001 From: Izhar Ahmad <54180221+nerdguyahmad@users.noreply.github.com> Date: Tue, 7 Sep 2021 16:33:11 +0500 Subject: [PATCH 20/49] Document raw_thread_delete event --- docs/api.rst | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/api.rst b/docs/api.rst index 0bd611886c..38e859f154 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -759,7 +759,9 @@ to handle it, which defaults to print a traceback and ignoring the exception. .. function:: on_thread_delete(thread) - Called whenever a thread is deleted. + Called whenever a thread is deleted. If the deleted thread isn't found in internal cache + then this will not be called. Consider using :func:`on_raw_thread_delete` + Note that you can get the guild from :attr:`Thread.guild`. @@ -770,6 +772,14 @@ to handle it, which defaults to print a traceback and ignoring the exception. :param thread: The thread that got deleted. :type thread: :class:`Thread` +.. function:: on_raw_thread_delete(payload) + + Called whenever a thread is deleted. Unlike :func:`on_thread_delete` this is called + regardless of the state of the internal cache. + + :param payload: The raw event payload data. + :type payload: :class:`RawThreadDeleteEvent` + .. function:: on_thread_member_join(member) on_thread_member_remove(member) From da6486863c7f3561928f6fcb9df7ec8e4320865c Mon Sep 17 00:00:00 2001 From: Izhar Ahmad <54180221+nerdguyahmad@users.noreply.github.com> Date: Tue, 7 Sep 2021 16:37:48 +0500 Subject: [PATCH 21/49] Add archived thread notice and document RawThreadDeleteEvent --- docs/api.rst | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/api.rst b/docs/api.rst index 38e859f154..6f0fdb942b 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -760,7 +760,7 @@ to handle it, which defaults to print a traceback and ignoring the exception. .. function:: on_thread_delete(thread) Called whenever a thread is deleted. If the deleted thread isn't found in internal cache - then this will not be called. Consider using :func:`on_raw_thread_delete` + then this will not be called. Archived threads are not in the cache. Consider using :func:`on_raw_thread_delete` Note that you can get the guild from :attr:`Thread.guild`. @@ -3953,6 +3953,14 @@ RawIntegrationDeleteEvent .. autoclass:: RawIntegrationDeleteEvent() :members: +RawThreadDeleteEvent +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. attributetable:: RawThreadDeleteEvent + +.. autoclass:: RawThreadDeleteEvent() + :members: + PartialWebhookGuild ~~~~~~~~~~~~~~~~~~~~ From f13d9f2c2ba5e823832268de66bd45eccb4df108 Mon Sep 17 00:00:00 2001 From: Izhar Ahmad <54180221+nerdguyahmad@users.noreply.github.com> Date: Tue, 7 Sep 2021 16:52:56 +0500 Subject: [PATCH 22/49] Typecast IDs and add __slots__ --- discord/raw_models.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/discord/raw_models.py b/discord/raw_models.py index 244091b1c6..acb2253f56 100644 --- a/discord/raw_models.py +++ b/discord/raw_models.py @@ -300,12 +300,13 @@ class RawThreadDeleteEvent(_RawReprMixin): thread: Optional[:class:`discord.Thread`] The thread that was deleted. This may be ``None`` if deleted thread is not found in internal cache. """ - + __slots__ = ('thread_id', 'thread_type', 'guild_id', 'parent_id', 'thread') + def __init__(self, data: ThreadDeleteEvent) -> None: - self.thread_id: int = data['id'] - self.thread_type: ChannelType = try_enum(ChannelType, data['type']) - self.guild_id: int = data['guild_id'] - self.parent_id: int = data['parent_id'] + self.thread_id: int = int(data['id']) + self.thread_type: ChannelType = try_enum(ChannelType, int(data['type'])) + self.guild_id: int = int(data['guild_id']) + self.parent_id: int = int(data['parent_id']) self.thread: Optional[Thread] = None From f8d3153b6468d07188938ebcdc3899fdf107b00d Mon Sep 17 00:00:00 2001 From: Izhar Ahmad <54180221+nerdguyahmad@users.noreply.github.com> Date: Tue, 7 Sep 2021 17:18:21 +0500 Subject: [PATCH 23/49] Add Thread import --- discord/raw_models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/discord/raw_models.py b/discord/raw_models.py index acb2253f56..589e0af079 100644 --- a/discord/raw_models.py +++ b/discord/raw_models.py @@ -42,6 +42,8 @@ from .message import Message from .partial_emoji import PartialEmoji from .member import Member + from .threads import Thread + __all__ = ( From 5fd13c6c4618a1f3267042c8b21fdcd229124ccb Mon Sep 17 00:00:00 2001 From: Pop Rosian Date: Tue, 7 Sep 2021 17:55:50 +0300 Subject: [PATCH 24/49] Remove type hinting of slash_command, user_command, message_command --- discord/bot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/discord/bot.py b/discord/bot.py index 7723fc2a01..fbd070744a 100644 --- a/discord/bot.py +++ b/discord/bot.py @@ -247,7 +247,7 @@ async def handle_interaction(self, interaction: Interaction) -> None: context = await self.get_application_context(interaction) await command.invoke(context) - def slash_command(self, **kwargs) -> SlashCommand: + def slash_command(self, **kwargs): """A shortcut decorator that invokes :func:`.ApplicationCommandMixin.command` and adds it to the internal command list via :meth:`~.ApplicationCommandMixin.add_application_command`. This shortcut is made specifically for :class:`.SlashCommand`. @@ -262,7 +262,7 @@ def slash_command(self, **kwargs) -> SlashCommand: """ return self.application_command(cls=SlashCommand, **kwargs) - def user_command(self, **kwargs) -> UserCommand: + def user_command(self, **kwargs): """A shortcut decorator that invokes :func:`.ApplicationCommandMixin.command` and adds it to the internal command list via :meth:`~.ApplicationCommandMixin.add_application_command`. This shortcut is made specifically for :class:`.UserCommand`. @@ -277,7 +277,7 @@ def user_command(self, **kwargs) -> UserCommand: """ return self.application_command(cls=UserCommand, **kwargs) - def message_command(self, **kwargs) -> MessageCommand: + def message_command(self, **kwargs): """A shortcut decorator that invokes :func:`.ApplicationCommandMixin.command` and adds it to the internal command list via :meth:`~.ApplicationCommandMixin.add_application_command`. This shortcut is made specifically for :class:`.MessageCommand`. From 3b406b3923574564150ec57f6447e5865b564472 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 7 Sep 2021 21:52:55 +0530 Subject: [PATCH 25/49] Add create_party_invite shortcut method --- discord/channel.py | 61 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/discord/channel.py b/discord/channel.py index be3315cf1a..8fc7042065 100644 --- a/discord/channel.py +++ b/discord/channel.py @@ -45,7 +45,7 @@ import discord.abc from .permissions import PermissionOverwrite, Permissions -from .enums import ChannelType, StagePrivacyLevel, try_enum, VoiceRegion, VideoQualityMode +from .enums import ChannelType, InviteTarget, StagePrivacyLevel, try_enum, VoiceRegion, VideoQualityMode from .mixins import Hashable from .object import Object from . import utils @@ -55,6 +55,7 @@ from .stage_instance import StageInstance from .threads import Thread from .iterators import ArchivedThreadIterator +from .invite import Invite __all__ = ( 'TextChannel', @@ -1037,6 +1038,64 @@ async def edit(self, *, reason=None, **options): # the payload will always be the proper channel payload return self.__class__(state=self._state, guild=self.guild, data=payload) # type: ignore + async def create_party_invite(self, event:str, **kwargs) -> Invite: + """|coro| + + A shortcut method that creates an instant party invite. + + You must have the :attr:`~discord.Permissions.create_instant_invite` permission to + do this. + + Parameters + ------------ + event: :class:`str` + The event to create an invite for. + max_age: :class:`int` + How long the invite should last in seconds. If it's 0 then the invite + doesn't expire. Defaults to ``0``. + max_uses: :class:`int` + How many uses the invite could be used for. If it's 0 then there + are unlimited uses. Defaults to ``0``. + temporary: :class:`bool` + Denotes that the invite grants temporary membership + (i.e. they get kicked after they disconnect). Defaults to ``False``. + unique: :class:`bool` + Indicates if a unique invite URL should be created. Defaults to True. + If this is set to ``False`` then it will return a previously created + invite. + reason: Optional[:class:`str`] + The reason for creating this invite. Shows up on the audit log. + + + Raises + ------- + InvalidArgument + If the event is not a valid event. + ~discord.HTTPException + Invite creation failed. + + Returns + -------- + :class:`~discord.Invite` + The invite that was created. + """ + + application_ids = { + 'youtube' : 755600276941176913, + 'poker' : 755827207812677713, + 'betrayal': 773336526917861400, + 'fishing' : 814288819477020702, + 'chess' : 832012774040141894, + } + event = application_ids.get(event) + if event is None: + raise InvalidArgument('Invalid event.') + + return await self.create_invite( + target_type=InviteTarget.embedded_application, + target_application_id=event, + **kwargs + ) class StageChannel(VocalGuildChannel): """Represents a Discord guild stage channel. From 2a46b88c9151b642a4db00b8539d3bde44ea7c26 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 Sep 2021 07:19:23 +0530 Subject: [PATCH 26/49] Change create_party_invite to create_activity_invite --- discord/channel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/channel.py b/discord/channel.py index 8fc7042065..2164cfc5f1 100644 --- a/discord/channel.py +++ b/discord/channel.py @@ -1038,10 +1038,10 @@ async def edit(self, *, reason=None, **options): # the payload will always be the proper channel payload return self.__class__(state=self._state, guild=self.guild, data=payload) # type: ignore - async def create_party_invite(self, event:str, **kwargs) -> Invite: + async def create_activity_invite(self, event:str, **kwargs) -> Invite: """|coro| - A shortcut method that creates an instant party invite. + A shortcut method that creates an instant activity invite. You must have the :attr:`~discord.Permissions.create_instant_invite` permission to do this. From 5da5516d0133b5d89200d6e0e493d065a3660a2a Mon Sep 17 00:00:00 2001 From: BobDotCom Date: Tue, 7 Sep 2021 21:56:20 -0500 Subject: [PATCH 27/49] Make InteractionContext an abc.Messageable --- discord/abc.py | 1 + discord/app/context.py | 14 ++++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/discord/abc.py b/discord/abc.py index 6d06a12d46..80179cc6e2 100644 --- a/discord/abc.py +++ b/discord/abc.py @@ -1141,6 +1141,7 @@ class Messageable: - :class:`~discord.Member` - :class:`~discord.ext.commands.Context` - :class:`~discord.Thread` + - :class:`~discord.InteractionContext` """ __slots__ = () diff --git a/discord/app/context.py b/discord/app/context.py index cb11bf921c..593c42e580 100644 --- a/discord/app/context.py +++ b/discord/app/context.py @@ -24,8 +24,11 @@ from typing import TYPE_CHECKING, Optional, Union +import discord.abc + if TYPE_CHECKING: import discord + from discord.state import ConnectionState from ..guild import Guild from ..interactions import Interaction, InteractionResponse @@ -36,7 +39,7 @@ from ..context_managers import Typing -class InteractionContext: +class InteractionContext(discord.abc.Messageable): """Represents a Discord interaction context. This class is not created manually and is instead passed to application @@ -58,6 +61,10 @@ def __init__(self, bot: "discord.Bot", interaction: Interaction): self.bot = bot self.interaction = interaction self.command = None + self._state: ConnectionState = self.interaction._state + + async def _get_channel(self) -> discord.abc.Messageable: + return self.channel @cached_property def channel(self): @@ -100,11 +107,6 @@ def response(self) -> InteractionResponse: def respond(self): return self.followup.send if self.response.is_done() else self.interaction.response.send_message - @property - def send(self): - """Behaves like :attr:`~discord.abc.Messagable.send` if the response is done, else behaves like :attr:`~discord.app.InteractionContext.respond`""" - return self.channel.send if self.response.is_done() else self.respond - @property def defer(self): return self.interaction.response.defer From b1af10a5be4c0b806c55fbf7b931d1735acc006a Mon Sep 17 00:00:00 2001 From: BobDotCom Date: Tue, 7 Sep 2021 21:57:12 -0500 Subject: [PATCH 28/49] add abc.messageable to __all__ --- discord/abc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/discord/abc.py b/discord/abc.py index 80179cc6e2..7c97544924 100644 --- a/discord/abc.py +++ b/discord/abc.py @@ -62,6 +62,7 @@ 'GuildChannel', 'Messageable', 'Connectable', + 'Mentionable' ) T = TypeVar('T', bound=VoiceProtocol) From f412916bf60f8c00779b66bd8e119d329aeb27ff Mon Sep 17 00:00:00 2001 From: BobDotCom <71356958+BobDotCom@users.noreply.github.com> Date: Tue, 7 Sep 2021 22:28:57 -0500 Subject: [PATCH 29/49] remove interactioncontext.typing --- discord/app/context.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/discord/app/context.py b/discord/app/context.py index 593c42e580..6b686360b7 100644 --- a/discord/app/context.py +++ b/discord/app/context.py @@ -36,7 +36,6 @@ from ..message import Message from ..user import User from ..utils import cached_property -from ..context_managers import Typing class InteractionContext(discord.abc.Messageable): @@ -94,9 +93,6 @@ def user(self) -> Optional[Union[Member, User]]: def voice_client(self): return self.guild.voice_client - def typing(self): - return Typing(self.channel) - @cached_property def response(self) -> InteractionResponse: return self.interaction.response From cc4381b57fa8fd1413325ec64d9c6a6a757018b2 Mon Sep 17 00:00:00 2001 From: proguy914629 <74696067+proguy914629bot@users.noreply.github.com> Date: Wed, 8 Sep 2021 16:40:45 +0700 Subject: [PATCH 30/49] Update colour.py --- discord/colour.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/colour.py b/discord/colour.py index baf40838a3..8b9cb68d29 100644 --- a/discord/colour.py +++ b/discord/colour.py @@ -326,7 +326,7 @@ def yellow(cls: Type[CT]) -> CT: return cls(0xFEE75C) @classmethod - def nitro_pink(cls, Type: [CT]) -> CT: + def nitro_pink(cls: Type[CT]) -> CT: """A factory method that returns a :class:`Color` with a value of ``0xf47fff`. .. versionadded:: 2.0 From 1009a8d4be98e3d04e49ae0a21a98538bd0bbd4a Mon Sep 17 00:00:00 2001 From: proguy914629 <74696067+proguy914629bot@users.noreply.github.com> Date: Wed, 8 Sep 2021 16:46:48 +0700 Subject: [PATCH 31/49] Update colour.py --- discord/colour.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/colour.py b/discord/colour.py index 8b9cb68d29..3b4742cfef 100644 --- a/discord/colour.py +++ b/discord/colour.py @@ -327,7 +327,7 @@ def yellow(cls: Type[CT]) -> CT: @classmethod def nitro_pink(cls: Type[CT]) -> CT: - """A factory method that returns a :class:`Color` with a value of ``0xf47fff`. + """A factory method that returns a :class:`Color` with a value of ``0xf47fff``. .. versionadded:: 2.0 """ From b55fb12ecb79fc50d8ac74976d7e86c4eaec9232 Mon Sep 17 00:00:00 2001 From: proguy914629 <74696067+proguy914629bot@users.noreply.github.com> Date: Wed, 8 Sep 2021 16:47:14 +0700 Subject: [PATCH 32/49] Update colour.py better practice ig --- discord/colour.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/colour.py b/discord/colour.py index 3b4742cfef..8cf52dc2ec 100644 --- a/discord/colour.py +++ b/discord/colour.py @@ -327,7 +327,7 @@ def yellow(cls: Type[CT]) -> CT: @classmethod def nitro_pink(cls: Type[CT]) -> CT: - """A factory method that returns a :class:`Color` with a value of ``0xf47fff``. + """A factory method that returns a :class:`Colour` with a value of ``0xf47fff``. .. versionadded:: 2.0 """ From 1170ad50bf455197752daf08793f6953315de630 Mon Sep 17 00:00:00 2001 From: Gracie <87055757+Grace-codes@users.noreply.github.com> Date: Thu, 9 Sep 2021 09:53:10 +0530 Subject: [PATCH 33/49] Allow option decor to infer type from the typehint --- discord/app/commands.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/discord/app/commands.py b/discord/app/commands.py index b0528221bd..a0d54ebfd8 100644 --- a/discord/app/commands.py +++ b/discord/app/commands.py @@ -501,9 +501,11 @@ def __init__(self, name: str, value: Optional[Union[str, int, float]] = None): def to_dict(self) -> Dict[str, Union[str, int, float]]: return {"name": self.name, "value": self.value} -def option(name, type, **kwargs): +def option(name, type=None, **kwargs): """A decorator that can be used instead of typehinting Option""" def decor(func): + nonlocal type + type = type or func.__annotations__.get(name, str) func.__annotations__[name] = Option(type, **kwargs) return func return decor From 5a727fa15dc199fd20e296fda0a4507a4818c00a Mon Sep 17 00:00:00 2001 From: Izhar Ahmad <54180221+nerdguyahmad@users.noreply.github.com> Date: Thu, 9 Sep 2021 11:28:31 +0500 Subject: [PATCH 34/49] [welcome_screen] basic implementation --- discord/welcome_screen.py | 137 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 discord/welcome_screen.py diff --git a/discord/welcome_screen.py b/discord/welcome_screen.py new file mode 100644 index 0000000000..dc2d68c3f5 --- /dev/null +++ b/discord/welcome_screen.py @@ -0,0 +1,137 @@ +""" +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 TYPE_CHECKING, List, Union +from .utils import _get_as_snowflake, get + +if TYPE_CHECKING: + from .types.welcome_screen import ( + WelcomeScreen as WelcomeScreenPayload, + WelcomeScreenChannel as WelcomeScreenChannelPayload, + ) + from .guild import Guild + from .abc import Snowflake + from .partial_emoji import PartialEmoji + from .emoji import Emoji + +__all__ = ( + 'WelcomeScreen', + 'WelcomeScreenChannel', +) + +class WelcomeScreenChannel: + """Represents a welcome channel displayed on :class:`WelcomeScreen` + + .. versionadded:: 2.0 + + Attributes + ---------- + + channel: :class:`abc.Snowflake` + The channel that is being referenced. + description: :class:`str` + The description of channel that is shown on the welcome screen. + emoji: :class:`Union[Emoji, PartialEmoji, str]` + The emoji of channel that is shown on welcome screen. + """ + def __init__(self, channel: Snowflake, description: str, emoji: Union[Emoji, PartialEmoji, str]): + self.channel = channel + self.description = description + self.emoji = emoji + + def to_dict(self) -> WelcomeScreenChannelPayload: + dict_: WelcomeScreenChannelPayload = { + 'channel_id': self.channel.id, + 'description': self.description, + 'emoji_id': None, + 'emoji_name': None, + } + + if isinstance(self.emoji, (PartialEmoji, Emoji)): + # custom guild emoji + dict_['emoji_id'] = self.emoji.id # type: ignore + dict_['emoji_name'] = self.emoji.name # type: ignore + else: + # unicode emoji or None + dict_['emoji_name'] = self.emoji + dict_['emoji_id'] = None # type: ignore + + return dict_ + + + @classmethod + def _from_dict(cls, data: WelcomeScreenChannelPayload, guild: Guild) -> WelcomeChannel: + channel_id = _get_as_snowflake(data, 'channel_id') + channel = guild.get_channel(channel_id) + description = data['description'] + _emoji_id = _get_as_snowflake(data, 'emoji_id') + _emoji_name = data['emoji_name'] + + if _emoji_id: + # custom guild emoji + emoji = get(guild.emojis, id=_emoji_id) + else: + # unicode emoji or None + emoji = _emoji_name + + return cls(channel=channel, description=description, emoji=emoji) # type: ignore + + + +class WelcomeScreen: + """Represents the welcome screen of a guild. + + .. versionadded:: 2.0 + + Attributes + ---------- + + description: :class:`str` + The description text displayed on the welcome screen. + welcome_channels: List[:class:`WelcomeScreenChannel`] + A list of channels displayed on welcome screen. + """ + __slots__ = ('description', 'welcome_channels') + + def __init__(self, data: WelcomeScreenPayload, guild: Guild): + self._guild = guild + self._update(data) + + def _update(self, data: WelcomeScreenPayload): + self.description: str = data.get('description') + self.welcome_channels: List[WelcomeScreenChannel] = [WelcomeScreenChannel._from_dict(channel) for channel in data.get('welcome_channels', [])] + + + @property + def enabled(self) -> bool: + """:class:`bool`: Indicates whether the welcome screen is enabled or not.""" + return 'WELCOME_SCREEN_ENABLED' in self._guild.features + + @property + def guild(self) -> Guild: + """:class:`Guild`: The guild this welcome screen belongs to.""" + return self._guild + From 5dd2ab3b97550b447c2bca6fdb121a94819cc4d4 Mon Sep 17 00:00:00 2001 From: Izhar Ahmad <54180221+nerdguyahmad@users.noreply.github.com> Date: Thu, 9 Sep 2021 11:29:45 +0500 Subject: [PATCH 35/49] [welcome_screen] Add relative import for welcome_screen --- discord/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/discord/__init__.py b/discord/__init__.py index 10e35b3101..2ace62c380 100644 --- a/discord/__init__.py +++ b/discord/__init__.py @@ -61,6 +61,7 @@ from .threads import * from .bot import * from .app import * +from .welcome_screen import * class VersionInfo(NamedTuple): From d874769360cda146faa1c0f76c569c3a060cce24 Mon Sep 17 00:00:00 2001 From: NerdGuyAhmad Date: Thu, 9 Sep 2021 12:29:31 +0500 Subject: [PATCH 36/49] [guild] Add support for getting welcome screen --- discord/guild.py | 28 ++++++++++++++++++++++++++++ discord/http.py | 5 +++++ 2 files changed, 33 insertions(+) diff --git a/discord/guild.py b/discord/guild.py index 41545f773b..c564c2d3f8 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -76,6 +76,7 @@ from .threads import Thread, ThreadMember from .sticker import GuildSticker from .file import File +from .welcome_screen import WelcomeScreen __all__ = ( @@ -2942,3 +2943,30 @@ async def change_voice_state( ws = self._state._get_websocket(self.id) channel_id = channel.id if channel else None await ws.voice_state(self.id, channel_id, self_mute, self_deaf) + + async def welcome_screen(self): + """|coro| + + Returns the :class:`WelcomeScreen` of the guild. + + The guild must have ``COMMUNITY`` in :attr:`~Guild.features`. + + You must have the :attr:`~Permissions.manage_guild` permission in order to get this. + + .. versionadded:: 2.0 + + Raises + ------- + Forbidden + You do not have the proper permissions to get this. + HTTPException + Retrieving the welcome screen failed somehow. + + Returns + -------- + :class:`WelcomeScreen` + The welcome screen of guild. + """ + data = await self._state.http.get_welcome_screen(self.id) + return WelcomeScreen(data=data, guild=self) + diff --git a/discord/http.py b/discord/http.py index 7a4c2adced..95381e734b 100644 --- a/discord/http.py +++ b/discord/http.py @@ -1490,6 +1490,11 @@ def delete_channel_permissions( ) -> Response[None]: r = Route('DELETE', '/channels/{channel_id}/permissions/{target}', channel_id=channel_id, target=target) return self.request(r, reason=reason) + + # Welcome Screen + + def get_welcome_screen(self, guild_id: Snowflake) -> Response[welcome_screen.WelcomeScreen]: + return self.request(Route('GET', '/guilds/{guild_id}/welcome-screen', guild_id=guild_id)) # Voice management From 74b8cd25878ba74a58c9e723f6813435acb7daef Mon Sep 17 00:00:00 2001 From: NerdGuyAhmad Date: Thu, 9 Sep 2021 12:30:07 +0500 Subject: [PATCH 37/49] Fixed potentjal NameErrors and AttributeErrors --- discord/welcome_screen.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/discord/welcome_screen.py b/discord/welcome_screen.py index dc2d68c3f5..1cf972819c 100644 --- a/discord/welcome_screen.py +++ b/discord/welcome_screen.py @@ -61,6 +61,9 @@ def __init__(self, channel: Snowflake, description: str, emoji: Union[Emoji, Par self.channel = channel self.description = description self.emoji = emoji + + def __repr__(self): + return f'WelcomeScreenChannel(channel={self.channel} description={self.description})' def to_dict(self) -> WelcomeScreenChannelPayload: dict_: WelcomeScreenChannelPayload = { @@ -86,9 +89,9 @@ def to_dict(self) -> WelcomeScreenChannelPayload: def _from_dict(cls, data: WelcomeScreenChannelPayload, guild: Guild) -> WelcomeChannel: channel_id = _get_as_snowflake(data, 'channel_id') channel = guild.get_channel(channel_id) - description = data['description'] + description = data.get('description') _emoji_id = _get_as_snowflake(data, 'emoji_id') - _emoji_name = data['emoji_name'] + _emoji_name = data.get('emoji_name') if _emoji_id: # custom guild emoji @@ -114,15 +117,17 @@ class WelcomeScreen: welcome_channels: List[:class:`WelcomeScreenChannel`] A list of channels displayed on welcome screen. """ - __slots__ = ('description', 'welcome_channels') - + def __init__(self, data: WelcomeScreenPayload, guild: Guild): self._guild = guild self._update(data) + def __repr__(self): + return f' Date: Thu, 9 Sep 2021 13:07:22 +0500 Subject: [PATCH 38/49] Add support for editing welcome screen --- discord/http.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/discord/http.py b/discord/http.py index 95381e734b..74888ee716 100644 --- a/discord/http.py +++ b/discord/http.py @@ -1496,6 +1496,17 @@ def delete_channel_permissions( def get_welcome_screen(self, guild_id: Snowflake) -> Response[welcome_screen.WelcomeScreen]: return self.request(Route('GET', '/guilds/{guild_id}/welcome-screen', guild_id=guild_id)) + def edit_welcome_screen(self, guild_id: Snowflake, payload: Any) -> Response[welcome_screen.WelcomeScreen]: + keys = ( + 'description', + 'welcome_channels', + 'enabled', + ) + payload = { + key: val for key, val in payload.items() if key in keys + } + return self.request(Route('PATCH', '/guilds/{guild_id}/welcome-screen', guild_id=guild_id), json=payload) + # Voice management def move_member( From c78b0d32b84a5ed59e9ab6a36ff863e6cff33956 Mon Sep 17 00:00:00 2001 From: NerdGuyAhmad Date: Thu, 9 Sep 2021 13:07:48 +0500 Subject: [PATCH 39/49] Fix NameError --- discord/welcome_screen.py | 62 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/discord/welcome_screen.py b/discord/welcome_screen.py index 1cf972819c..8b42674aad 100644 --- a/discord/welcome_screen.py +++ b/discord/welcome_screen.py @@ -26,6 +26,7 @@ from typing import TYPE_CHECKING, List, Union from .utils import _get_as_snowflake, get +from .partial_emoji import _EmojiTag if TYPE_CHECKING: from .types.welcome_screen import ( @@ -73,7 +74,7 @@ def to_dict(self) -> WelcomeScreenChannelPayload: 'emoji_name': None, } - if isinstance(self.emoji, (PartialEmoji, Emoji)): + if isinstance(self.emoji, _EmojiTag): # custom guild emoji dict_['emoji_id'] = self.emoji.id # type: ignore dict_['emoji_name'] = self.emoji.name # type: ignore @@ -140,3 +141,62 @@ def guild(self) -> Guild: """:class:`Guild`: The guild this welcome screen belongs to.""" return self._guild + async def edit(self, **options): + """|coro| + + Edits the welcome screen. + + You must have the :attr:`~Permissions.manage_guild` permission in the + guild to do this. + + Usage: :: + rules_channel = guild.get_channel(12345678) + announcements_channel = guild.get_channel(87654321) + custom_emoji = utils.get(guild.emojis, name='loudspeaker') + await welcome_screen.edit( + description='This is a very cool community server!', + welcome_channels=[ + WelcomeChannel(channel=rules_channel, description='Read the rules!', emoji='👨‍🏫'), + WelcomeChannel(channel=announcements_channel, description='Watch out for announcements!', emoji=custom_emoji), + ] + ) + + .. note:: + Welcome channels can only accept custom emojis if :attr:`~Guild.premium_tier` is level 2 or above. + + Parameters + ------------ + + description: Optional[:class:`str`] + The new description of welcome screen. + welcome_channels: Optional[List[:class:`WelcomeChannel`]] + The welcome channels. The order of the channels would be same as the passed list order. + enabled: Optional[:class:`bool`] + Whether the welcome screen should be displayed. + + Raises + ------- + + HTTPException + Editing the welcome screen failed somehow. + Forbidden + You don't have permissions to edit the welcome screen. + NotFound + This welcome screen does not exist. + + """ + + welcome_channels = options.get('welcome_channels', []) + welcome_channels_data = [] + + for channel in welcome_channels: + if not isinstance(channel, WelcomeScreenChannel): + raise InvalidArgument('welcome_channels parameter must be a list of WelcomeChannel.') + + welcome_channels_data.append(channel.to_dict()) + + options['welcome_channels'] = welcome_channels_data + + if options: + new = await self._guild._state.http.edit_welcome_screen(self._guild.id, options) + self._update(new) \ No newline at end of file From 5e3525383ecc1034f6e6c728a2cf4d411970fda9 Mon Sep 17 00:00:00 2001 From: NerdGuyAhmad Date: Thu, 9 Sep 2021 13:20:18 +0500 Subject: [PATCH 40/49] Add shorthand for WelcomeScreen.edit() --- discord/guild.py | 49 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/discord/guild.py b/discord/guild.py index c564c2d3f8..e6b95488a0 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -76,7 +76,7 @@ from .threads import Thread, ThreadMember from .sticker import GuildSticker from .file import File -from .welcome_screen import WelcomeScreen +from .welcome_screen import WelcomeScreen, WelcomeScreenChannel __all__ = ( @@ -2969,4 +2969,51 @@ async def welcome_screen(self): """ data = await self._state.http.get_welcome_screen(self.id) return WelcomeScreen(data=data, guild=self) + + async def edit_welcome_screen(self, **options): + """|coro| + + A shorthand for :attr:`WelcomeScreen.edit` without fetching the welcome screen. + + You must have the :attr:`~Permissions.manage_guild` permission in the + guild to do this. + + The guild must have ``COMMUNITY`` in :attr:`Guild.features` + + Parameters + ------------ + + description: Optional[:class:`str`] + The new description of welcome screen. + welcome_channels: Optional[List[:class:`WelcomeChannel`]] + The welcome channels. The order of the channels would be same as the passed list order. + enabled: Optional[:class:`bool`] + Whether the welcome screen should be displayed. + + Raises + ------- + + HTTPException + Editing the welcome screen failed somehow. + Forbidden + You don't have permissions to edit the welcome screen. + NotFound + This welcome screen does not exist. + + """ + + welcome_channels = options.get('welcome_channels', []) + welcome_channels_data = [] + + for channel in welcome_channels: + if not isinstance(channel, WelcomeScreenChannel): + raise InvalidArgument('welcome_channels parameter must be a list of WelcomeScreenChannel.') + + welcome_channels_data.append(channel.to_dict()) + + options['welcome_channels'] = welcome_channels_data + + if options: + new = await self._state.http.edit_welcome_screen(self.id, options) + return WelcomeScreen(data=new, guild=self) From 5c28ce9aff7206678848f14d3a164e53cd5588f9 Mon Sep 17 00:00:00 2001 From: NerdGuyAhmad Date: Thu, 9 Sep 2021 13:20:32 +0500 Subject: [PATCH 41/49] fix: typo --- discord/welcome_screen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/welcome_screen.py b/discord/welcome_screen.py index 8b42674aad..6ca8d75aac 100644 --- a/discord/welcome_screen.py +++ b/discord/welcome_screen.py @@ -191,7 +191,7 @@ async def edit(self, **options): for channel in welcome_channels: if not isinstance(channel, WelcomeScreenChannel): - raise InvalidArgument('welcome_channels parameter must be a list of WelcomeChannel.') + raise InvalidArgument('welcome_channels parameter must be a list of WelcomeScreenChannel.') welcome_channels_data.append(channel.to_dict()) From b60ff112f873069fac0a27cab9dc5c250a493056 Mon Sep 17 00:00:00 2001 From: NerdGuyAhmad Date: Thu, 9 Sep 2021 13:25:10 +0500 Subject: [PATCH 42/49] Return updated instances on edit --- discord/guild.py | 5 +++++ discord/welcome_screen.py | 11 +++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/discord/guild.py b/discord/guild.py index e6b95488a0..1834ca5f5f 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -3000,6 +3000,11 @@ async def edit_welcome_screen(self, **options): NotFound This welcome screen does not exist. + Returns + -------- + + :class:`WelcomeScreen` + The edited welcome screen. """ welcome_channels = options.get('welcome_channels', []) diff --git a/discord/welcome_screen.py b/discord/welcome_screen.py index 6ca8d75aac..ee0aea4a32 100644 --- a/discord/welcome_screen.py +++ b/discord/welcome_screen.py @@ -24,7 +24,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, List, Union +from typing import TYPE_CHECKING, List, Union, overload from .utils import _get_as_snowflake, get from .partial_emoji import _EmojiTag @@ -184,6 +184,11 @@ async def edit(self, **options): NotFound This welcome screen does not exist. + Returns + -------- + + :class:`WelcomeScreen` + The updated welcome screen. """ welcome_channels = options.get('welcome_channels', []) @@ -199,4 +204,6 @@ async def edit(self, **options): if options: new = await self._guild._state.http.edit_welcome_screen(self._guild.id, options) - self._update(new) \ No newline at end of file + self._update(new) + + return self \ No newline at end of file From 996c0f89b0d2b3ddd79154907da1c4d52f63b33a Mon Sep 17 00:00:00 2001 From: NerdGuyAhmad Date: Thu, 9 Sep 2021 13:47:11 +0500 Subject: [PATCH 43/49] Typehint WelcomeScreen --- discord/guild.py | 18 +++++++++++++++++- discord/welcome_screen.py | 22 ++++++++++++++++------ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/discord/guild.py b/discord/guild.py index 1834ca5f5f..abafcd89ae 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -2969,6 +2969,22 @@ async def welcome_screen(self): """ data = await self._state.http.get_welcome_screen(self.id) return WelcomeScreen(data=data, guild=self) + + + @overload + async def edit_welcome_screen( + self, + *, + description: Optional[str] = ..., + welcome_channels: Optional[List[WelcomeChannel]] = ..., + enabled: Optional[bool] = ..., + ) -> WelcomeScreen: + ... + + @overload + async def edit_welcome_screen(self) -> None: + ... + async def edit_welcome_screen(self, **options): """|coro| @@ -3012,7 +3028,7 @@ async def edit_welcome_screen(self, **options): for channel in welcome_channels: if not isinstance(channel, WelcomeScreenChannel): - raise InvalidArgument('welcome_channels parameter must be a list of WelcomeScreenChannel.') + raise TypeError('welcome_channels parameter must be a list of WelcomeScreenChannel.') welcome_channels_data.append(channel.to_dict()) diff --git a/discord/welcome_screen.py b/discord/welcome_screen.py index ee0aea4a32..9f11ccc0c9 100644 --- a/discord/welcome_screen.py +++ b/discord/welcome_screen.py @@ -140,6 +140,21 @@ def enabled(self) -> bool: def guild(self) -> Guild: """:class:`Guild`: The guild this welcome screen belongs to.""" return self._guild + + + @overload + async def edit( + self, + *, + description: Optional[str] = ..., + welcome_channels: Optional[List[WelcomeChannel]] = ..., + enabled: Optional[bool] = ..., + ) -> None: + ... + + @overload + async def edit(self) -> None: + ... async def edit(self, **options): """|coro| @@ -184,11 +199,6 @@ async def edit(self, **options): NotFound This welcome screen does not exist. - Returns - -------- - - :class:`WelcomeScreen` - The updated welcome screen. """ welcome_channels = options.get('welcome_channels', []) @@ -196,7 +206,7 @@ async def edit(self, **options): for channel in welcome_channels: if not isinstance(channel, WelcomeScreenChannel): - raise InvalidArgument('welcome_channels parameter must be a list of WelcomeScreenChannel.') + raise TypeError('welcome_channels parameter must be a list of WelcomeScreenChannel.') welcome_channels_data.append(channel.to_dict()) From e3e0e149791e8076cff5d723ee38053a8d4fb78c Mon Sep 17 00:00:00 2001 From: NerdGuyAhmad Date: Thu, 9 Sep 2021 13:49:13 +0500 Subject: [PATCH 44/49] Document welcome screen --- docs/api.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/api.rst b/docs/api.rst index 6f0fdb942b..7f4939eeb4 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -3831,6 +3831,22 @@ Template .. autoclass:: Template() :members: + +WelcomeScreen +~~~~~~~~~~~~~~~ + +.. attributetable:: WelcomeScreen + +.. autoclass:: WelcomeScreen() + :members: + +WelcomeScreenChannel +~~~~~~~~~~~~~~~ + +.. attributetable:: WelcomeScreenChannel + +.. autoclass:: WelcomeScreenChannel() + :members: WidgetChannel ~~~~~~~~~~~~~~~ From 6439c2f61ed887124ba2b8e6dcc2796f6e202ad5 Mon Sep 17 00:00:00 2001 From: NerdGuyAhmad Date: Thu, 9 Sep 2021 15:07:06 +0500 Subject: [PATCH 45/49] Add NotFound detail --- discord/guild.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/discord/guild.py b/discord/guild.py index abafcd89ae..35a73e1dfe 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -2961,6 +2961,9 @@ async def welcome_screen(self): You do not have the proper permissions to get this. HTTPException Retrieving the welcome screen failed somehow. + NotFound + The guild doesn't has a welcome screen or community feature is disabled. + Returns -------- From 310c09552be0b7f9aa5c18f1a73867eb5c754f41 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Sep 2021 17:26:34 +0530 Subject: [PATCH 46/49] Make get_or_fetch return None instead of raising Exception --- discord/app/commands.py | 10 +++++----- discord/utils.py | 9 ++++++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/discord/app/commands.py b/discord/app/commands.py index a0d54ebfd8..5bbfaf6cc4 100644 --- a/discord/app/commands.py +++ b/discord/app/commands.py @@ -405,13 +405,13 @@ async def _invoke(self, ctx: ApplicationContext) -> None: <= SlashCommandOptionType.role.value ): name = "member" if op.input_type.name == "user" else op.input_type.name - arg = await get_or_fetch(ctx.guild, name, int(arg)) + arg = await get_or_fetch(ctx.guild, name, int(arg), default=int(arg)) elif op.input_type == SlashCommandOptionType.mentionable: - try: - arg = await get_or_fetch(ctx.guild, "member", int(arg)) - except NotFound: - arg = await get_or_fetch(ctx.guild, "role", int(arg)) + arg_id = int(arg) + arg = await get_or_fetch(ctx.guild, "member", arg_id) + if arg is None: + arg = ctx.guild.get_role(arg_id) or arg_id kwargs[op.name] = arg diff --git a/discord/utils.py b/discord/utils.py index eef27ced61..61b3b6ece8 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -61,7 +61,7 @@ import types import warnings -from .errors import InvalidArgument +from .errors import InvalidArgument, NotFound try: import orjson @@ -448,11 +448,14 @@ def get(iterable: Iterable[T], **attrs: Any) -> Optional[T]: return elem return None -async def get_or_fetch(obj, attr: str, id: int): +async def get_or_fetch(obj, attr: str, id: int, *, default: Any = None): # TODO: Document this getter = getattr(obj, f'get_{attr}')(id) if getter is None: - getter = await getattr(obj, f'fetch_{attr}')(id) + try: + getter = await getattr(obj, f'fetch_{attr}')(id) + except NotFound: + return default return getter def _unique(iterable: Iterable[T]) -> List[T]: From e5099e173abc3c10298bebf49b5b8c75286dbfea Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Sep 2021 17:29:12 +0530 Subject: [PATCH 47/49] Fix incorrect error --- discord/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index 61b3b6ece8..3479914ed3 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -61,7 +61,7 @@ import types import warnings -from .errors import InvalidArgument, NotFound +from .errors import InvalidArgument, HTTPException try: import orjson @@ -454,7 +454,7 @@ async def get_or_fetch(obj, attr: str, id: int, *, default: Any = None): if getter is None: try: getter = await getattr(obj, f'fetch_{attr}')(id) - except NotFound: + except HTTPException: return default return getter From 5d4e7a2feeebf073165278c1ebb17cbc88f4deb6 Mon Sep 17 00:00:00 2001 From: BobDotCom <71356958+BobDotCom@users.noreply.github.com> Date: Thu, 9 Sep 2021 09:12:12 -0500 Subject: [PATCH 48/49] Update CONTRIBUTING.md --- .github/CONTRIBUTING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index e6b9ad0ce4..6175ece47f 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -34,6 +34,10 @@ If the bug report is missing this information then it'll take us longer to fix t Submitting a pull request is fairly simple, just make sure it focuses on a single aspect and doesn't manage to have scope creep and it's probably good to go. It would be incredibly lovely if the style is consistent to that found in the project. This project follows PEP-8 guidelines (mostly) with a column limit of 125. +### Licensing + +By submitting a pull request, you agree that; 1) You hold the copyright on all submitted code inside said pull request; 2) You agree to transfer all rights to the owner of this repository, and; 3) If you are found to be in fault with any of the above, we shall not be held responsible in any way after the pull request has been merged. + ### Git Commit Guidelines - Use present tense (e.g. "Add feature" not "Added feature") From 7bf8ff6991fd840a970f49d51385bb5e6fb16dc5 Mon Sep 17 00:00:00 2001 From: BobDotCom <71356958+BobDotCom@users.noreply.github.com> Date: Thu, 9 Sep 2021 09:18:40 -0500 Subject: [PATCH 49/49] change InteractionContext to ApplicationContext --- discord/abc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/abc.py b/discord/abc.py index 7c97544924..ba5984e57a 100644 --- a/discord/abc.py +++ b/discord/abc.py @@ -1142,7 +1142,7 @@ class Messageable: - :class:`~discord.Member` - :class:`~discord.ext.commands.Context` - :class:`~discord.Thread` - - :class:`~discord.InteractionContext` + - :class:`~discord.ApplicationContext` """ __slots__ = () @@ -1692,4 +1692,4 @@ async def connect( class Mentionable: # TODO: documentation, methods if needed - pass \ No newline at end of file + pass