From 93fa3cb9d65be3ce6815ae2b6ae6f3e262237895 Mon Sep 17 00:00:00 2001 From: DA344 <108473820+DA-344@users.noreply.github.com> Date: Sun, 22 Feb 2026 22:45:49 +0100 Subject: [PATCH] Fix (Sync)Webhook.edit_message missing the view parameter --- discord/webhook/async_.py | 38 +++++++++++++++++++++++++------------- discord/webhook/sync.py | 37 +++++++++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 15 deletions(-) diff --git a/discord/webhook/async_.py b/discord/webhook/async_.py index 9d4fa0da6..5768c7200 100644 --- a/discord/webhook/async_.py +++ b/discord/webhook/async_.py @@ -364,6 +364,7 @@ class AsyncWebhookAdapter: multipart: Optional[List[Dict[str, Any]]] = None, files: Optional[Sequence[File]] = None, thread_id: Optional[int] = None, + with_components: bool = False, ) -> Response[MessagePayload]: route = Route( 'PATCH', @@ -372,7 +373,9 @@ class AsyncWebhookAdapter: webhook_token=token, message_id=message_id, ) - params = None if thread_id is None else {'thread_id': thread_id} + params = {'with_components': int(with_components)} + if thread_id: + params['thread_id'] = thread_id return self.request( route, session=session, @@ -848,7 +851,15 @@ class WebhookMessage(Message): See :meth:`.abc.Messageable.send` for more information. view: Optional[:class:`~discord.ui.View`] The updated view to update this message with. If ``None`` is passed then - the view is removed. + the view is removed. If the webhook is partial or is not managed by the + library, then you can not send interactable components. Otherwise, you + can send views with any type of components. + + .. note:: + + To update the message to add a :class:`~discord.ui.LayoutView`, you + must explicitly set the ``content``, ``embed``, ``embeds``, and + ``attachments`` parameters to either ``None`` or an empty array, as appropriate. .. versionadded:: 2.0 @@ -1772,7 +1783,7 @@ class Webhook(BaseWebhook): .. versionadded:: 1.4 view: Union[:class:`discord.ui.View`, :class:`discord.ui.LayoutView`] The view to send with the message. If the webhook is partial or - is not managed by the library, then you can only send URL buttons. + is not managed by the library, then you can not send interactable components. Otherwise, you can send views with any type of components. .. versionadded:: 2.0 @@ -1857,12 +1868,10 @@ class Webhook(BaseWebhook): if view is not MISSING: if not hasattr(view, '__discord_ui_view__'): - raise TypeError(f'expected view parameter to be of type View not {view.__class__.__name__}') + raise TypeError(f'expected view parameter to be of type View or LayoutView, not {view.__class__.__name__}') if isinstance(self._state, _WebhookState) and view.is_dispatchable(): - raise ValueError( - 'Webhook views with any component other than URL buttons require an associated state with the webhook' - ) + raise ValueError('Webhook views with interactable components require an associated state with the webhook') if ephemeral is True and view.timeout is None and view.is_dispatchable(): view.timeout = 15 * 60.0 @@ -2048,8 +2057,9 @@ class Webhook(BaseWebhook): See :meth:`.abc.Messageable.send` for more information. view: Optional[Union[:class:`~discord.ui.View`, :class:`~discord.ui.LayoutView`]] The updated view to update this message with. If ``None`` is passed then - the view is removed. The webhook must have state attached, similar to - :meth:`send`. + the view is removed. If the webhook is partial or is not managed by the + library, then you can not send interactable components. Otherwise, you + can send views with any type of components. .. note:: @@ -2085,11 +2095,12 @@ class Webhook(BaseWebhook): if self.token is None: raise ValueError('This webhook does not have a token associated with it') - if view is not MISSING: - if isinstance(self._state, _WebhookState): - raise ValueError('This webhook does not have state associated with it') + if view: + if not hasattr(view, '__discord_ui_view__'): + raise TypeError(f'expected view parameter to be of type View or LayoutView, not {view.__class__.__name__}') - self._state.prevent_view_updates_for(message_id) + if isinstance(self._state, _WebhookState) and view.is_dispatchable(): + raise ValueError('Webhook views with interactable components require an associated state with the webhook') previous_mentions: Optional[AllowedMentions] = getattr(self._state, 'allowed_mentions', None) with handle_message_parameters( @@ -2117,6 +2128,7 @@ class Webhook(BaseWebhook): multipart=params.multipart, files=params.files, thread_id=thread_id, + with_components=bool(view), ) message = self._create_message(data, thread=thread) diff --git a/discord/webhook/sync.py b/discord/webhook/sync.py index 1786496fa..b76af8337 100644 --- a/discord/webhook/sync.py +++ b/discord/webhook/sync.py @@ -329,6 +329,7 @@ class WebhookAdapter: multipart: Optional[List[Dict[str, Any]]] = None, files: Optional[Sequence[File]] = None, thread_id: Optional[int] = None, + with_components: bool = False, ) -> MessagePayload: route = Route( 'PATCH', @@ -337,7 +338,9 @@ class WebhookAdapter: webhook_token=token, message_id=message_id, ) - params = None if thread_id is None else {'thread_id': thread_id} + params = {'with_components': int(with_components)} + if thread_id: + params['thread_id'] = thread_id return self.request(route, session, payload=payload, multipart=multipart, files=files, params=params) def delete_webhook_message( @@ -415,6 +418,7 @@ class SyncWebhookMessage(Message): embed: Optional[Embed] = MISSING, attachments: Sequence[Union[Attachment, File]] = MISSING, allowed_mentions: Optional[AllowedMentions] = None, + view: Optional[BaseView] = MISSING, ) -> SyncWebhookMessage: """Edits the message. @@ -443,6 +447,19 @@ class SyncWebhookMessage(Message): allowed_mentions: :class:`AllowedMentions` Controls the mentions being processed in this message. See :meth:`.abc.Messageable.send` for more information. + view: Union[:class:`discord.ui.View`, :class:`discord.ui.LayoutView`] + The updated view to update this message with. This can only have non-interactible items, which do not + require a state to be attached to it. If ``None`` is passed then the view is removed. + + If you want to edit a webhook message with any component attached to it, check :meth:`WebhookMessage.edit`. + + .. note:: + + To update the message to add a :class:`~discord.ui.LayoutView`, you + must explicitly set the ``content``, ``embed``, ``embeds``, and + ``attachments`` parameters to either ``None`` or an empty array, as appropriate. + + .. versionadded:: 2.7 Raises ------- @@ -451,7 +468,7 @@ class SyncWebhookMessage(Message): Forbidden Edited a message that is not yours. TypeError - You specified both ``embed`` and ``embeds`` + You specified both ``embed`` and ``embeds``. ValueError The length of ``embeds`` was invalid or there was no token associated with this webhook. @@ -469,6 +486,7 @@ class SyncWebhookMessage(Message): attachments=attachments, allowed_mentions=allowed_mentions, thread=self._state._thread, + view=view, ) def add_files(self, *files: File) -> SyncWebhookMessage: @@ -1245,6 +1263,12 @@ class SyncWebhook(BaseWebhook): If you want to edit a webhook message with any component attached to it, check :meth:`WebhookMessage.edit`. + .. note:: + + To update the message to add a :class:`~discord.ui.LayoutView`, you + must explicitly set the ``content``, ``embed``, ``embeds``, and + ``attachments`` parameters to either ``None`` or an empty array, as appropriate. + .. versionadded:: 2.6 allowed_mentions: :class:`AllowedMentions` Controls the mentions being processed in this message. @@ -1270,6 +1294,13 @@ class SyncWebhook(BaseWebhook): if self.token is None: raise ValueError('This webhook does not have a token associated with it') + if view: + if not hasattr(view, '__discord_ui_view__'): + raise TypeError(f'expected view parameter to be of type View or LayoutView, not {view.__class__.__name__}') + + if view.is_dispatchable(): + raise ValueError('SyncWebhooks can not send interactable components') + previous_mentions: Optional[AllowedMentions] = getattr(self._state, 'allowed_mentions', None) with handle_message_parameters( content=content, @@ -1278,6 +1309,7 @@ class SyncWebhook(BaseWebhook): embeds=embeds, allowed_mentions=allowed_mentions, previous_allowed_mentions=previous_mentions, + view=view, ) as params: thread_id: Optional[int] = None if thread is not MISSING: @@ -1293,6 +1325,7 @@ class SyncWebhook(BaseWebhook): multipart=params.multipart, files=params.files, thread_id=thread_id, + with_components=bool(view), ) return self._create_message(data, thread=thread)