From 39243f9bc536cfefd84e49cd6c55c3b523882070 Mon Sep 17 00:00:00 2001 From: vi Date: Thu, 9 Jul 2026 18:52:03 +0200 Subject: [PATCH 1/2] fix(typing): allow `ForwardedMessage` in `components_from_message` and similar methods --- disnake/ui/action_row.py | 10 +++++----- disnake/ui/view.py | 8 +++++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/disnake/ui/action_row.py b/disnake/ui/action_row.py index d3841a6753..c5535a8562 100644 --- a/disnake/ui/action_row.py +++ b/disnake/ui/action_row.py @@ -77,7 +77,7 @@ from ..abc import AnyChannel from ..emoji import Emoji from ..member import Member - from ..message import Message + from ..message import ForwardedMessage, Message from ..partial_emoji import PartialEmoji from ..role import Role from ..types.components import ( @@ -877,7 +877,7 @@ def with_message_components(cls, *, id: int = 0) -> ActionRow[ActionRowMessageCo @classmethod def rows_from_message( cls, - message: Message, + message: Message | ForwardedMessage, *, strict: bool = True, ) -> list[ActionRow[ActionRowMessageComponent]]: @@ -895,7 +895,7 @@ def rows_from_message( Parameters ---------- - message: :class:`disnake.Message` + message: :class:`~disnake.Message` | :class:`~disnake.ForwardedMessage` The message from which to extract the components. strict: :class:`bool` Whether or not to raise an exception if an unknown component type is encountered. @@ -1096,7 +1096,7 @@ def walk_components(components: Sequence[ComponentT]) -> Iterator[ComponentT]: yield from _walk_internal(item, seen) -def components_from_message(message: Message) -> list[MessageTopLevelComponent]: +def components_from_message(message: Message | ForwardedMessage) -> list[MessageTopLevelComponent]: r"""Create a list of :class:`UIComponent`\s from the components of an existing message. This will abide by existing component format on the message, including component @@ -1107,7 +1107,7 @@ def components_from_message(message: Message) -> list[MessageTopLevelComponent]: Parameters ---------- - message: :class:`disnake.Message` + message: :class:`~disnake.Message` | :class:`~disnake.ForwardedMessage` The message from which to extract the components. Raises diff --git a/disnake/ui/view.py b/disnake/ui/view.py index ab8d681298..712832f8f7 100644 --- a/disnake/ui/view.py +++ b/disnake/ui/view.py @@ -32,7 +32,7 @@ from typing_extensions import Self from ..interactions import MessageInteraction - from ..message import Message + from ..message import ForwardedMessage, Message from ..state import ConnectionState from ..types.components import ActionRow as ActionRowPayload, Component as ComponentPayload from .item import ItemCallbackType @@ -194,7 +194,9 @@ def key(item: Item[Self]) -> int: return components @classmethod - def from_message(cls, message: Message, /, *, timeout: float | None = 180.0) -> View: + def from_message( + cls, message: Message | ForwardedMessage, /, *, timeout: float | None = 180.0 + ) -> View: """Converts a message's components into a :class:`View`. The :attr:`.Message.components` of a message are read-only @@ -204,7 +206,7 @@ def from_message(cls, message: Message, /, *, timeout: float | None = 180.0) -> Parameters ---------- - message: :class:`disnake.Message` + message: :class:`~disnake.Message` | :class:`~disnake.ForwardedMessage` The message with components to convert into a view. timeout: :class:`float` | :data:`None` The timeout of the converted view. From 8edd9c1ceb648b29db42e26db66e8914b86f9a22 Mon Sep 17 00:00:00 2001 From: vi Date: Thu, 9 Jul 2026 19:08:14 +0200 Subject: [PATCH 2/2] refactor: yay protocols --- disnake/ui/_types.py | 8 +++++++- disnake/ui/action_row.py | 6 +++--- disnake/ui/view.py | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/disnake/ui/_types.py b/disnake/ui/_types.py index 6bf5066b96..c4713d1a17 100644 --- a/disnake/ui/_types.py +++ b/disnake/ui/_types.py @@ -3,13 +3,14 @@ from __future__ import annotations from collections.abc import Sequence -from typing import TYPE_CHECKING, Any, TypeAlias, Union +from typing import TYPE_CHECKING, Any, Protocol, TypeAlias, Union from typing_extensions import ParamSpec, TypeVar if TYPE_CHECKING: from typing import TypeAlias + from ..components import MessageTopLevelComponent as MessageTopLevelRawComponent from . import ( ActionRow, Button, @@ -97,3 +98,8 @@ ActionRowModalComponent, # deprecated ModalTopLevelComponent_, ] + + +class MessageWithComponents(Protocol): + @property + def components(self) -> Sequence[MessageTopLevelRawComponent]: ... diff --git a/disnake/ui/action_row.py b/disnake/ui/action_row.py index c5535a8562..decaac1c87 100644 --- a/disnake/ui/action_row.py +++ b/disnake/ui/action_row.py @@ -50,6 +50,7 @@ ActionRowModalComponent, ComponentInput, MessageTopLevelComponent, + MessageWithComponents, NonActionRowChildT, ) from .button import Button @@ -77,7 +78,6 @@ from ..abc import AnyChannel from ..emoji import Emoji from ..member import Member - from ..message import ForwardedMessage, Message from ..partial_emoji import PartialEmoji from ..role import Role from ..types.components import ( @@ -877,7 +877,7 @@ def with_message_components(cls, *, id: int = 0) -> ActionRow[ActionRowMessageCo @classmethod def rows_from_message( cls, - message: Message | ForwardedMessage, + message: MessageWithComponents, *, strict: bool = True, ) -> list[ActionRow[ActionRowMessageComponent]]: @@ -1096,7 +1096,7 @@ def walk_components(components: Sequence[ComponentT]) -> Iterator[ComponentT]: yield from _walk_internal(item, seen) -def components_from_message(message: Message | ForwardedMessage) -> list[MessageTopLevelComponent]: +def components_from_message(message: MessageWithComponents) -> list[MessageTopLevelComponent]: r"""Create a list of :class:`UIComponent`\s from the components of an existing message. This will abide by existing component format on the message, including component diff --git a/disnake/ui/view.py b/disnake/ui/view.py index 712832f8f7..3195bfd09d 100644 --- a/disnake/ui/view.py +++ b/disnake/ui/view.py @@ -32,9 +32,9 @@ from typing_extensions import Self from ..interactions import MessageInteraction - from ..message import ForwardedMessage, Message from ..state import ConnectionState from ..types.components import ActionRow as ActionRowPayload, Component as ComponentPayload + from ._types import MessageWithComponents from .item import ItemCallbackType @@ -195,7 +195,7 @@ def key(item: Item[Self]) -> int: @classmethod def from_message( - cls, message: Message | ForwardedMessage, /, *, timeout: float | None = 180.0 + cls, message: MessageWithComponents, /, *, timeout: float | None = 180.0 ) -> View: """Converts a message's components into a :class:`View`.