From 827524e57fb9cdd3158bf42787d1aad3bee2680b Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Wed, 6 Aug 2025 23:13:12 +0100 Subject: [PATCH 01/45] types --- discord/types/components.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/discord/types/components.py b/discord/types/components.py index 16d9661b3a..45b5dd24d4 100644 --- a/discord/types/components.py +++ b/discord/types/components.py @@ -33,7 +33,7 @@ from .emoji import PartialEmoji from .snowflake import Snowflake -ComponentType = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17] +ComponentType = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18] ButtonStyle = Literal[1, 2, 3, 4, 5, 6] InputTextStyle = Literal[1, 2] SeparatorSpacingSize = Literal[1, 2] @@ -69,7 +69,7 @@ class InputText(BaseComponent): type: Literal[4] style: InputTextStyle custom_id: str - label: str + label: NotRequired[str] class SelectOption(TypedDict): @@ -89,6 +89,7 @@ class SelectMenu(BaseComponent): options: NotRequired[list[SelectOption]] type: Literal[3, 5, 6, 7, 8] custom_id: str + required: NotRequired[bool] class TextDisplayComponent(BaseComponent): @@ -150,6 +151,12 @@ class ContainerComponent(BaseComponent): spoiler: NotRequired[bool] components: list[AllowedContainerComponents] +class LabelComponent(BaseComponent): + type: Literal[18] + label: str + description: NotRequired[str] + Component: Union[SelectMenu, InputText] + Component = Union[ActionRow, ButtonComponent, SelectMenu, InputText] From 48e155794ef44013f71764d5d37de82d1f32fd98 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 22:15:17 +0000 Subject: [PATCH 02/45] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/types/components.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/discord/types/components.py b/discord/types/components.py index 45b5dd24d4..1a79a30bf3 100644 --- a/discord/types/components.py +++ b/discord/types/components.py @@ -151,11 +151,12 @@ class ContainerComponent(BaseComponent): spoiler: NotRequired[bool] components: list[AllowedContainerComponents] + class LabelComponent(BaseComponent): type: Literal[18] label: str description: NotRequired[str] - Component: Union[SelectMenu, InputText] + Component: SelectMenu | InputText Component = Union[ActionRow, ButtonComponent, SelectMenu, InputText] From dcdea3fbab6eb4578548aa25b86865338e8e560e Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Wed, 6 Aug 2025 23:45:02 +0100 Subject: [PATCH 03/45] component --- discord/components.py | 62 +++++++++++++++++++++++++++++++++++++ discord/types/components.py | 2 +- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/discord/components.py b/discord/components.py index 39576c9eea..d3ed911fb8 100644 --- a/discord/components.py +++ b/discord/components.py @@ -58,6 +58,7 @@ from .types.components import SeparatorComponent as SeparatorComponentPayload from .types.components import TextDisplayComponent as TextDisplayComponentPayload from .types.components import ThumbnailComponent as ThumbnailComponentPayload + from .types.components import LabelComponent as LabelComponentPayload from .types.components import UnfurledMediaItem as UnfurledMediaItemPayload __all__ = ( @@ -76,6 +77,7 @@ "FileComponent", "Separator", "Container", + "Label", ) C = TypeVar("C", bound="Component") @@ -375,6 +377,10 @@ class SelectMenu(Component): Added support for :attr:`ComponentType.user_select`, :attr:`ComponentType.role_select`, :attr:`ComponentType.mentionable_select`, and :attr:`ComponentType.channel_select`. + .. versionchanged:: 2.7 + + Added the :attr:`required` attribute for use in modals. + Attributes ---------- type: :class:`ComponentType` @@ -399,6 +405,8 @@ class SelectMenu(Component): except for :attr:`ComponentType.channel_select`. disabled: :class:`bool` Whether the select is disabled or not. + required: Optional[:class:`bool`] + Whether the select is required or not. Only useable in modals. Defaults to ``False``. """ __slots__: tuple[str, ...] = ( @@ -409,6 +417,7 @@ class SelectMenu(Component): "options", "channel_types", "disabled", + "required", ) __repr_info__: ClassVar[tuple[str, ...]] = __slots__ @@ -428,6 +437,7 @@ def __init__(self, data: SelectMenuPayload): self.channel_types: list[ChannelType] = [ try_enum(ChannelType, ct) for ct in data.get("channel_types", []) ] + self.required: bool | None = data.get("required") # Currently defaults to False, pending change def to_dict(self) -> SelectMenuPayload: payload: SelectMenuPayload = { @@ -445,6 +455,8 @@ def to_dict(self) -> SelectMenuPayload: payload["channel_types"] = [ct.value for ct in self.channel_types] if self.placeholder: payload["placeholder"] = self.placeholder + if self.required is not None: + payload["required"] = self.required return payload @@ -1037,6 +1049,55 @@ def walk_components(self) -> Iterator[Component]: yield c +class Label(Component): + """Represents a Label used in modals as the top-level component. + + This is a component that holda another component alongside additional text in modals. + ``component`` may only be: + + - :class:`InputText` + - :class:`SelectMenu` (string) + + This inherits from :class:`Component`. + + .. versionadded:: 2.7 + + Attributes + ---------- + component: :class:`Component` + The component contained in this label. Currently supports :class:`InputText` and :class:`SelectMenu`. + label: :class:`str` + The main text associated with this label's ``component``. + description: Optional[:class:`str`] + The description associated with this label's ``component``. + """ + + __slots__: tuple[str, ...] = ("component", "label", "description") + + __repr_info__: ClassVar[tuple[str, ...]] = __slots__ + versions: tuple[int, ...] = () + + def __init__(self, data: LabelComponentPayload): + self.type: ComponentType = try_enum(ComponentType, data["type"]) + self.id: int = data.get("id") + self.component: Component = _component_factory(data.get("component", {})) + self.label: str = data.get("label") + self.description: str | None = data.get("description") + + def to_dict(self) -> LabelComponentPayload: + payload = { + "type": int(self.type), + "id": self.id, + "component": self.components.to_dict(), + "label": self.label, + "description": self.description + } + return payload + + def walk_components(self) -> Iterator[Component]: + yield from [self.component] + + COMPONENT_MAPPINGS = { 1: ActionRow, 2: Button, @@ -1053,6 +1114,7 @@ def walk_components(self) -> Iterator[Component]: 13: FileComponent, 14: Separator, 17: Container, + 18: Label, } STATE_COMPONENTS = (Section, Container, Thumbnail, MediaGallery, FileComponent) diff --git a/discord/types/components.py b/discord/types/components.py index 1a79a30bf3..14063cdf54 100644 --- a/discord/types/components.py +++ b/discord/types/components.py @@ -156,7 +156,7 @@ class LabelComponent(BaseComponent): type: Literal[18] label: str description: NotRequired[str] - Component: SelectMenu | InputText + component: SelectMenu | InputText Component = Union[ActionRow, ButtonComponent, SelectMenu, InputText] From 6e68e6add044928dd748a9186fb7ed478ca4bab4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 22:45:58 +0000 Subject: [PATCH 04/45] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/components.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/discord/components.py b/discord/components.py index d3ed911fb8..35420f8690 100644 --- a/discord/components.py +++ b/discord/components.py @@ -50,6 +50,7 @@ from .types.components import ContainerComponent as ContainerComponentPayload from .types.components import FileComponent as FileComponentPayload from .types.components import InputText as InputTextComponentPayload + from .types.components import LabelComponent as LabelComponentPayload from .types.components import MediaGalleryComponent as MediaGalleryComponentPayload from .types.components import MediaGalleryItem as MediaGalleryItemPayload from .types.components import SectionComponent as SectionComponentPayload @@ -58,7 +59,6 @@ from .types.components import SeparatorComponent as SeparatorComponentPayload from .types.components import TextDisplayComponent as TextDisplayComponentPayload from .types.components import ThumbnailComponent as ThumbnailComponentPayload - from .types.components import LabelComponent as LabelComponentPayload from .types.components import UnfurledMediaItem as UnfurledMediaItemPayload __all__ = ( @@ -437,7 +437,9 @@ def __init__(self, data: SelectMenuPayload): self.channel_types: list[ChannelType] = [ try_enum(ChannelType, ct) for ct in data.get("channel_types", []) ] - self.required: bool | None = data.get("required") # Currently defaults to False, pending change + self.required: bool | None = data.get( + "required" + ) # Currently defaults to False, pending change def to_dict(self) -> SelectMenuPayload: payload: SelectMenuPayload = { @@ -1090,7 +1092,7 @@ def to_dict(self) -> LabelComponentPayload: "id": self.id, "component": self.components.to_dict(), "label": self.label, - "description": self.description + "description": self.description, } return payload From 4686ea08c829f92ce2efae48f7dc506361c01359 Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Thu, 7 Aug 2025 00:35:17 +0100 Subject: [PATCH 05/45] items --- discord/components.py | 2 +- discord/ui/input_text.py | 10 +++++ discord/ui/item.py | 3 ++ discord/ui/modal.py | 83 ++++++++++++++++++++++++++-------------- discord/ui/select.py | 19 +++++++++ discord/ui/view.py | 10 +++++ 6 files changed, 98 insertions(+), 29 deletions(-) diff --git a/discord/components.py b/discord/components.py index 35420f8690..da021f0ab0 100644 --- a/discord/components.py +++ b/discord/components.py @@ -1090,7 +1090,7 @@ def to_dict(self) -> LabelComponentPayload: payload = { "type": int(self.type), "id": self.id, - "component": self.components.to_dict(), + "component": self.component.to_dict(), "label": self.label, "description": self.description, } diff --git a/discord/ui/input_text.py b/discord/ui/input_text.py index fa9d7615ab..8d45ffd5ce 100644 --- a/discord/ui/input_text.py +++ b/discord/ui/input_text.py @@ -17,6 +17,10 @@ class InputText: .. versionadded:: 2.0 + .. versionchanged:: 2.7 + + Added :attr:`description`. + Parameters ---------- style: :class:`~discord.InputTextStyle` @@ -58,6 +62,7 @@ class InputText: "max_length", "custom_id", "id", + "description", ) def __init__( @@ -73,6 +78,7 @@ def __init__( value: str | None = None, row: int | None = None, id: int | None = None, + description: str | None = None, ): super().__init__() if len(str(label)) > 45: @@ -90,6 +96,7 @@ def __init__( f"expected custom_id to be str, not {custom_id.__class__.__name__}" ) custom_id = os.urandom(16).hex() if custom_id is None else custom_id + self.description: str | None = description self._underlying = InputTextComponent._raw_construct( type=ComponentType.input_text, @@ -236,3 +243,6 @@ def to_component_dict(self) -> InputTextComponentPayload: def refresh_state(self, data) -> None: self._input_value = data["value"] + + def uses_label(self) -> bool: + return self.description is not None diff --git a/discord/ui/item.py b/discord/ui/item.py index 7c324b0b2b..5b57a19d0f 100644 --- a/discord/ui/item.py +++ b/discord/ui/item.py @@ -107,6 +107,9 @@ def is_storable(self) -> bool: def is_persistent(self) -> bool: return not self.is_dispatchable() or self._provided_custom_id + def uses_label(self) -> bool: + return False + def copy_text(self) -> str: return "" diff --git a/discord/ui/modal.py b/discord/ui/modal.py index 58cf7db1e6..f4194ddc5d 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -9,6 +9,8 @@ from typing import TYPE_CHECKING, Any, Callable from .input_text import InputText +from .select import Select +from ..enums import ComponentType __all__ = ( "Modal", @@ -30,10 +32,14 @@ class Modal: .. versionadded:: 2.0 + .. versionchanged:: 2.7 + + :attr:`discord.ComponentType.string_select` can now be sent in modals. + Parameters ---------- - children: :class:`InputText` - The initial InputText fields that are displayed in the modal dialog. + children: Union[:class:`InputText`, :class:`Select`] + The initial InputText fields or Select Menus that are displayed in the modal dialog. title: :class:`str` The title of the modal dialog. Must be 45 characters or fewer. @@ -53,7 +59,7 @@ class Modal: def __init__( self, - *children: InputText, + *children: InputText | Select, title: str, custom_id: str | None = None, timeout: float | None = None, @@ -67,7 +73,7 @@ def __init__( if len(title) > 45: raise ValueError("title must be 45 characters or fewer") self._title = title - self._children: list[InputText] = list(children) + self._children: list[InputText | Select] = list(children) self._weights = _ModalWeights(self._children) loop = asyncio.get_running_loop() self._stopped: asyncio.Future[bool] = loop.create_future() @@ -138,18 +144,20 @@ def title(self, value: str): self._title = value @property - def children(self) -> list[InputText]: + def children(self) -> list[InputText | Select]: """The child components associated with the modal dialog.""" return self._children @children.setter - def children(self, value: list[InputText]): + def children(self, value: list[InputText | Select]): for item in value: - if not isinstance(item, InputText): + if not isinstance(item, (InputText, Select)): raise TypeError( - "all Modal children must be InputText, not" + "all Modal children must be InputText or Select, not" f" {item.__class__.__name__}" ) + elif isinstance(item, Select) and item.type is not ComponentType.string_select: + raise TypeError("only string selects may be added to modals") self._weights = _ModalWeights(self._children) self._children = value @@ -182,50 +190,69 @@ async def callback(self, interaction: Interaction): self.stop() def to_components(self) -> list[dict[str, Any]]: - def key(item: InputText) -> int: + def key(item: InputText | Select) -> int: return item._rendered_row or 0 children = sorted(self._children, key=key) components: list[dict[str, Any]] = [] for _, group in groupby(children, key=key): - children = [item.to_component_dict() for item in group] + labels = False + children = [] + for item in group: + if item.uses_label(): + labels = True + children.append(item) if not children: continue - components.append( - { - "type": 1, - "components": children, - } - ) + if labels: + for item in children: + item.append( + { + "type": 18, + "component": item.to_component_dict(), + "label": item.label, + "description": item.description, + "required": item.required, + } + ) + else: + components.append( + { + "type": 1, + "components": [item.to_component_dict() for item in children], + } + ) return components - def add_item(self, item: InputText) -> Self: - """Adds an InputText component to the modal dialog. + def add_item(self, item: InputText | Select) -> Self: + """Adds an InputText or Select component to the modal dialog. Parameters ---------- - item: :class:`InputText` + item: Union[:class:`InputText`, :class:`Select`] The item to add to the modal dialog """ if len(self._children) > 5: raise ValueError("You can only have up to 5 items in a modal dialog.") - if not isinstance(item, InputText): - raise TypeError(f"expected InputText not {item.__class__!r}") + if not isinstance(item, (InputText, Select)): + raise TypeError(f"expected InputText or Select, not {item.__class__!r}") + if isinstance(item, Select) and item.type is not ComponentType.string_select: + raise TypeError("only string selects may be added to modals") self._weights.add_item(item) self._children.append(item) return self - def remove_item(self, item: InputText) -> Self: - """Removes an InputText component from the modal dialog. + def remove_item(self, item: InputText | Select) -> Self: + """Removes an InputText or Select component from the modal dialog. Parameters ---------- - item: :class:`InputText` + item: Union[:class:`InputText`, :class:`Select`] The item to remove from the modal dialog. """ try: @@ -280,7 +307,7 @@ async def on_timeout(self) -> None: class _ModalWeights: __slots__ = ("weights",) - def __init__(self, children: list[InputText]): + def __init__(self, children: list[InputText | Select]): self.weights: list[int] = [0, 0, 0, 0, 0] key = lambda i: sys.maxsize if i.row is None else i.row @@ -289,14 +316,14 @@ def __init__(self, children: list[InputText]): for item in group: self.add_item(item) - def find_open_space(self, item: InputText) -> int: + def find_open_space(self, item: InputText | Select) -> int: for index, weight in enumerate(self.weights): if weight + item.width <= 5: return index raise ValueError("could not find open space for item") - def add_item(self, item: InputText) -> None: + def add_item(self, item: InputText | Select) -> None: if item.row is not None: total = self.weights[item.row] + item.width if total > 5: @@ -310,7 +337,7 @@ def add_item(self, item: InputText) -> None: self.weights[index] += item.width item._rendered_row = index - def remove_item(self, item: InputText) -> None: + def remove_item(self, item: InputText | Select) -> None: if item._rendered_row is not None: self.weights[item._rendered_row] -= item.width item._rendered_row = None diff --git a/discord/ui/select.py b/discord/ui/select.py index 7c2f8b1f4a..9818893d55 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -80,6 +80,11 @@ class Select(Item[V]): :attr:`discord.ComponentType.role_select`, :attr:`discord.ComponentType.mentionable_select`, and :attr:`discord.ComponentType.channel_select`. + .. versionchanged:: 2.7 + + :attr:`discord.ComponentType.string_select` can now be sent in :class:`discord.ui.Modal`. + Added support for :attr:`label`, :attr:`description`, and :attr:`required` when being sent in modals. + Parameters ---------- select_type: :class:`discord.ComponentType` @@ -126,6 +131,9 @@ class Select(Item[V]): "disabled", "custom_id", "id", + "label", + "description", + "required", ) def __init__( @@ -141,9 +149,14 @@ def __init__( disabled: bool = False, row: int | None = None, id: int | None = None, + label: str | None = None, + description: str | None = None, + required: str | None = False, ) -> None: if options and select_type is not ComponentType.string_select: raise InvalidArgument("options parameter is only valid for string selects") + if (label or description or disabled) and select_type is not ComponentType.string_select: + raise InvalidArgument("label, description and required parameters are only valid for selects in modals") if channel_types and select_type is not ComponentType.channel_select: raise InvalidArgument( "channel_types parameter is only valid for channel selects" @@ -162,6 +175,10 @@ def __init__( f"expected custom_id to be str, not {custom_id.__class__.__name__}" ) + self.label: str | None = label + self.description: str | None = description + self.required: bool | None = required + self._provided_custom_id = custom_id is not None custom_id = os.urandom(16).hex() if custom_id is None else custom_id self._underlying: SelectMenu = SelectMenu._raw_construct( @@ -450,6 +467,8 @@ def is_dispatchable(self) -> bool: def is_storable(self) -> bool: return True + def uses_label(self) -> bool: + return bool(self.label or self.description or (self.required is not None)) _select_types = ( ComponentType.string_select, diff --git a/discord/ui/view.py b/discord/ui/view.py index 64b0520172..128bfd7a8d 100644 --- a/discord/ui/view.py +++ b/discord/ui/view.py @@ -44,6 +44,7 @@ from ..components import Separator as SeparatorComponent from ..components import TextDisplay as TextDisplayComponent from ..components import Thumbnail as ThumbnailComponent +from ..components import Label as LabelComponent from ..components import _component_factory from ..utils import find, get from .item import Item, ItemCallbackType @@ -120,6 +121,12 @@ def _component_to_item(component: Component) -> Item[V]: # Handle ActionRow.children manually, or design ui.ActionRow? return component + if isinstance(component, LabelComponent): + ret = _component_to_item(component.component) + ret.label = component.label + ret.description = component.description + ret.required = component.required + return ret return Item.from_component(component) @@ -392,6 +399,9 @@ def add_item(self, item: Item[V]) -> None: if not isinstance(item, Item): raise TypeError(f"expected Item not {item.__class__!r}") + if item.uses_label(): + raise ValueError(f"cannot use label, description or required on select menus in views.") + self.__weights.add_item(item) item.parent = self From be6f1c0da46ada4fb8a15b5b493986f0eb1d4247 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 23:35:43 +0000 Subject: [PATCH 06/45] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/ui/modal.py | 7 +++++-- discord/ui/select.py | 9 +++++++-- discord/ui/view.py | 6 ++++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/discord/ui/modal.py b/discord/ui/modal.py index f4194ddc5d..6f0874dba3 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -8,9 +8,9 @@ from itertools import groupby from typing import TYPE_CHECKING, Any, Callable +from ..enums import ComponentType from .input_text import InputText from .select import Select -from ..enums import ComponentType __all__ = ( "Modal", @@ -156,7 +156,10 @@ def children(self, value: list[InputText | Select]): "all Modal children must be InputText or Select, not" f" {item.__class__.__name__}" ) - elif isinstance(item, Select) and item.type is not ComponentType.string_select: + elif ( + isinstance(item, Select) + and item.type is not ComponentType.string_select + ): raise TypeError("only string selects may be added to modals") self._weights = _ModalWeights(self._children) self._children = value diff --git a/discord/ui/select.py b/discord/ui/select.py index 9818893d55..db529aa1ca 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -155,8 +155,12 @@ def __init__( ) -> None: if options and select_type is not ComponentType.string_select: raise InvalidArgument("options parameter is only valid for string selects") - if (label or description or disabled) and select_type is not ComponentType.string_select: - raise InvalidArgument("label, description and required parameters are only valid for selects in modals") + if ( + label or description or disabled + ) and select_type is not ComponentType.string_select: + raise InvalidArgument( + "label, description and required parameters are only valid for selects in modals" + ) if channel_types and select_type is not ComponentType.channel_select: raise InvalidArgument( "channel_types parameter is only valid for channel selects" @@ -470,6 +474,7 @@ def is_storable(self) -> bool: def uses_label(self) -> bool: return bool(self.label or self.description or (self.required is not None)) + _select_types = ( ComponentType.string_select, ComponentType.user_select, diff --git a/discord/ui/view.py b/discord/ui/view.py index 128bfd7a8d..5bd27aeca5 100644 --- a/discord/ui/view.py +++ b/discord/ui/view.py @@ -38,13 +38,13 @@ from ..components import Component from ..components import Container as ContainerComponent from ..components import FileComponent +from ..components import Label as LabelComponent from ..components import MediaGallery as MediaGalleryComponent from ..components import Section as SectionComponent from ..components import SelectMenu as SelectComponent from ..components import Separator as SeparatorComponent from ..components import TextDisplay as TextDisplayComponent from ..components import Thumbnail as ThumbnailComponent -from ..components import Label as LabelComponent from ..components import _component_factory from ..utils import find, get from .item import Item, ItemCallbackType @@ -400,7 +400,9 @@ def add_item(self, item: Item[V]) -> None: raise TypeError(f"expected Item not {item.__class__!r}") if item.uses_label(): - raise ValueError(f"cannot use label, description or required on select menus in views.") + raise ValueError( + f"cannot use label, description or required on select menus in views." + ) self.__weights.add_item(item) From e76fd92e0b0bba1af89f55dc27cd68ab4393099e Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Thu, 7 Aug 2025 00:44:47 +0100 Subject: [PATCH 07/45] interaction._raw_data for testing --- discord/interactions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/discord/interactions.py b/discord/interactions.py index 888ed7658f..f4a4aed2d9 100644 --- a/discord/interactions.py +++ b/discord/interactions.py @@ -215,6 +215,7 @@ def __init__(self, *, data: InteractionPayload, state: ConnectionState): self._from_data(data) def _from_data(self, data: InteractionPayload): + self._raw_data: InteractionPayload = data self.id: int = int(data["id"]) self.type: InteractionType = try_enum(InteractionType, data["type"]) self.data: InteractionData | None = data.get("data") From 20f1096bf62a358b2c695e01e88c0922d407503a Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Thu, 7 Aug 2025 01:01:28 +0100 Subject: [PATCH 08/45] append --- discord/ui/modal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/ui/modal.py b/discord/ui/modal.py index 6f0874dba3..28c7da9d12 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -210,7 +210,7 @@ def key(item: InputText | Select) -> int: if labels: for item in children: - item.append( + components.append( { "type": 18, "component": item.to_component_dict(), From e1c37602ce3acf59436c55931f62ac7839250f6a Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Thu, 7 Aug 2025 01:13:33 +0100 Subject: [PATCH 09/45] move required around --- discord/ui/modal.py | 1 - discord/ui/select.py | 14 ++++++++++++-- discord/ui/view.py | 1 - 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/discord/ui/modal.py b/discord/ui/modal.py index 28c7da9d12..3d95fbb5e3 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -216,7 +216,6 @@ def key(item: InputText | Select) -> int: "component": item.to_component_dict(), "label": item.label, "description": item.description, - "required": item.required, } ) else: diff --git a/discord/ui/select.py b/discord/ui/select.py index db529aa1ca..c44c8705fe 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -156,7 +156,7 @@ def __init__( if options and select_type is not ComponentType.string_select: raise InvalidArgument("options parameter is only valid for string selects") if ( - label or description or disabled + label or description or required ) and select_type is not ComponentType.string_select: raise InvalidArgument( "label, description and required parameters are only valid for selects in modals" @@ -181,7 +181,6 @@ def __init__( self.label: str | None = label self.description: str | None = description - self.required: bool | None = required self._provided_custom_id = custom_id is not None custom_id = os.urandom(16).hex() if custom_id is None else custom_id @@ -195,6 +194,7 @@ def __init__( options=options or [], channel_types=channel_types or [], id=id, + required=required, ) self.row = row @@ -253,6 +253,15 @@ def disabled(self) -> bool: """Whether the select is disabled or not.""" return self._underlying.disabled + @property + def required(self) -> bool: + """Whether the select is required or not. Only applicable in modal selects.""" + return self._underlying.required + + @required.setter + def required(self, value: bool): + self._underlying.required = bool(value) + @disabled.setter def disabled(self, value: bool): self._underlying.disabled = bool(value) @@ -459,6 +468,7 @@ def from_component(cls: type[S], component: SelectMenu) -> S: disabled=component.disabled, row=None, id=component.id, + required=component.required, ) @property diff --git a/discord/ui/view.py b/discord/ui/view.py index 5bd27aeca5..43dd9cccba 100644 --- a/discord/ui/view.py +++ b/discord/ui/view.py @@ -125,7 +125,6 @@ def _component_to_item(component: Component) -> Item[V]: ret = _component_to_item(component.component) ret.label = component.label ret.description = component.description - ret.required = component.required return ret return Item.from_component(component) From cf33b2bbe54df3e641e705dfad60b5500e105a03 Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Thu, 7 Aug 2025 01:15:41 +0100 Subject: [PATCH 10/45] raw --- discord/interactions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/discord/interactions.py b/discord/interactions.py index f4a4aed2d9..5b29cdb336 100644 --- a/discord/interactions.py +++ b/discord/interactions.py @@ -193,6 +193,7 @@ class Interaction: "view", "modal", "attachment_size_limit", + "_raw_data", "_channel_data", "_message_data", "_guild_data", From 132a63f41df12c8cbe16fb84574da44a85f4e7d7 Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Thu, 7 Aug 2025 01:19:21 +0100 Subject: [PATCH 11/45] req? --- discord/ui/select.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/ui/select.py b/discord/ui/select.py index c44c8705fe..76b8e62acc 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -260,7 +260,7 @@ def required(self) -> bool: @required.setter def required(self, value: bool): - self._underlying.required = bool(value) + self._underlying.required = value @disabled.setter def disabled(self, value: bool): From bf614aad4fa38b7651fdac476bdfdbab16d6d8c2 Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Thu, 7 Aug 2025 01:20:48 +0100 Subject: [PATCH 12/45] default required none --- discord/ui/select.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/ui/select.py b/discord/ui/select.py index 76b8e62acc..16976b7446 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -151,7 +151,7 @@ def __init__( id: int | None = None, label: str | None = None, description: str | None = None, - required: str | None = False, + required: str | None = None, ) -> None: if options and select_type is not ComponentType.string_select: raise InvalidArgument("options parameter is only valid for string selects") From 20b39bff2fc9a8e807e9bc7798145ec6b3e559db Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Thu, 7 Aug 2025 01:23:43 +0100 Subject: [PATCH 13/45] select always uses labelcomponent --- discord/ui/select.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/ui/select.py b/discord/ui/select.py index 16976b7446..2e6d0a1899 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -482,7 +482,7 @@ def is_storable(self) -> bool: return True def uses_label(self) -> bool: - return bool(self.label or self.description or (self.required is not None)) + return True _select_types = ( From 24f018b421cc89511fe8287cae9be281fddf7fcc Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Thu, 7 Aug 2025 01:27:25 +0100 Subject: [PATCH 14/45] labels --- discord/ui/modal.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/discord/ui/modal.py b/discord/ui/modal.py index 3d95fbb5e3..1a64d35239 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -210,11 +210,13 @@ def key(item: InputText | Select) -> int: if labels: for item in children: + component = item.to_component_dict() + label = component.pop("label", item.label) components.append( { "type": 18, - "component": item.to_component_dict(), - "label": item.label, + "component": component, + "label": label, "description": item.description, } ) @@ -244,6 +246,8 @@ def add_item(self, item: InputText | Select) -> Self: raise TypeError(f"expected InputText or Select, not {item.__class__!r}") if isinstance(item, Select) and item.type is not ComponentType.string_select: raise TypeError("only string selects may be added to modals") + if not item.label: + raise ValueError("Item must have a label set") self._weights.add_item(item) self._children.append(item) From 7dd44f5ab3a2ed5a91ce7cffa1998e277d98d852 Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Thu, 7 Aug 2025 01:41:06 +0100 Subject: [PATCH 15/45] store --- discord/ui/modal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/ui/modal.py b/discord/ui/modal.py index 1a64d35239..ffaac995c5 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -377,7 +377,7 @@ async def dispatch(self, user_id: int, custom_id: str, interaction: Interaction) components = [ component for parent_component in interaction.data["components"] - for component in parent_component["components"] + for component in (parent_component.get("components") or ([x] if x := parent_component.get("component") else [])) ] for component in components: for child in value.children: From 4be7d146db9113c1adea48418969d4f613ab43e3 Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Thu, 7 Aug 2025 01:43:30 +0100 Subject: [PATCH 16/45] := --- discord/ui/modal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/ui/modal.py b/discord/ui/modal.py index ffaac995c5..b9aa74786e 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -377,7 +377,7 @@ async def dispatch(self, user_id: int, custom_id: str, interaction: Interaction) components = [ component for parent_component in interaction.data["components"] - for component in (parent_component.get("components") or ([x] if x := parent_component.get("component") else [])) + for component in (parent_component.get("components") or ([x] if (x := parent_component.get("component")) else [])) ] for component in components: for child in value.children: From 2c3ab0dbeb140fafaf89eed165f06c882e6417b3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 00:44:21 +0000 Subject: [PATCH 17/45] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/ui/modal.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/discord/ui/modal.py b/discord/ui/modal.py index b9aa74786e..8ff8194bda 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -377,7 +377,10 @@ async def dispatch(self, user_id: int, custom_id: str, interaction: Interaction) components = [ component for parent_component in interaction.data["components"] - for component in (parent_component.get("components") or ([x] if (x := parent_component.get("component")) else [])) + for component in ( + parent_component.get("components") + or ([x] if (x := parent_component.get("component")) else []) + ) ] for component in components: for child in value.children: From 9a756353511ace05009a6d278fe8f6351a7ae42e Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Thu, 7 Aug 2025 01:45:41 +0100 Subject: [PATCH 18/45] again --- discord/ui/modal.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/discord/ui/modal.py b/discord/ui/modal.py index 8ff8194bda..df07a8783c 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -377,10 +377,7 @@ async def dispatch(self, user_id: int, custom_id: str, interaction: Interaction) components = [ component for parent_component in interaction.data["components"] - for component in ( - parent_component.get("components") - or ([x] if (x := parent_component.get("component")) else []) - ) + for component in (parent_component.get("components") or ([parent_component.get("component")] if parent_component.get("component") else [])) ] for component in components: for child in value.children: From 0ddc5d27aa4bd109b15a57e72468baff2c75ca14 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 00:46:34 +0000 Subject: [PATCH 19/45] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/ui/modal.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/discord/ui/modal.py b/discord/ui/modal.py index df07a8783c..1eb2b58aa3 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -377,7 +377,14 @@ async def dispatch(self, user_id: int, custom_id: str, interaction: Interaction) components = [ component for parent_component in interaction.data["components"] - for component in (parent_component.get("components") or ([parent_component.get("component")] if parent_component.get("component") else [])) + for component in ( + parent_component.get("components") + or ( + [parent_component.get("component")] + if parent_component.get("component") + else [] + ) + ) ] for component in components: for child in value.children: From 68ecdd4372c8ed3b7575036bef46341c9076553c Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Thu, 7 Aug 2025 01:48:38 +0100 Subject: [PATCH 20/45] refresh_state --- discord/ui/select.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/ui/select.py b/discord/ui/select.py index 2e6d0a1899..ef7e879a3a 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -450,8 +450,8 @@ def to_component_dict(self) -> SelectMenuPayload: def refresh_component(self, component: SelectMenu) -> None: self._underlying = component - def refresh_state(self, interaction: Interaction) -> None: - data: ComponentInteractionData = interaction.data # type: ignore + def refresh_state(self, interaction: Interaction | dict) -> None: + data: ComponentInteractionData = interaction.data if isinstance(interaction, Interaction) else interaction self._selected_values = data.get("values", []) self._interaction = interaction From a54c4498d4ed74b99d3b3d81a901b848d4a71916 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 00:49:04 +0000 Subject: [PATCH 21/45] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/ui/select.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/discord/ui/select.py b/discord/ui/select.py index ef7e879a3a..ab6a5768f1 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -451,7 +451,9 @@ def refresh_component(self, component: SelectMenu) -> None: self._underlying = component def refresh_state(self, interaction: Interaction | dict) -> None: - data: ComponentInteractionData = interaction.data if isinstance(interaction, Interaction) else interaction + data: ComponentInteractionData = ( + interaction.data if isinstance(interaction, Interaction) else interaction + ) self._selected_values = data.get("values", []) self._interaction = interaction From 5b53a3a4642e78cd865f5007156b26e641f8833e Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Thu, 7 Aug 2025 02:05:10 +0100 Subject: [PATCH 22/45] docs and limits; unsure on legacy textinput label limit --- discord/ui/input_text.py | 5 +++++ discord/ui/select.py | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/discord/ui/input_text.py b/discord/ui/input_text.py index 8d45ffd5ce..9739c9f2e2 100644 --- a/discord/ui/input_text.py +++ b/discord/ui/input_text.py @@ -30,6 +30,9 @@ class InputText: label: :class:`str` The label for the input text field. Must be 45 characters or fewer. + description: Optional[:class:`str`] + The description for the input text field. + Must be 100 characters or fewer. placeholder: Optional[:class:`str`] The placeholder text that is shown if nothing is selected, if any. Must be 100 characters or fewer. @@ -83,6 +86,8 @@ def __init__( super().__init__() if len(str(label)) > 45: raise ValueError("label must be 45 characters or fewer") + if description and len(description) > 100: + raise ValueError("description must be 100 characters or fewer") if min_length and (min_length < 0 or min_length > 4000): raise ValueError("min_length must be between 0 and 4000") if max_length and (max_length < 0 or max_length > 4000): diff --git a/discord/ui/select.py b/discord/ui/select.py index ab6a5768f1..7c8437dca9 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -119,6 +119,14 @@ class Select(Item[V]): ordering. The row number must be between 0 and 4 (i.e. zero indexed). id: Optional[:class:`int`] The select menu's ID. + label: Optional[:class:`str`] + The label for the select menu. Only useable in modals. + Must be 100 characters or fewer. + description: Optional[:class:`str`] + The description for the select menu. Only useable in modals. + Must be 100 characters or fewer. + required: Optional[:class:`bool`] + Whether the select is required or not. Only useable in modals. Defaults to ``False``. """ __item_repr_attributes__: tuple[str, ...] = ( @@ -161,6 +169,10 @@ def __init__( raise InvalidArgument( "label, description and required parameters are only valid for selects in modals" ) + if label and len(label) > 100: + raise ValueError("label must be 100 characters or fewer") + if description and len(description) > 100: + raise ValueError("description must be 100 characters or fewer") if channel_types and select_type is not ComponentType.channel_select: raise InvalidArgument( "channel_types parameter is only valid for channel selects" From e70b067f7c7948356c8325afc1761f387369b2b7 Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Thu, 7 Aug 2025 02:23:28 +0100 Subject: [PATCH 23/45] uses_label --- discord/ui/modal.py | 2 +- discord/ui/select.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/ui/modal.py b/discord/ui/modal.py index 1eb2b58aa3..65eedb354f 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -202,7 +202,7 @@ def key(item: InputText | Select) -> int: labels = False children = [] for item in group: - if item.uses_label(): + if item.uses_label() or isinstance(item, Select): labels = True children.append(item) if not children: diff --git a/discord/ui/select.py b/discord/ui/select.py index 7c8437dca9..f7b0d350ef 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -496,7 +496,7 @@ def is_storable(self) -> bool: return True def uses_label(self) -> bool: - return True + return bool(self.label or self.description or (self.required is not None)) _select_types = ( From d9e693901598cbd9f2fda804fbb83591810b57f1 Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Thu, 7 Aug 2025 15:31:25 +0100 Subject: [PATCH 24/45] update limits --- discord/components.py | 4 ++-- discord/ui/select.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/discord/components.py b/discord/components.py index da021f0ab0..ecba1382fb 100644 --- a/discord/components.py +++ b/discord/components.py @@ -1069,9 +1069,9 @@ class Label(Component): component: :class:`Component` The component contained in this label. Currently supports :class:`InputText` and :class:`SelectMenu`. label: :class:`str` - The main text associated with this label's ``component``. + The main text associated with this label's ``component``, up to 45 characters. description: Optional[:class:`str`] - The description associated with this label's ``component``. + The description associated with this label's ``component``, up to 100 characters. """ __slots__: tuple[str, ...] = ("component", "label", "description") diff --git a/discord/ui/select.py b/discord/ui/select.py index f7b0d350ef..08c4f8b146 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -121,7 +121,7 @@ class Select(Item[V]): The select menu's ID. label: Optional[:class:`str`] The label for the select menu. Only useable in modals. - Must be 100 characters or fewer. + Must be 45 characters or fewer. description: Optional[:class:`str`] The description for the select menu. Only useable in modals. Must be 100 characters or fewer. @@ -169,8 +169,8 @@ def __init__( raise InvalidArgument( "label, description and required parameters are only valid for selects in modals" ) - if label and len(label) > 100: - raise ValueError("label must be 100 characters or fewer") + if label and len(label) > 45: + raise ValueError("label must be 45 characters or fewer") if description and len(description) > 100: raise ValueError("description must be 100 characters or fewer") if channel_types and select_type is not ComponentType.channel_select: From a1ded7eb8705ee6759b6b3fd3af4c0458acfdaa1 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Fri, 8 Aug 2025 00:00:07 +0200 Subject: [PATCH 25/45] Update discord/ui/select.py Co-authored-by: DA344 <108473820+DA-344@users.noreply.github.com> Signed-off-by: Lala Sabathil --- discord/ui/select.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/discord/ui/select.py b/discord/ui/select.py index 08c4f8b146..7615d79bb4 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -122,11 +122,17 @@ class Select(Item[V]): label: Optional[:class:`str`] The label for the select menu. Only useable in modals. Must be 45 characters or fewer. + + .. versionadded:: 2.7 description: Optional[:class:`str`] The description for the select menu. Only useable in modals. Must be 100 characters or fewer. + + .. versionadded:: 2.7 required: Optional[:class:`bool`] Whether the select is required or not. Only useable in modals. Defaults to ``False``. + + .. versionadded:: 2.7 """ __item_repr_attributes__: tuple[str, ...] = ( From 4455bda18321f4d1312de3c91e6aefaf04d03cc1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 22:01:20 +0000 Subject: [PATCH 26/45] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/ui/select.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/discord/ui/select.py b/discord/ui/select.py index 7615d79bb4..876e4cdf0e 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -122,16 +122,16 @@ class Select(Item[V]): label: Optional[:class:`str`] The label for the select menu. Only useable in modals. Must be 45 characters or fewer. - + .. versionadded:: 2.7 description: Optional[:class:`str`] The description for the select menu. Only useable in modals. Must be 100 characters or fewer. - + .. versionadded:: 2.7 required: Optional[:class:`bool`] Whether the select is required or not. Only useable in modals. Defaults to ``False``. - + .. versionadded:: 2.7 """ From 0cf4938b44b9b47cbbd5390025ab0b065d4b088a Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Thu, 7 Aug 2025 23:40:10 +0100 Subject: [PATCH 27/45] v --- discord/ui/input_text.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/discord/ui/input_text.py b/discord/ui/input_text.py index 9739c9f2e2..178d18b225 100644 --- a/discord/ui/input_text.py +++ b/discord/ui/input_text.py @@ -17,10 +17,6 @@ class InputText: .. versionadded:: 2.0 - .. versionchanged:: 2.7 - - Added :attr:`description`. - Parameters ---------- style: :class:`~discord.InputTextStyle` @@ -33,6 +29,8 @@ class InputText: description: Optional[:class:`str`] The description for the input text field. Must be 100 characters or fewer. + + .. versionadded:: 2.7 placeholder: Optional[:class:`str`] The placeholder text that is shown if nothing is selected, if any. Must be 100 characters or fewer. From ac8b012f1482d966771948ffd15c8643880e4b77 Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Thu, 7 Aug 2025 23:42:10 +0100 Subject: [PATCH 28/45] remove clarification --- discord/ui/select.py | 1 - 1 file changed, 1 deletion(-) diff --git a/discord/ui/select.py b/discord/ui/select.py index 876e4cdf0e..d40f839987 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -83,7 +83,6 @@ class Select(Item[V]): .. versionchanged:: 2.7 :attr:`discord.ComponentType.string_select` can now be sent in :class:`discord.ui.Modal`. - Added support for :attr:`label`, :attr:`description`, and :attr:`required` when being sent in modals. Parameters ---------- From f6b0d59f80d734b6f2545cc32d9456398ba2b98a Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Fri, 8 Aug 2025 02:46:57 +0100 Subject: [PATCH 29/45] true --- discord/ui/select.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/ui/select.py b/discord/ui/select.py index d40f839987..d052a6878c 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -129,7 +129,7 @@ class Select(Item[V]): .. versionadded:: 2.7 required: Optional[:class:`bool`] - Whether the select is required or not. Only useable in modals. Defaults to ``False``. + Whether the select is required or not. Only useable in modals. Defaults to ``True`` in modals. .. versionadded:: 2.7 """ From 64f24067e06b3f8fd5c20b322cdd4fe4d64f097e Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Fri, 8 Aug 2025 02:50:46 +0100 Subject: [PATCH 30/45] required check --- discord/ui/select.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/discord/ui/select.py b/discord/ui/select.py index d052a6878c..42009c612e 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -182,6 +182,8 @@ def __init__( raise InvalidArgument( "channel_types parameter is only valid for channel selects" ) + if required and min_values < 1: + raise ValueError("min_values must be greater than 0 when required=True") super().__init__() self._selected_values: list[str] = [] self._interaction: Interaction | None = None From 18cf7fa8ca5d67f0133b7903a94a240b53a29637 Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Tue, 12 Aug 2025 08:00:43 +0100 Subject: [PATCH 31/45] cl --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d76398448..6d462a09bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,8 @@ These changes are available on the `master` branch, but have not yet been releas ([#2818](https://github.com/Pycord-Development/pycord/pull/2818)) - Added `Interaction.attachment_size_limit`. ([#2854](https://github.com/Pycord-Development/pycord/pull/2854)) +- Added support for adding string selects to modals + ([#2858](https://github.com/Pycord-Development/pycord/pull/2858)) ### Fixed From 9e29a09df3b28bc6cf3f43fceaa0eb48788d4428 Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Tue, 12 Aug 2025 14:25:00 +0100 Subject: [PATCH 32/45] Update CHANGELOG.md Co-authored-by: Lala Sabathil Signed-off-by: UK <41271523+NeloBlivion@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d462a09bc..26c1d6c434 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,7 +69,7 @@ These changes are available on the `master` branch, but have not yet been releas ([#2818](https://github.com/Pycord-Development/pycord/pull/2818)) - Added `Interaction.attachment_size_limit`. ([#2854](https://github.com/Pycord-Development/pycord/pull/2854)) -- Added support for adding string selects to modals +- Added support for string selects in modals ([#2858](https://github.com/Pycord-Development/pycord/pull/2858)) ### Fixed From 7dfe5c2095e4ee7dabefdaa2371250b92b815890 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 12 Aug 2025 16:20:51 +0200 Subject: [PATCH 33/45] Apply suggestions from code review Co-authored-by: Dorukyum <53639936+Dorukyum@users.noreply.github.com> Signed-off-by: Lala Sabathil --- CHANGELOG.md | 2 +- discord/components.py | 6 +++--- discord/ui/modal.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26c1d6c434..6f7a28343c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,7 +69,7 @@ These changes are available on the `master` branch, but have not yet been releas ([#2818](https://github.com/Pycord-Development/pycord/pull/2818)) - Added `Interaction.attachment_size_limit`. ([#2854](https://github.com/Pycord-Development/pycord/pull/2854)) -- Added support for string selects in modals +- Added support for string selects in modals. ([#2858](https://github.com/Pycord-Development/pycord/pull/2858)) ### Fixed diff --git a/discord/components.py b/discord/components.py index ecba1382fb..2420db40d7 100644 --- a/discord/components.py +++ b/discord/components.py @@ -1081,9 +1081,9 @@ class Label(Component): def __init__(self, data: LabelComponentPayload): self.type: ComponentType = try_enum(ComponentType, data["type"]) - self.id: int = data.get("id") - self.component: Component = _component_factory(data.get("component", {})) - self.label: str = data.get("label") + self.id: int = data["id"] + self.component: Component = _component_factory(data["component"]) + self.label: str = data["label"] self.description: str | None = data.get("description") def to_dict(self) -> LabelComponentPayload: diff --git a/discord/ui/modal.py b/discord/ui/modal.py index 65eedb354f..0e10e3e028 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -34,12 +34,12 @@ class Modal: .. versionchanged:: 2.7 - :attr:`discord.ComponentType.string_select` can now be sent in modals. + :attr:`discord.ComponentType.string_select` can now be used in modals. Parameters ---------- children: Union[:class:`InputText`, :class:`Select`] - The initial InputText fields or Select Menus that are displayed in the modal dialog. + The initial InputText or Select components that are displayed in the modal dialog. title: :class:`str` The title of the modal dialog. Must be 45 characters or fewer. From 985c640668426966280042f3ffd240a331a3118a Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Thu, 21 Aug 2025 16:24:00 +0100 Subject: [PATCH 34/45] required --- discord/components.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/components.py b/discord/components.py index 2420db40d7..e5b538ddc0 100644 --- a/discord/components.py +++ b/discord/components.py @@ -439,7 +439,7 @@ def __init__(self, data: SelectMenuPayload): ] self.required: bool | None = data.get( "required" - ) # Currently defaults to False, pending change + ) def to_dict(self) -> SelectMenuPayload: payload: SelectMenuPayload = { From fa77faa2a07506ef3f96e19055a346a665e3e0fc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 15:24:26 +0000 Subject: [PATCH 35/45] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/components.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/discord/components.py b/discord/components.py index e5b538ddc0..710f836e7f 100644 --- a/discord/components.py +++ b/discord/components.py @@ -437,9 +437,7 @@ def __init__(self, data: SelectMenuPayload): self.channel_types: list[ChannelType] = [ try_enum(ChannelType, ct) for ct in data.get("channel_types", []) ] - self.required: bool | None = data.get( - "required" - ) + self.required: bool | None = data.get("required") def to_dict(self) -> SelectMenuPayload: payload: SelectMenuPayload = { From efe8fe61f947679b25ac4991671eaac2eca18434 Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Thu, 21 Aug 2025 16:29:14 +0100 Subject: [PATCH 36/45] add Modal.get_item --- discord/ui/modal.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/discord/ui/modal.py b/discord/ui/modal.py index 0e10e3e028..f21f3ede9e 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Any, Callable from ..enums import ComponentType +from ..utils import find from .input_text import InputText from .select import Select @@ -267,6 +268,25 @@ def remove_item(self, item: InputText | Select) -> Self: pass return self + def get_item(self, id: str | int) -> Select | InputText | None: + """Gets an item from the modal. Roughly equal to `utils.get(modal.children, ...)`. + If an :class:`int` is provided, the item will be retrieved by ``id``, otherwise by ``custom_id``. + + Parameters + ---------- + id: Union[:class:`int`, :class:`str`] + The id or custom_id of the item to get + + Returns + ------- + Optional[:class:`Item`] + The item with the matching ``custom_id`` or ``id`` if it exists. + """ + if not id: + return None + attr = "id" if isinstance(id, int) else "custom_id" + return find(lambda i: getattr(i, attr, None) == id, self.children) + def stop(self) -> None: """Stops listening to interaction events from the modal dialog.""" if not self._stopped.done(): From 785a357d80eaae97f88d4a515c277d8eb0fd2f61 Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Sat, 23 Aug 2025 00:15:23 +0100 Subject: [PATCH 37/45] TextDisplay and all Selects in modals --- discord/ui/modal.py | 63 +++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/discord/ui/modal.py b/discord/ui/modal.py index f21f3ede9e..04f05e8a50 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -6,12 +6,14 @@ import time from functools import partial from itertools import groupby -from typing import TYPE_CHECKING, Any, Callable +from typing import TYPE_CHECKING, Any, Callable, TypeVar from ..enums import ComponentType from ..utils import find from .input_text import InputText +from .item import Item from .select import Select +from .text_display import TextDisplay __all__ = ( "Modal", @@ -25,6 +27,8 @@ from ..interactions import Interaction from ..state import ConnectionState +M = TypeVar("M", bound="Modal", covariant=True) + class Modal: """Represents a UI Modal dialog. @@ -35,12 +39,12 @@ class Modal: .. versionchanged:: 2.7 - :attr:`discord.ComponentType.string_select` can now be used in modals. + :class:`discord.ui.Select` and :class:`discord.ui.TextDisplay` can now be used in modals. Parameters ---------- - children: Union[:class:`InputText`, :class:`Select`] - The initial InputText or Select components that are displayed in the modal dialog. + children: Union[:class:`InputText`, :class:`Select`, :class:`TextDisplay`] + The initial InputText, Select, or TextDisplay components that are displayed in the modal dialog. title: :class:`str` The title of the modal dialog. Must be 45 characters or fewer. @@ -60,7 +64,7 @@ class Modal: def __init__( self, - *children: InputText | Select, + *children: Item[M], title: str, custom_id: str | None = None, timeout: float | None = None, @@ -74,7 +78,7 @@ def __init__( if len(title) > 45: raise ValueError("title must be 45 characters or fewer") self._title = title - self._children: list[InputText | Select] = list(children) + self._children: list[Item[M]] = list(children) self._weights = _ModalWeights(self._children) loop = asyncio.get_running_loop() self._stopped: asyncio.Future[bool] = loop.create_future() @@ -145,23 +149,18 @@ def title(self, value: str): self._title = value @property - def children(self) -> list[InputText | Select]: + def children(self) -> list[Item[M]]: """The child components associated with the modal dialog.""" return self._children @children.setter - def children(self, value: list[InputText | Select]): + def children(self, value: list[Item[M]]): for item in value: - if not isinstance(item, (InputText, Select)): + if not isinstance(item, (InputText, Select, TextDisplay)): raise TypeError( - "all Modal children must be InputText or Select, not" + "all Modal children must be InputText, Select, or TextDisplay, not" f" {item.__class__.__name__}" ) - elif ( - isinstance(item, Select) - and item.type is not ComponentType.string_select - ): - raise TypeError("only string selects may be added to modals") self._weights = _ModalWeights(self._children) self._children = value @@ -194,7 +193,7 @@ async def callback(self, interaction: Interaction): self.stop() def to_components(self) -> list[dict[str, Any]]: - def key(item: InputText | Select) -> int: + def key(item: Item[M]) -> int: return item._rendered_row or 0 children = sorted(self._children, key=key) @@ -231,35 +230,33 @@ def key(item: InputText | Select) -> int: return components - def add_item(self, item: InputText | Select) -> Self: - """Adds an InputText or Select component to the modal dialog. + def add_item(self, item: Item[M]) -> Self: + """Adds a component to the modal dialog. Parameters ---------- - item: Union[:class:`InputText`, :class:`Select`] + item: Union[:class:`Item`] The item to add to the modal dialog """ if len(self._children) > 5: raise ValueError("You can only have up to 5 items in a modal dialog.") - if not isinstance(item, (InputText, Select)): - raise TypeError(f"expected InputText or Select, not {item.__class__!r}") - if isinstance(item, Select) and item.type is not ComponentType.string_select: - raise TypeError("only string selects may be added to modals") - if not item.label: - raise ValueError("Item must have a label set") + if not isinstance(item, (InputText, Select, TextDisplay)): + raise TypeError(f"expected InputText, Select, or TextDisplay, not {item.__class__!r}") + if isinstance(item, (InputText, Select)) and not item.label: + raise ValueError("InputTexts and Selects must have a label set") self._weights.add_item(item) self._children.append(item) return self - def remove_item(self, item: InputText | Select) -> Self: - """Removes an InputText or Select component from the modal dialog. + def remove_item(self, item: Item[M]) -> Self: + """Removes a component from the modal dialog. Parameters ---------- - item: Union[:class:`InputText`, :class:`Select`] + item: Union[:class:`Item`] The item to remove from the modal dialog. """ try: @@ -268,7 +265,7 @@ def remove_item(self, item: InputText | Select) -> Self: pass return self - def get_item(self, id: str | int) -> Select | InputText | None: + def get_item(self, id: str | int) -> Item[M] | None: """Gets an item from the modal. Roughly equal to `utils.get(modal.children, ...)`. If an :class:`int` is provided, the item will be retrieved by ``id``, otherwise by ``custom_id``. @@ -333,7 +330,7 @@ async def on_timeout(self) -> None: class _ModalWeights: __slots__ = ("weights",) - def __init__(self, children: list[InputText | Select]): + def __init__(self, children: list[Item[M]]): self.weights: list[int] = [0, 0, 0, 0, 0] key = lambda i: sys.maxsize if i.row is None else i.row @@ -342,14 +339,14 @@ def __init__(self, children: list[InputText | Select]): for item in group: self.add_item(item) - def find_open_space(self, item: InputText | Select) -> int: + def find_open_space(self, item: Item[M]) -> int: for index, weight in enumerate(self.weights): if weight + item.width <= 5: return index raise ValueError("could not find open space for item") - def add_item(self, item: InputText | Select) -> None: + def add_item(self, item: Item[M]) -> None: if item.row is not None: total = self.weights[item.row] + item.width if total > 5: @@ -363,7 +360,7 @@ def add_item(self, item: InputText | Select) -> None: self.weights[index] += item.width item._rendered_row = index - def remove_item(self, item: InputText | Select) -> None: + def remove_item(self, item: Item[M]) -> None: if item._rendered_row is not None: self.weights[item._rendered_row] -= item.width item._rendered_row = None From e0c86cf8cc2163e847bc8a2b4131fd0cb2d33d50 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 23:15:51 +0000 Subject: [PATCH 38/45] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/ui/modal.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/discord/ui/modal.py b/discord/ui/modal.py index 04f05e8a50..16798af487 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -243,7 +243,9 @@ def add_item(self, item: Item[M]) -> Self: raise ValueError("You can only have up to 5 items in a modal dialog.") if not isinstance(item, (InputText, Select, TextDisplay)): - raise TypeError(f"expected InputText, Select, or TextDisplay, not {item.__class__!r}") + raise TypeError( + f"expected InputText, Select, or TextDisplay, not {item.__class__!r}" + ) if isinstance(item, (InputText, Select)) and not item.label: raise ValueError("InputTexts and Selects must have a label set") From cf73790efe59e4a557fb1f45fd291b6daadaf30a Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Sat, 23 Aug 2025 00:28:42 +0100 Subject: [PATCH 39/45] check --- discord/ui/modal.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/discord/ui/modal.py b/discord/ui/modal.py index 16798af487..b43805e87e 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -200,10 +200,13 @@ def key(item: Item[M]) -> int: components: list[dict[str, Any]] = [] for _, group in groupby(children, key=key): labels = False + toplevel = False children = [] for item in group: if item.uses_label() or isinstance(item, Select): labels = True + elif isinstance(item, (TextDisplay, )): + toplevel = True children.append(item) if not children: continue @@ -220,6 +223,8 @@ def key(item: Item[M]) -> int: "description": item.description, } ) + elif toplevel: + components += [item.to_component_dict() for item in children] else: components.append( { From 4fa413ac82f23564bb532e5dacbc1594696b7878 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 23:29:13 +0000 Subject: [PATCH 40/45] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/ui/modal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/ui/modal.py b/discord/ui/modal.py index b43805e87e..07d8660db3 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -205,7 +205,7 @@ def key(item: Item[M]) -> int: for item in group: if item.uses_label() or isinstance(item, Select): labels = True - elif isinstance(item, (TextDisplay, )): + elif isinstance(item, (TextDisplay,)): toplevel = True children.append(item) if not children: From 276a0385b6ef8ba00d1898f6ee16235e581d7357 Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Sat, 23 Aug 2025 00:48:16 +0100 Subject: [PATCH 41/45] remove check --- discord/ui/select.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/discord/ui/select.py b/discord/ui/select.py index 42009c612e..ed20e16bc4 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -82,7 +82,7 @@ class Select(Item[V]): .. versionchanged:: 2.7 - :attr:`discord.ComponentType.string_select` can now be sent in :class:`discord.ui.Modal`. + Can now be sent in :class:`discord.ui.Modal`. Parameters ---------- @@ -168,12 +168,6 @@ def __init__( ) -> None: if options and select_type is not ComponentType.string_select: raise InvalidArgument("options parameter is only valid for string selects") - if ( - label or description or required - ) and select_type is not ComponentType.string_select: - raise InvalidArgument( - "label, description and required parameters are only valid for selects in modals" - ) if label and len(label) > 45: raise ValueError("label must be 45 characters or fewer") if description and len(description) > 100: From daa25ba1985d81d4918d27d99feb870277c90081 Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Sat, 23 Aug 2025 01:11:12 +0100 Subject: [PATCH 42/45] new dispatch? --- discord/ui/input_text.py | 3 +++ discord/ui/item.py | 3 +++ discord/ui/modal.py | 7 +++---- discord/ui/select.py | 4 ++++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/discord/ui/input_text.py b/discord/ui/input_text.py index 178d18b225..380694db6a 100644 --- a/discord/ui/input_text.py +++ b/discord/ui/input_text.py @@ -247,5 +247,8 @@ def to_component_dict(self) -> InputTextComponentPayload: def refresh_state(self, data) -> None: self._input_value = data["value"] + def refresh_from_modal(self, interaction: Interaction, data: dict) -> None: + return self.refresh_state(data) + def uses_label(self) -> bool: return self.description is not None diff --git a/discord/ui/item.py b/discord/ui/item.py index 5b57a19d0f..e2a568e345 100644 --- a/discord/ui/item.py +++ b/discord/ui/item.py @@ -90,6 +90,9 @@ def refresh_component(self, component: Component) -> None: def refresh_state(self, interaction: Interaction) -> None: return None + def refresh_from_modal(self, interaction: Interaction, data: dict) -> None: + return None + @classmethod def from_component(cls: type[I], component: Component) -> I: return cls() diff --git a/discord/ui/modal.py b/discord/ui/modal.py index 07d8660db3..82fc9cebfd 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -406,15 +406,14 @@ async def dispatch(self, user_id: int, custom_id: str, interaction: Interaction) or ( [parent_component.get("component")] if parent_component.get("component") - else [] + else [parent_component] ) ) ] for component in components: for child in value.children: - if child.custom_id == component["custom_id"]: # type: ignore - child.refresh_state(component) - break + child.refresh_from_modal(interaction, component) + break await value.callback(interaction) self.remove_modal(value, user_id) except Exception as e: diff --git a/discord/ui/select.py b/discord/ui/select.py index ed20e16bc4..40301b06ac 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -470,6 +470,10 @@ def refresh_state(self, interaction: Interaction | dict) -> None: self._selected_values = data.get("values", []) self._interaction = interaction + def refresh_from_modal(self, interaction: Interaction | dict, data: dict) -> None: + self._selected_values = data.get("values", []) + self._interaction = interaction + @classmethod def from_component(cls: type[S], component: SelectMenu) -> S: return cls( From 7320cd1ab972c9365e5ff9de51c06f6c0984fdc9 Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Sat, 23 Aug 2025 01:24:19 +0100 Subject: [PATCH 43/45] zip --- discord/ui/modal.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/discord/ui/modal.py b/discord/ui/modal.py index 82fc9cebfd..95deb15d71 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -410,10 +410,8 @@ async def dispatch(self, user_id: int, custom_id: str, interaction: Interaction) ) ) ] - for component in components: - for child in value.children: - child.refresh_from_modal(interaction, component) - break + for component, child in zip(components, value.children): + child.refresh_from_modal(interaction, component) await value.callback(interaction) self.remove_modal(value, user_id) except Exception as e: From 65cb824b72b086ca046a4078378c1491b7be1745 Mon Sep 17 00:00:00 2001 From: UK <41271523+NeloBlivion@users.noreply.github.com> Date: Sat, 23 Aug 2025 18:58:59 +0100 Subject: [PATCH 44/45] typing --- discord/ui/input_text.py | 1 + 1 file changed, 1 insertion(+) diff --git a/discord/ui/input_text.py b/discord/ui/input_text.py index 380694db6a..3b7a2dad8a 100644 --- a/discord/ui/input_text.py +++ b/discord/ui/input_text.py @@ -10,6 +10,7 @@ if TYPE_CHECKING: from ..types.components import InputText as InputTextComponentPayload + from ..interactions import Interaction class InputText: From e1e6511c6a3d7b1812345eb2360cb09194673ac5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 23 Aug 2025 17:59:25 +0000 Subject: [PATCH 45/45] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/ui/input_text.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/ui/input_text.py b/discord/ui/input_text.py index 3b7a2dad8a..5170e5a00a 100644 --- a/discord/ui/input_text.py +++ b/discord/ui/input_text.py @@ -9,8 +9,8 @@ __all__ = ("InputText",) if TYPE_CHECKING: - from ..types.components import InputText as InputTextComponentPayload from ..interactions import Interaction + from ..types.components import InputText as InputTextComponentPayload class InputText: