From fb035395b45d753589638181cda299aa65f4fd15 Mon Sep 17 00:00:00 2001 From: yashpaliwal-bst Date: Wed, 17 Apr 2024 17:18:09 +0530 Subject: [PATCH 01/14] added ratelimit logs --- discord/http.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/discord/http.py b/discord/http.py index 30184b665a..7f0721f375 100644 --- a/discord/http.py +++ b/discord/http.py @@ -298,6 +298,8 @@ async def request( remaining = response.headers.get("X-Ratelimit-Remaining") if remaining == "0" and response.status != 429: # we've depleted our current bucket + if response.status != 200 or response.status != 204: + print(f"RatelimitReached: status: {response.status}, url: {url}, method: {method}") delta = utils._parse_ratelimit_header( response, use_clock=self.use_clock ) @@ -319,6 +321,7 @@ async def request( # we are being rate limited if response.status == 429: + print(f"APIRatelimitError: status: {response.status}, url: {url}, method: {method}") if not response.headers.get("Via") or isinstance(data, str): # Banned by Cloudflare more than likely. raise HTTPException(response, data) @@ -335,6 +338,7 @@ async def request( # check if it's a global rate limit is_global = data.get("global", False) if is_global: + print(f"GlobalRateLimitReached: status: {response.status}, url: {url}, method: {method}") _log.warning( ( "Global rate limit has been hit. Retrying in" @@ -361,6 +365,7 @@ async def request( continue # the usual error cases + print(f"APIError: status: {response.status}, url: {url}, method: {method}") if response.status == 403: raise Forbidden(response, data) elif response.status == 404: From cfb48048fb8f95b9b2ba68566d71fb2dc960d21c Mon Sep 17 00:00:00 2001 From: yashpaliwal-bst Date: Mon, 13 May 2024 17:30:43 +0530 Subject: [PATCH 02/14] added method for soundboard perms --- discord/permissions.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/discord/permissions.py b/discord/permissions.py index e94e6c2116..a02e5609a0 100644 --- a/discord/permissions.py +++ b/discord/permissions.py @@ -610,6 +610,18 @@ def moderate_members(self) -> int: """ return 1 << 40 + @flag_value + def view_creator_monetization_analytics(self) -> int: + """:class:`bool`: Returns ``True`` if a user can view creator monetization analytics. + .. versionadded:: 2.4""" + return 1 << 41 + + @flag_value + def use_soundboard(self) -> int: + """:class:`bool`: Returns ``True`` if a user can use soundboard. + .. versionadded:: 2.4""" + return 1 << 42 + @flag_value def send_voice_messages(self) -> int: """:class:`bool`: Returns ``True`` if a member can send voice messages. @@ -736,6 +748,8 @@ class PermissionOverwrite: start_embedded_activities: bool | None moderate_members: bool | None send_voice_messages: bool | None + view_creator_monetization_analytics: bool | None + use_soundboard: bool | None def __init__(self, **kwargs: bool | None): self._values: dict[str, bool | None] = {} From 6ff189f427442f21d09f2979690a465473e514e6 Mon Sep 17 00:00:00 2001 From: yashpaliwal-bst Date: Mon, 13 May 2024 18:06:36 +0530 Subject: [PATCH 03/14] additional perms added --- discord/permissions.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/discord/permissions.py b/discord/permissions.py index a02e5609a0..99c9f8612c 100644 --- a/discord/permissions.py +++ b/discord/permissions.py @@ -622,6 +622,24 @@ def use_soundboard(self) -> int: .. versionadded:: 2.4""" return 1 << 42 + @flag_value + def create_guild_expressions(self) -> int: + """:class:`bool`: Returns ``True`` if a user can create emoji, stickers, and soundboard sounds. + .. versionadded:: 2.4""" + return 1 << 43 + + @flag_value + def create_events(self) -> int: + """:class:`bool`: Returns ``True`` if a user can create and edit events. + .. versionadded:: 2.4""" + return 1 << 44 + + @flag_value + def use_external_sounds(self) -> int: + """:class:`bool`: Returns ``True`` if a user can use custom soundboard sounds. + .. versionadded:: 2.4""" + return 1 << 45 + @flag_value def send_voice_messages(self) -> int: """:class:`bool`: Returns ``True`` if a member can send voice messages. @@ -630,6 +648,12 @@ def send_voice_messages(self) -> int: """ return 1 << 46 + @flag_value + def send_polls(self) -> int: + """:class:`bool`: Returns ``True`` if a user can send polls. + .. versionadded:: 2.4""" + return 1 << 49 + PO = TypeVar("PO", bound="PermissionOverwrite") @@ -750,6 +774,10 @@ class PermissionOverwrite: send_voice_messages: bool | None view_creator_monetization_analytics: bool | None use_soundboard: bool | None + create_guild_expressions: bool | None + create_events: bool | None + use_external_sounds: bool | None + send_polls: bool | None def __init__(self, **kwargs: bool | None): self._values: dict[str, bool | None] = {} From bbbe134e57a7d54bebc7d972196c2f7f9d486a13 Mon Sep 17 00:00:00 2001 From: yashpaliwal-bst Date: Mon, 13 May 2024 18:26:01 +0530 Subject: [PATCH 04/14] removed unused aliases --- discord/permissions.py | 34 ++++------------------------------ 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/discord/permissions.py b/discord/permissions.py index 99c9f8612c..70d380b7e5 100644 --- a/discord/permissions.py +++ b/discord/permissions.py @@ -371,14 +371,6 @@ def view_channel(self) -> int: """:class:`bool`: Returns ``True`` if a user can view all or specific channels.""" return 1 << 10 - @make_permission_alias("view_channel") - def read_messages(self) -> int: - """:class:`bool`: An alias for :attr:`view_channel`. - - .. versionadded:: 1.3 - """ - return 1 << 10 - @flag_value def send_messages(self) -> int: """:class:`bool`: Returns ``True`` if a user can send messages from all or specific text channels.""" @@ -420,16 +412,8 @@ def mention_everyone(self) -> int: return 1 << 17 @flag_value - def external_emojis(self) -> int: - """:class:`bool`: Returns ``True`` if a user can use emojis from other guilds.""" - return 1 << 18 - - @make_permission_alias("external_emojis") def use_external_emojis(self) -> int: - """:class:`bool`: An alias for :attr:`external_emojis`. - - .. versionadded:: 1.3 - """ + """:class:`bool`: Returns ``True`` if a user can use emojis from other guilds.""" return 1 << 18 @flag_value @@ -571,20 +555,13 @@ def create_private_threads(self) -> int: return 1 << 36 @flag_value - def external_stickers(self) -> int: + def use_external_stickers(self) -> int: """:class:`bool`: Returns ``True`` if a user can use stickers from other guilds. .. versionadded:: 2.0 """ return 1 << 37 - @make_permission_alias("external_stickers") - def use_external_stickers(self) -> int: - """:class:`bool`: An alias for :attr:`external_stickers`. - - .. versionadded:: 2.0 - """ - return 1 << 37 @flag_value def send_messages_in_threads(self) -> int: @@ -595,7 +572,7 @@ def send_messages_in_threads(self) -> int: return 1 << 38 @flag_value - def start_embedded_activities(self) -> int: + def use_embedded_activities(self) -> int: """:class:`bool`: Returns ``True`` if a user can launch an activity flagged 'EMBEDDED' in a voice channel. .. versionadded:: 2.0 @@ -735,7 +712,6 @@ class PermissionOverwrite: view_audit_log: bool | None priority_speaker: bool | None stream: bool | None - read_messages: bool | None view_channel: bool | None send_messages: bool | None send_tts_messages: bool | None @@ -744,7 +720,6 @@ class PermissionOverwrite: attach_files: bool | None read_message_history: bool | None mention_everyone: bool | None - external_emojis: bool | None use_external_emojis: bool | None view_guild_insights: bool | None connect: bool | None @@ -767,9 +742,8 @@ class PermissionOverwrite: create_public_threads: bool | None create_private_threads: bool | None send_messages_in_threads: bool | None - external_stickers: bool | None use_external_stickers: bool | None - start_embedded_activities: bool | None + use_embedded_activities: bool | None moderate_members: bool | None send_voice_messages: bool | None view_creator_monetization_analytics: bool | None From 8e9965880d3d2bf9492bb68eaeb78cfd7368e074 Mon Sep 17 00:00:00 2001 From: yashpaliwal-bst Date: Mon, 13 May 2024 18:40:45 +0530 Subject: [PATCH 05/14] alias changes --- discord/permissions.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/discord/permissions.py b/discord/permissions.py index 70d380b7e5..dd959c890c 100644 --- a/discord/permissions.py +++ b/discord/permissions.py @@ -371,6 +371,13 @@ def view_channel(self) -> int: """:class:`bool`: Returns ``True`` if a user can view all or specific channels.""" return 1 << 10 + @make_permission_alias("view_channel") + def read_messages(self) -> int: + """:class:`bool`: An alias for :attr:`view_channel`. + .. versionadded:: 1.3 + """ + return 1 << 10 + @flag_value def send_messages(self) -> int: """:class:`bool`: Returns ``True`` if a user can send messages from all or specific text channels.""" @@ -416,6 +423,13 @@ def use_external_emojis(self) -> int: """:class:`bool`: Returns ``True`` if a user can use emojis from other guilds.""" return 1 << 18 + @make_permission_alias("use_external_emojis") + def external_emojis(self) -> int: + """:class:`bool`: An alias for :attr:`use_external_emojis`. + .. versionadded:: 1.3 + """ + return 1 << 18 + @flag_value def view_guild_insights(self) -> int: """:class:`bool`: Returns ``True`` if a user can view the guild's insights. @@ -562,6 +576,13 @@ def use_external_stickers(self) -> int: """ return 1 << 37 + @make_permission_alias("use_external_stickers") + def external_stickers(self) -> int: + """:class:`bool`: An alias for :attr:`use_external_stickers`. + .. versionadded:: 2.0 + """ + return 1 << 37 + @flag_value def send_messages_in_threads(self) -> int: @@ -579,6 +600,13 @@ def use_embedded_activities(self) -> int: """ return 1 << 39 + @make_permission_alias("use_embedded_activities") + def start_embedded_activities(self) -> int: + """:class:`bool`: An alias for :attr:`use_embedded_activities`. + .. versionadded:: 2.0 + """ + return 1 << 39 + @flag_value def moderate_members(self) -> int: """:class:`bool`: Returns ``True`` if a user can moderate members (timeout). @@ -712,6 +740,7 @@ class PermissionOverwrite: view_audit_log: bool | None priority_speaker: bool | None stream: bool | None + read_messages: bool | None view_channel: bool | None send_messages: bool | None send_tts_messages: bool | None @@ -720,6 +749,7 @@ class PermissionOverwrite: attach_files: bool | None read_message_history: bool | None mention_everyone: bool | None + external_emojis: bool | None use_external_emojis: bool | None view_guild_insights: bool | None connect: bool | None @@ -743,7 +773,9 @@ class PermissionOverwrite: create_private_threads: bool | None send_messages_in_threads: bool | None use_external_stickers: bool | None + external_stickers: bool | None use_embedded_activities: bool | None + start_embedded_activities: bool | None moderate_members: bool | None send_voice_messages: bool | None view_creator_monetization_analytics: bool | None From 5ca7fd6ee13e6124b695dda42b6ab7de3f83a7c6 Mon Sep 17 00:00:00 2001 From: AMIT YADAV Date: Fri, 14 Jun 2024 15:33:15 +0530 Subject: [PATCH 06/14] added ratelimit header logs --- discord/http.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/discord/http.py b/discord/http.py index 7f0721f375..30efe889c8 100644 --- a/discord/http.py +++ b/discord/http.py @@ -299,7 +299,12 @@ async def request( if remaining == "0" and response.status != 429: # we've depleted our current bucket if response.status != 200 or response.status != 204: - print(f"RatelimitReached: status: {response.status}, url: {url}, method: {method}") + print(f"RatelimitReached: status: {response.status}, url: {url}, method: {method}, " + f"x-ratelimit-bucket: {response.headers.get('x-ratelimit-bucket')}," + f"x-ratelimit-limit: {response.headers.get('x-ratelimit-limit')}, " + f"x-ratelimit-remaining: {response.headers.get('x-ratelimit-remaining')}, " + f"x-ratelimit-reset: {response.headers.get('x-ratelimit-reset')}, " + f"x-ratelimit-reset-after: {response.headers.get('x-ratelimit-reset-after')}") delta = utils._parse_ratelimit_header( response, use_clock=self.use_clock ) @@ -321,7 +326,13 @@ async def request( # we are being rate limited if response.status == 429: - print(f"APIRatelimitError: status: {response.status}, url: {url}, method: {method}") + print(f"APIRatelimitError: status: {response.status}, url: {url}, method: {method}, " + f"x-ratelimit-bucket: {response.headers.get('x-ratelimit-bucket')}, " + f"x-ratelimit-limit: {response.headers.get('x-ratelimit-limit')}," + f"x-ratelimit-remaining: {response.headers.get('x-ratelimit-remaining')}," + f"x-ratelimit-reset: {response.headers.get('x-ratelimit-reset')}, " + f"x-ratelimit-reset-after: {response.headers.get('x-ratelimit-reset-after')}, " + f"is_global_ratelimit: {data.get('global', False)}") if not response.headers.get("Via") or isinstance(data, str): # Banned by Cloudflare more than likely. raise HTTPException(response, data) @@ -365,7 +376,12 @@ async def request( continue # the usual error cases - print(f"APIError: status: {response.status}, url: {url}, method: {method}") + print(f"APIError: status: {response.status}, url: {url}, method: {method}," + f"x-ratelimit-bucket: {response.headers.get('x-ratelimit-bucket')}," + f"x-ratelimit-limit: {response.headers.get('x-ratelimit-limit')}," + f"x-ratelimit-remaining: {response.headers.get('x-ratelimit-remaining')}," + f"x-ratelimit-reset: {response.headers.get('x-ratelimit-reset')}, " + f"x-ratelimit-reset-after: {response.headers.get('x-ratelimit-reset-after')}") if response.status == 403: raise Forbidden(response, data) elif response.status == 404: From e6c3f0d450e6d355b8d55c39973f76f94ce4ff6b Mon Sep 17 00:00:00 2001 From: AMIT YADAV Date: Fri, 14 Jun 2024 17:22:25 +0530 Subject: [PATCH 07/14] Added CF-RAY to header logs --- discord/http.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/discord/http.py b/discord/http.py index 30efe889c8..7e707bb98d 100644 --- a/discord/http.py +++ b/discord/http.py @@ -304,6 +304,7 @@ async def request( f"x-ratelimit-limit: {response.headers.get('x-ratelimit-limit')}, " f"x-ratelimit-remaining: {response.headers.get('x-ratelimit-remaining')}, " f"x-ratelimit-reset: {response.headers.get('x-ratelimit-reset')}, " + f"CF-RAY: {response.headers.get('CF-RAY')}, " f"x-ratelimit-reset-after: {response.headers.get('x-ratelimit-reset-after')}") delta = utils._parse_ratelimit_header( response, use_clock=self.use_clock @@ -332,6 +333,7 @@ async def request( f"x-ratelimit-remaining: {response.headers.get('x-ratelimit-remaining')}," f"x-ratelimit-reset: {response.headers.get('x-ratelimit-reset')}, " f"x-ratelimit-reset-after: {response.headers.get('x-ratelimit-reset-after')}, " + f"CF-RAY: {response.headers.get('CF-RAY')}, " f"is_global_ratelimit: {data.get('global', False)}") if not response.headers.get("Via") or isinstance(data, str): # Banned by Cloudflare more than likely. @@ -381,6 +383,7 @@ async def request( f"x-ratelimit-limit: {response.headers.get('x-ratelimit-limit')}," f"x-ratelimit-remaining: {response.headers.get('x-ratelimit-remaining')}," f"x-ratelimit-reset: {response.headers.get('x-ratelimit-reset')}, " + f"CF-RAY: {response.headers.get('CF-RAY')}, " f"x-ratelimit-reset-after: {response.headers.get('x-ratelimit-reset-after')}") if response.status == 403: raise Forbidden(response, data) From 05304d8a46e81ef90f27dd389e4caa1fbe1d5720 Mon Sep 17 00:00:00 2001 From: AMIT YADAV Date: Fri, 14 Jun 2024 20:50:53 +0530 Subject: [PATCH 08/14] added support for discord proxy --- discord/http.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/discord/http.py b/discord/http.py index 7e707bb98d..a1e51bc003 100644 --- a/discord/http.py +++ b/discord/http.py @@ -33,6 +33,7 @@ from urllib.parse import quote as _uriquote import aiohttp +import os from . import __version__, utils from .errors import ( @@ -123,6 +124,9 @@ def __init__(self, method: str, path: str, **parameters: Any) -> None: @property def base(self) -> str: + DISCORD_PROXY = bool(os.environ.get("DISCORD_PROXY", False)) + if DISCORD_PROXY: + return f"http://discord.com/api/v{API_VERSION}" return f"https://discord.com/api/v{API_VERSION}" @property From 2a647ab4037587a3d918a12ac1b3029f97151ed3 Mon Sep 17 00:00:00 2001 From: AMIT YADAV Date: Fri, 14 Jun 2024 21:04:59 +0530 Subject: [PATCH 09/14] added discord proxy url --- discord/http.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/http.py b/discord/http.py index a1e51bc003..87b07e9942 100644 --- a/discord/http.py +++ b/discord/http.py @@ -124,9 +124,9 @@ def __init__(self, method: str, path: str, **parameters: Any) -> None: @property def base(self) -> str: - DISCORD_PROXY = bool(os.environ.get("DISCORD_PROXY", False)) + DISCORD_PROXY = os.environ.get("DISCORD_PROXY", "") if DISCORD_PROXY: - return f"http://discord.com/api/v{API_VERSION}" + return f"http://{DISCORD_PROXY}/api/v{API_VERSION}" return f"https://discord.com/api/v{API_VERSION}" @property From 6a6921f2d1408e1b7aa95a74c509dbdfd95e560d Mon Sep 17 00:00:00 2001 From: Amit Yadav Date: Fri, 4 Oct 2024 16:51:33 +0530 Subject: [PATCH 10/14] entitlements parsers and dispatchers --- discord/state.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/discord/state.py b/discord/state.py index d222ba4518..6c40fb3339 100644 --- a/discord/state.py +++ b/discord/state.py @@ -665,6 +665,18 @@ def parse_auto_moderation_action_execution(self, data) -> None: event = AutoModActionExecutionEvent(self, data) self.dispatch("auto_moderation_action_execution", event) + def parse_entitlement_create(self, data) -> None: + # event = Entitlement(data=data, state=self) + self.dispatch("entitlement_create", data) + + def parse_entitlement_update(self, data) -> None: + # event = Entitlement(data=data, state=self) + self.dispatch("entitlement_update", data) + + def parse_entitlement_delete(self, data) -> None: + # event = Entitlement(data=data, state=self) + self.dispatch("entitlement_delete", data) + def parse_message_create(self, data) -> None: channel, _ = self._get_guild_channel(data) # channel would be the correct type here From a5122eeb04a90a350d87d7db2039d7c771fd113e Mon Sep 17 00:00:00 2001 From: yashpaliwal-bst Date: Tue, 8 Oct 2024 23:30:18 +0530 Subject: [PATCH 11/14] first set of updates regarding library --- discord/abc.py | 23 +++-- discord/enums.py | 21 +++++ discord/message.py | 195 +++++++++++++++++++++++++++++++++++++-- discord/types/embed.py | 9 +- discord/types/message.py | 24 +++++ 5 files changed, 256 insertions(+), 16 deletions(-) diff --git a/discord/abc.py b/discord/abc.py index ce811492bc..5d6c2ecc90 100644 --- a/discord/abc.py +++ b/discord/abc.py @@ -1489,8 +1489,8 @@ async def send( .. versionadded:: 1.4 reference: Union[:class:`~discord.Message`, :class:`~discord.MessageReference`, :class:`~discord.PartialMessage`] - A reference to the :class:`~discord.Message` to which you are replying, this can be created using - :meth:`~discord.Message.to_reference` or passed directly as a :class:`~discord.Message`. You can control + A reference to the :class:`~discord.Message` you are replying to or forwarding, this can be created using + :meth:`~discord.Message.to_reference` or passed directly as a :class:`~discord.Message`. When replying, you can control whether this mentions the author of the referenced message using the :attr:`~discord.AllowedMentions.replied_user` attribute of ``allowed_mentions`` or by setting ``mention_author``. @@ -1577,9 +1577,20 @@ async def send( allowed_mentions = allowed_mentions or AllowedMentions().to_dict() allowed_mentions["replied_user"] = bool(mention_author) + _reference = None + if reference is not None: try: - reference = reference.to_message_reference_dict() + _reference = reference.to_message_reference_dict() + from .message import MessageReference + + if not isinstance(reference, MessageReference): + utils.warn_deprecated( + f"Passing {type(reference).__name__} to reference", + "MessageReference", + "2.7", + "3.0", + ) except AttributeError: raise InvalidArgument( "reference parameter must be Message, MessageReference, or" @@ -1613,7 +1624,7 @@ async def send( embed=embed, embeds=embeds, nonce=nonce, - message_reference=reference, + message_reference=_reference, stickers=stickers, components=components, flags=flags, @@ -1639,7 +1650,7 @@ async def send( embeds=embeds, nonce=nonce, allowed_mentions=allowed_mentions, - message_reference=reference, + message_reference=_reference, stickers=stickers, components=components, flags=flags, @@ -1656,7 +1667,7 @@ async def send( embeds=embeds, nonce=nonce, allowed_mentions=allowed_mentions, - message_reference=reference, + message_reference=_reference, stickers=stickers, components=components, flags=flags, diff --git a/discord/enums.py b/discord/enums.py index f2bb6d3452..e3b3c16b46 100644 --- a/discord/enums.py +++ b/discord/enums.py @@ -68,6 +68,8 @@ "AutoModKeywordPresetType", "ApplicationRoleConnectionMetadataType", "ReactionType", + "PollLayoutType", + "MessageReferenceType", ) @@ -257,6 +259,12 @@ class MessageType(Enum): stage_raise_hand = 30 stage_topic = 31 guild_application_premium_subscription = 32 + guild_incident_alert_mode_enabled = 36 + guild_incident_alert_mode_disabled = 37 + guild_incident_report_raid = 38 + guild_incident_report_false_alarm = 39 + purchase_notification = 44 + poll_result = 46 class VoiceRegion(Enum): @@ -951,6 +959,19 @@ class ReactionType(Enum): normal = 0 burst = 1 +class PollLayoutType(Enum): + """The poll's layout type.""" + default = 1 + + +class MessageReferenceType(Enum): + """The message reference's type""" + + default = 0 + forward = 1 + + + T = TypeVar("T") diff --git a/discord/message.py b/discord/message.py index c74f40c288..99fcb62d54 100644 --- a/discord/message.py +++ b/discord/message.py @@ -44,7 +44,7 @@ from .components import _component_factory from .embeds import Embed from .emoji import Emoji -from .enums import ChannelType, MessageType, try_enum +from .enums import ChannelType, MessageReferenceType, MessageType, try_enum from .errors import InvalidArgument from .file import File from .flags import MessageFlags @@ -74,10 +74,12 @@ from .types.member import Member as MemberPayload from .types.member import UserWithMember as UserWithMemberPayload from .types.message import Attachment as AttachmentPayload + from .types.message import ForwardedMessage as ForwardedMessagePayload from .types.message import Message as MessagePayload from .types.message import MessageActivity as MessageActivityPayload from .types.message import MessageApplication as MessageApplicationPayload from .types.message import MessageReference as MessageReferencePayload + from .types.message import MessageSnapshot as MessageSnapshotPayload from .types.message import Reaction as ReactionPayload from .types.threads import ThreadArchiveDuration from .types.user import User as UserPayload @@ -93,8 +95,9 @@ "PartialMessage", "MessageReference", "DeletedReferencedMessage", + "ForwardedMessage", ) - +#continue from here def convert_emoji_reaction(emoji): if isinstance(emoji, Reaction): @@ -422,6 +425,10 @@ class MessageReference: Attributes ---------- + type: Optional[:class:`~discord.MessageReferenceType`] + The type of message reference. If this is not provided, assume default behavior (reply). + .. versionadded:: 2.7 + message_id: Optional[:class:`int`] The id of the message referenced. channel_id: :class:`int` @@ -452,6 +459,7 @@ class MessageReference: "guild_id", "fail_if_not_exists", "resolved", + "type", "_state", ) @@ -462,6 +470,7 @@ def __init__( channel_id: int, guild_id: int | None = None, fail_if_not_exists: bool = True, + type: MessageReferenceType = MessageReferenceType.default, ): self._state: ConnectionState | None = None self.resolved: Message | DeletedReferencedMessage | None = None @@ -469,14 +478,19 @@ def __init__( self.channel_id: int = channel_id self.guild_id: int | None = guild_id self.fail_if_not_exists: bool = fail_if_not_exists + self.type: MessageReferenceType = type @classmethod def with_state( cls: type[MR], state: ConnectionState, data: MessageReferencePayload ) -> MR: self = cls.__new__(cls) + self.type = ( + try_enum(MessageReferenceType, data.get("type")) + or MessageReferenceType.default + ) self.message_id = utils._get_as_snowflake(data, "message_id") - self.channel_id = int(data.pop("channel_id")) + self.channel_id = utils._get_as_snowflake(data, "channel_id") self.guild_id = utils._get_as_snowflake(data, "guild_id") self.fail_if_not_exists = data.get("fail_if_not_exists", True) self._state = state @@ -485,7 +499,11 @@ def with_state( @classmethod def from_message( - cls: type[MR], message: Message, *, fail_if_not_exists: bool = True + cls: type[MR], + message: Message, + *, + fail_if_not_exists: bool = True, + type: MessageReferenceType = MessageReferenceType.default, ) -> MR: """Creates a :class:`MessageReference` from an existing :class:`~discord.Message`. @@ -500,6 +518,9 @@ def from_message( if the message no longer exists or Discord could not fetch the message. .. versionadded:: 1.7 + type: Optional[:class:`~discord.MessageReferenceType`] + The type of reference to create. Defaults to :attr:`MessageReferenceType.default` (reply). + .. versionadded:: 2.7 Returns ------- @@ -511,6 +532,7 @@ def from_message( channel_id=message.channel.id, guild_id=getattr(message.guild, "id", None), fail_if_not_exists=fail_if_not_exists, + type=type, ) self._state = message._state return self @@ -532,7 +554,8 @@ def jump_url(self) -> str: def __repr__(self) -> str: return ( f"" + f" channel_id={self.channel_id!r} guild_id={self.guild_id!r}" + f" type={self.type!r}>" ) def to_dict(self) -> MessageReferencePayload: @@ -540,6 +563,7 @@ def to_dict(self) -> MessageReferencePayload: {"message_id": self.message_id} if self.message_id is not None else {} ) result["channel_id"] = self.channel_id + result["type"] = self.type and self.type.value if self.guild_id is not None: result["guild_id"] = self.guild_id if self.fail_if_not_exists is not None: @@ -548,6 +572,102 @@ def to_dict(self) -> MessageReferencePayload: to_message_reference_dict = to_dict +class ForwardedMessage: + """Represents the snapshotted contents from a forwarded message. Forwarded messages are immutable; any updates to the original message won't be reflected. + .. versionadded:: 2.7 + Attributes + ---------- + type: :class:`MessageType` + The type of message. In most cases this should not be checked, but it is helpful + in cases where it might be a system message for :attr:`system_content`. + content: :class:`str` + The contents of the original message. + embeds: List[:class:`Embed`] + A list of embeds the original message had. + attachments: List[:class:`Attachment`] + A list of attachments given to the original message. + flags: :class:`MessageFlags` + Extra features of the message. + mentions: List[Union[:class:`abc.User`, :class:`Object`]] + A list of :class:`Member` that were mentioned. + role_mentions: List[Union[:class:`Role`, :class:`Object`]] + A list of :class:`Role` that were mentioned. + stickers: List[:class:`StickerItem`] + A list of sticker items given to the original message. + components: List[:class:`Component`] + A list of components in the original message. + """ + + def __init__( + self, + *, + state: ConnectionState, + reference: MessageReference, + data: ForwardedMessagePayload, + ): + self._state: ConnectionState = state + self._reference = reference + self.id: int = reference.message_id + self.channel = state.get_channel(reference.channel_id) or ( + reference.channel_id and Object(reference.channel_id) + ) + self.guild = state._get_guild(reference.guild_id) or ( + reference.guild_id and Object(reference.guild_id) + ) + self.content: str = data["content"] + self.embeds: list[Embed] = [Embed.from_dict(a) for a in data["embeds"]] + self.attachments: list[Attachment] = [ + Attachment(data=a, state=state) for a in data["attachments"] + ] + self.flags: MessageFlags = MessageFlags._from_value(data.get("flags", 0)) + self.stickers: list[StickerItem] = [ + StickerItem(data=d, state=state) for d in data.get("sticker_items", []) + ] + self.components: list[Component] = [ + _component_factory(d) for d in data.get("components", []) + ] + self._edited_timestamp: datetime.datetime | None = utils.parse_time( + data["edited_timestamp"] + ) + + @property + def created_at(self) -> datetime.datetime: + """The original message's creation time in UTC.""" + return utils.snowflake_time(self.id) + + @property + def edited_at(self) -> datetime.datetime | None: + """An aware UTC datetime object containing the + edited time of the original message. + """ + return self._edited_timestamp + + def __repr__(self) -> str: + return f"" + + +class MessageSnapshot: + """Represents a message snapshot. + .. versionadded:: 2.7 + Attributes + ---------- + message: :class:`ForwardedMessage` + The forwarded message, which includes a minimal subset of fields from the original message. + """ + + def __init__( + self, + *, + state: ConnectionState, + reference: MessageReference, + data: MessageSnapshotPayload, + ): + self._state: ConnectionState = state + self.message: ForwardedMessage | None + if fm := data.get("message"): + self.message = ForwardedMessage(state=state, reference=reference, data=fm) + + def flatten_handlers(cls): prefix = len("_handle_") @@ -685,6 +805,9 @@ class Message(Hashable): The thread created from this message, if applicable. .. versionadded:: 2.0 + snapshots: Optional[List[:class:`MessageSnapshots`]] + The snapshots attached to this message, if applicable. + .. versionadded:: 2.7 """ __slots__ = ( @@ -720,6 +843,7 @@ class Message(Hashable): "guild", "interaction", "thread", + "snapshots", ) if TYPE_CHECKING: @@ -799,6 +923,19 @@ def __init__( # the channel will be the correct type here ref.resolved = self.__class__(channel=chan, data=resolved, state=state) # type: ignore + self.snapshots: list[MessageSnapshot] + try: + self.snapshots = [ + MessageSnapshot( + state=state, + reference=self.reference, + data=ms, + ) + for ms in data["message_snapshots"] + ] + except KeyError: + self.snapshots = [] + from .interactions import MessageInteraction self.interaction: MessageInteraction | None @@ -1766,9 +1903,41 @@ async def reply(self, content: str | None = None, **kwargs) -> Message: you specified both ``file`` and ``files``. """ - return await self.channel.send(content, reference=self, **kwargs) + return await self.channel.send(content, reference=self.to_reference(), **kwargs) - def to_reference(self, *, fail_if_not_exists: bool = True) -> MessageReference: + async def forward( + self, channel: MessageableChannel | PartialMessageableChannel, **kwargs + ) -> Message: + """|coro| + A shortcut method to :meth:`.abc.Messageable.send` to forward the + :class:`.Message` to a channel. + .. versionadded:: 2.7 + Parameters + ---------- + channel: Union[:class:`Emoji`, :class:`Reaction`, :class:`PartialEmoji`, :class:`str`] + The emoji to react with. + Returns + ------- + :class:`.Message` + The message that was sent. + Raises + ------ + ~discord.HTTPException + Sending the message failed. + ~discord.Forbidden + You do not have the proper permissions to send the message. + ~discord.InvalidArgument + The ``files`` list is not of the appropriate size, or + you specified both ``file`` and ``files``. + """ + + return await channel.send( + reference=self.to_reference(type=MessageReferenceType.forward) + ) + + def to_reference( + self, *, fail_if_not_exists: bool = True, type: MessageReferenceType = None + ) -> MessageReference: """Creates a :class:`~discord.MessageReference` from the current message. .. versionadded:: 1.6 @@ -1781,6 +1950,11 @@ def to_reference(self, *, fail_if_not_exists: bool = True) -> MessageReference: .. versionadded:: 1.7 + type: Optional[:class:`~discord.MessageReferenceType`] + The type of message reference. Defaults to a reply. + .. versionadded:: 2.7 + + Returns ------- :class:`~discord.MessageReference` @@ -1788,13 +1962,16 @@ def to_reference(self, *, fail_if_not_exists: bool = True) -> MessageReference: """ return MessageReference.from_message( - self, fail_if_not_exists=fail_if_not_exists + self, fail_if_not_exists=fail_if_not_exists, type=type ) - def to_message_reference_dict(self) -> MessageReferencePayload: + def to_message_reference_dict( + self, type: MessageReferenceType = None + ) -> MessageReferencePayload: data: MessageReferencePayload = { "message_id": self.id, "channel_id": self.channel.id, + "type": type and type.value, } if self.guild is not None: diff --git a/discord/types/embed.py b/discord/types/embed.py index 0d73afe40c..b7aff9d25d 100644 --- a/discord/types/embed.py +++ b/discord/types/embed.py @@ -75,7 +75,14 @@ class EmbedAuthor(TypedDict, total=False): EmbedType = Literal[ - "rich", "image", "video", "gifv", "article", "link", "auto_moderation_message" + "rich", + "image", + "video", + "gifv", + "article", + "link", + "auto_moderation_message", + "poll_result", ] diff --git a/discord/types/message.py b/discord/types/message.py index 93e99ba7ab..f78df67ede 100644 --- a/discord/types/message.py +++ b/discord/types/message.py @@ -91,8 +91,10 @@ class MessageApplication(TypedDict): icon: str | None name: str +MessageReferenceType = Literal[0, 1] class MessageReference(TypedDict, total=False): + type: NotRequired[MessageReferenceType] message_id: Snowflake channel_id: Snowflake guild_id: Snowflake @@ -102,8 +104,28 @@ class MessageReference(TypedDict, total=False): MessageType = Literal[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 18, 19, 20, 21, 22, 23, 24 ] +class MessageCall(TypedDict): + participants: SnowflakeList + ended_timestamp: NotRequired[str] +class ForwardedMessage(TypedDict): + type: MessageType + content: str + embeds: list[Embed] + attachments: list[Attachment] + timestamp: str + edited_timestamp: str | None + flags: NotRequired[int] + mentions: list[UserWithMember] + mention_roles: SnowflakeList + sticker_items: NotRequired[list[StickerItem]] + components: NotRequired[list[Component]] + + +class MessageSnapshot(TypedDict): + message: ForwardedMessage + class Message(TypedDict): guild_id: NotRequired[Snowflake] member: NotRequired[Member] @@ -135,6 +157,8 @@ class Message(TypedDict): embeds: list[Embed] pinned: bool type: MessageType + call: MessageCall + message_snapshots: NotRequired[list[MessageSnapshot]] AllowedMentionType = Literal["roles", "users", "everyone"] From 6d513679365f63932a4c1d419654ddfde749291a Mon Sep 17 00:00:00 2001 From: yashpaliwal-bst Date: Mon, 14 Oct 2024 22:11:31 +0530 Subject: [PATCH 12/14] removed unnecessary additions in library --- discord/enums.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/discord/enums.py b/discord/enums.py index e3b3c16b46..e96c809737 100644 --- a/discord/enums.py +++ b/discord/enums.py @@ -68,7 +68,6 @@ "AutoModKeywordPresetType", "ApplicationRoleConnectionMetadataType", "ReactionType", - "PollLayoutType", "MessageReferenceType", ) @@ -259,11 +258,6 @@ class MessageType(Enum): stage_raise_hand = 30 stage_topic = 31 guild_application_premium_subscription = 32 - guild_incident_alert_mode_enabled = 36 - guild_incident_alert_mode_disabled = 37 - guild_incident_report_raid = 38 - guild_incident_report_false_alarm = 39 - purchase_notification = 44 poll_result = 46 @@ -959,9 +953,6 @@ class ReactionType(Enum): normal = 0 burst = 1 -class PollLayoutType(Enum): - """The poll's layout type.""" - default = 1 class MessageReferenceType(Enum): From f5a29096916ed7fe8dee8660247fc471c04ade8b Mon Sep 17 00:00:00 2001 From: yashpaliwal-bst Date: Wed, 23 Oct 2024 17:16:24 +0530 Subject: [PATCH 13/14] imported object --- discord/message.py | 1 + 1 file changed, 1 insertion(+) diff --git a/discord/message.py b/discord/message.py index 99fcb62d54..ed1526b024 100644 --- a/discord/message.py +++ b/discord/message.py @@ -51,6 +51,7 @@ from .guild import Guild from .member import Member from .mixins import Hashable +from .object import Object from .partial_emoji import PartialEmoji from .reaction import Reaction from .sticker import StickerItem From be423d214c5f6519adc4a1c3241ac0bc362b1efa Mon Sep 17 00:00:00 2001 From: yashpaliwal-bst Date: Tue, 5 Nov 2024 12:41:13 +0530 Subject: [PATCH 14/14] removed comment --- discord/message.py | 1 - 1 file changed, 1 deletion(-) diff --git a/discord/message.py b/discord/message.py index ed1526b024..2e82ccd506 100644 --- a/discord/message.py +++ b/discord/message.py @@ -98,7 +98,6 @@ "DeletedReferencedMessage", "ForwardedMessage", ) -#continue from here def convert_emoji_reaction(emoji): if isinstance(emoji, Reaction):