From d8e1eefaf1ae3486e49253a44d96fe8cad14670d Mon Sep 17 00:00:00 2001 From: plun1331 Date: Fri, 12 Sep 2025 13:16:40 -0700 Subject: [PATCH 01/33] fix(state): ensure _messages is not None before updating message list --- discord/state.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/discord/state.py b/discord/state.py index 56bdcbd684..dc982d43bb 100644 --- a/discord/state.py +++ b/discord/state.py @@ -779,9 +779,10 @@ def parse_message_update(self, data) -> None: old_message = self._get_message(int(data["id"])) channel, _ = self._get_guild_channel(data) message = Message(channel=channel, data=data, state=self) - if old_message is not None: - self._messages.remove(old_message) - self._messages.append(message) + if self._messages is not None: + if old_message is not None: + self._messages.remove(old_message) + self._messages.append(message) raw = RawMessageUpdateEvent(data, message) self.dispatch("raw_message_edit", raw) if old_message is not None: From 9d3b32b88eee7cea99ce3692fcc2e0b44199477a Mon Sep 17 00:00:00 2001 From: plun1331 Date: Fri, 12 Sep 2025 13:20:03 -0700 Subject: [PATCH 02/33] chore: update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 563daa792d..19823624e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,8 @@ These changes are available on the `master` branch, but have not yet been releas ([#2915](https://github.com/Pycord-Development/pycord/pull/2915)) - `View.message` being `None` when it had not been interacted with yet. ([#2916](https://github.com/Pycord-Development/pycord/pull/2916)) +- Fixed a crash when processing message edit events while message cache was disabled. + ([#2924](https://github.com/Pycord-Development/pycord/pull/2924)) ### Removed From f32b6885f4f471b4f47393453e05dbbf101d581b Mon Sep 17 00:00:00 2001 From: plun1331 Date: Wed, 17 Sep 2025 23:11:52 -0700 Subject: [PATCH 03/33] feat: FileUpload in Modals --- discord/components.py | 66 +++++++++++- discord/enums.py | 1 + discord/types/components.py | 12 ++- discord/types/message.py | 1 + discord/ui/__init__.py | 1 + discord/ui/file_upload.py | 193 ++++++++++++++++++++++++++++++++++++ discord/ui/input_text.py | 2 +- discord/ui/modal.py | 22 ++-- 8 files changed, 285 insertions(+), 13 deletions(-) create mode 100644 discord/ui/file_upload.py diff --git a/discord/components.py b/discord/components.py index a798edb74d..1867924a93 100644 --- a/discord/components.py +++ b/discord/components.py @@ -60,6 +60,7 @@ from .types.components import TextDisplayComponent as TextDisplayComponentPayload from .types.components import ThumbnailComponent as ThumbnailComponentPayload from .types.components import UnfurledMediaItem as UnfurledMediaItemPayload + from .types.components import FileUploadComponent as FileUploadComponentPayload __all__ = ( "Component", @@ -754,7 +755,6 @@ def url(self, value: str) -> None: @classmethod def from_dict(cls, data: UnfurledMediaItemPayload, state=None) -> UnfurledMediaItem: - r = cls(data.get("url")) r.proxy_url = data.get("proxy_url") r.height = data.get("height") @@ -805,8 +805,8 @@ def __init__(self, data: ThumbnailComponentPayload, state=None): self.type: ComponentType = try_enum(ComponentType, data["type"]) self.id: int = data.get("id") self.media: UnfurledMediaItem = ( - umi := data.get("media") - ) and UnfurledMediaItem.from_dict(umi, state=state) + umi := data.get("media") + ) and UnfurledMediaItem.from_dict(umi, state=state) self.description: str | None = data.get("description") self.spoiler: bool | None = data.get("spoiler") @@ -1163,6 +1163,65 @@ def walk_components(self) -> Iterator[Component]: yield from [self.component] +class FileUpload(Component): + """Represents an File Upload field from the Discord Bot UI Kit. + This inherits from :class:`Component`. + + Attributes + ---------- + custom_id: Optional[:class:`str`] + The custom ID of the file upload field that gets received during an interaction. + min_values: Optional[:class:`int`] + The minimum number of files that must be uploaded. + Defaults to 0. + max_values: Optional[:class:`int`] + The maximum number of files that can be uploaded. + required: Optional[:class:`bool`] + Whether the file upload field is required or not. Defaults to `True`. + id: Optional[:class:`int`] + The file upload's ID. + """ + + __slots__: tuple[str, ...] = ( + "type", + "custom_id", + "min_values", + "max_values", + "required", + "id", + ) + + __repr_info__: ClassVar[tuple[str, ...]] = __slots__ + versions: tuple[int, ...] = (1, 2) + + def __init__(self, data: FileUploadComponentPayload): + self.type = ComponentType.file_upload + self.id: int | None = data.get("id") + self.custom_id = data["custom_id"] + self.min_values: int | None = data.get("min_values", None) + self.max_values: int | None = data.get("max_values", None) + self.required: bool = data.get("required", True) + + def to_dict(self) -> FileUploadComponentPayload: + payload = { + "type": 19, + "id": self.id, + } + if self.custom_id: + payload["custom_id"] = self.custom_id + + if self.min_values: + payload["min_values"] = self.min_values + + if self.max_values: + payload["max_values"] = self.max_values + + if not self.required: + payload["required"] = self.required + + return payload # type: ignore + + COMPONENT_MAPPINGS = { 1: ActionRow, 2: Button, @@ -1180,6 +1239,7 @@ def walk_components(self) -> Iterator[Component]: 14: Separator, 17: Container, 18: Label, + 19: FileUpload, } STATE_COMPONENTS = (Section, Container, Thumbnail, MediaGallery, FileComponent) diff --git a/discord/enums.py b/discord/enums.py index 5dcc73967a..61078d1fe0 100644 --- a/discord/enums.py +++ b/discord/enums.py @@ -733,6 +733,7 @@ class ComponentType(Enum): separator = 14 content_inventory_entry = 16 container = 17 + file_upload = 19 def __int__(self): return self.value diff --git a/discord/types/components.py b/discord/types/components.py index 14063cdf54..c1742f1f42 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, 18] +ComponentType = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19] ButtonStyle = Literal[1, 2, 3, 4, 5, 6] InputTextStyle = Literal[1, 2] SeparatorSpacingSize = Literal[1, 2] @@ -159,7 +159,15 @@ class LabelComponent(BaseComponent): component: SelectMenu | InputText -Component = Union[ActionRow, ButtonComponent, SelectMenu, InputText] +class FileUploadComponent(BaseComponent): + type: Literal[19] + custom_id: str + max_values: NotRequired[int] + max_values: NotRequired[int] + required: NotRequired[bool] + + +Component = Union[ActionRow, ButtonComponent, SelectMenu, InputText, FileUploadComponent] AllowedContainerComponents = Union[ diff --git a/discord/types/message.py b/discord/types/message.py index d9bc4f2f9d..c6a48881c7 100644 --- a/discord/types/message.py +++ b/discord/types/message.py @@ -81,6 +81,7 @@ class Attachment(TypedDict): waveform: NotRequired[str] flags: NotRequired[int] title: NotRequired[str] + ephemeral: NotRequired[bool] MessageActivityType = Literal[1, 2, 3, 5] diff --git a/discord/ui/__init__.py b/discord/ui/__init__.py index 473ac45563..cd2b701ebb 100644 --- a/discord/ui/__init__.py +++ b/discord/ui/__init__.py @@ -11,6 +11,7 @@ from .button import * from .container import * from .file import * +from .file_upload import * from .input_text import * from .item import * from .media_gallery import * diff --git a/discord/ui/file_upload.py b/discord/ui/file_upload.py new file mode 100644 index 0000000000..98966ad7d6 --- /dev/null +++ b/discord/ui/file_upload.py @@ -0,0 +1,193 @@ +from __future__ import annotations + +import os +from typing import TYPE_CHECKING + +from ..message import Attachment +from ..components import FileUpload as FileUploadComponent +from ..enums import ComponentType + +__all__ = ("FileUpload",) + +if TYPE_CHECKING: + from ..interactions import Interaction + from ..types.components import FileUploadComponent as FileUploadComponentPayload + + +class FileUpload: + """Represents a UI file upload field. + + .. versionadded:: 2.7 + + Parameters + ---------- + custom_id: Optional[:class:`str`] + The ID of the input text field that gets received during an interaction. + label: :class:`str` + The label for the file upload field. + Must be 45 characters or fewer. + description: Optional[:class:`str`] + The description for the file upload field. + Must be 100 characters or fewer. + min_values: Optional[:class:`int`] + The minimum number of files that must be uploaded. + Defaults to 0 and must be between 0 and 10, inclusive. + max_values: Optional[:class:`int`] + The maximum number of files that can be uploaded. + Must be between 1 and 10, inclusive. + required: Optional[:class:`bool`] + Whether the file upload field is required or not. Defaults to ``True``. + row: Optional[:class:`int`] + The relative row this file upload field belongs to. A modal dialog can only have 5 + rows. By default, items are arranged automatically into those 5 rows. If you'd + like to control the relative positioning of the row then passing an index is advised. + For example, row=1 will show up before row=2. Defaults to ``None``, which is automatic + ordering. The row number must be between 0 and 4 (i.e. zero indexed). + """ + + __item_repr_attributes__: tuple[str, ...] = ( + "label", + "required", + "min_values", + "max_values", + "custom_id", + "id", + "description", + ) + + def __init__( + self, + *, + custom_id: str | None = None, + label: str, + min_values: int | None = None, + max_values: int | None = None, + required: bool | None = True, + row: int | None = None, + id: int | None = None, + description: str | None = None, + ): + 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_values and (min_values < 0 or min_values > 10): + raise ValueError("min_values must be between 0 and 10") + if max_values and (max_values < 1 or max_values > 10): + raise ValueError("max_length must be between 1 and 10") + if not isinstance(custom_id, str) and custom_id is not None: + raise TypeError( + 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.label: str = str(label) + self.description: str | None = description + + self._underlying: FileUploadComponent = FileUploadComponent._raw_construct( + type=ComponentType.file_upload, + custom_id=custom_id, + label=label, + min_values=min_values, + max_values=max_values, + required=required, + id=id, + ) + self._interaction: Interaction | None = None + self._values: list[str] | None = None + self.row = row + self._rendered_row: int | None = None + + def __repr__(self) -> str: + attrs = " ".join( + f"{key}={getattr(self, key)!r}" for key in self.__item_repr_attributes__ + ) + return f"<{self.__class__.__name__} {attrs}>" + + @property + def type(self) -> ComponentType: + return self._underlying.type + + @property + def id(self) -> int | None: + """The file upload's ID. If not provided by the user, it is set sequentially by Discord.""" + return self._underlying.id + + @property + def custom_id(self) -> str: + """The ID of the file upload field that gets received during an interaction.""" + return self._underlying.custom_id + + @custom_id.setter + def custom_id(self, value: str): + if not isinstance(value, str): + raise TypeError( + f"custom_id must be None or str not {value.__class__.__name__}" + ) + self._underlying.custom_id = value + + @property + def min_values(self) -> int | None: + """The minimum number of files that must be uploaded. Defaults to 0.""" + return self._underlying.min_values + + @min_values.setter + def min_values(self, value: int | None): + if value and not isinstance(value, int): + raise TypeError(f"min_values must be None or int not {value.__class__.__name__}") # type: ignore + if value and (value < 0 or value) > 4000: + raise ValueError("min_values must be between 0 and 10") + self._underlying.min_values = value + + @property + def max_values(self) -> int | None: + """The maximum number of files that can be uploaded.""" + return self._underlying.max_values + + @max_values.setter + def max_values(self, value: int | None): + if value and not isinstance(value, int): + raise TypeError(f"max_values must be None or int not {value.__class__.__name__}") # type: ignore + if value and (value <= 0 or value > 4000): + raise ValueError("max_values must be between 1 and 10") + self._underlying.max_values = value + + @property + def required(self) -> bool | None: + """Whether the input file upload is required or not. Defaults to ``True``.""" + return self._underlying.required + + @required.setter + def required(self, value: bool | None): + if not isinstance(value, bool): + raise TypeError(f"required must be bool not {value.__class__.__name__}") # type: ignore + self._underlying.required = bool(value) + + @property + def values(self) -> list[Attachment] | None: + """The files that were uploaded to the field.""" + if self._interaction is None: + return None + attachments = [] + for attachment_id in self._values: + print(attachment_id) + attachment_data = self._interaction.data["resolved"]["attachments"][attachment_id] + attachments.append(Attachment(state=self._interaction._state, data=attachment_data)) + return attachments + + @property + def width(self) -> int: + return 5 + + def to_component_dict(self) -> FileUploadComponentPayload: + return self._underlying.to_dict() + + def refresh_from_modal(self, interaction: Interaction, data: dict) -> None: + print(data) + self._interaction = interaction + self._values = data.get("values", []) + + @staticmethod + def uses_label() -> bool: + return True + diff --git a/discord/ui/input_text.py b/discord/ui/input_text.py index 4f7828ce97..0f49bd19ec 100644 --- a/discord/ui/input_text.py +++ b/discord/ui/input_text.py @@ -246,7 +246,7 @@ def to_component_dict(self) -> InputTextComponentPayload: return self._underlying.to_dict() def refresh_state(self, data) -> None: - self._input_value = data["value"] + self._input_value = data.get("value", None) def refresh_from_modal(self, interaction: Interaction, data: dict) -> None: return self.refresh_state(data) diff --git a/discord/ui/modal.py b/discord/ui/modal.py index 32bf5853bc..a9f14227d7 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -10,6 +10,7 @@ from ..enums import ComponentType from ..utils import find +from .file_upload import FileUpload from .input_text import InputText from .item import Item from .select import Select @@ -29,7 +30,7 @@ M = TypeVar("M", bound="Modal", covariant=True) -ModalItem = Union[InputText, Item[M]] +ModalItem = Union[InputText, FileUpload, Item[M]] class Modal: @@ -249,10 +250,10 @@ def add_item(self, item: ModalItem) -> Self: if len(self._children) > 5: raise ValueError("You can only have up to 5 items in a modal dialog.") - if not isinstance(item, (InputText, Item)): - raise TypeError(f"expected InputText or Item, not {item.__class__!r}") - if isinstance(item, (InputText, Select)) and not item.label: - raise ValueError("InputTexts and Selects must have a label set") + if not isinstance(item, (InputText, FileUpload, Item)): + raise TypeError(f"expected InputText, FileUpload, or Item, not {item.__class__!r}") + if isinstance(item, (InputText, FileUpload, Select)) and not item.label: + raise ValueError("InputTexts, FileUploads, and Selects must have a label set") self._weights.add_item(item) self._children.append(item) @@ -410,8 +411,15 @@ async def dispatch(self, user_id: int, custom_id: str, interaction: Interaction) ) ) ] - for component, child in zip(components, value.children): - child.refresh_from_modal(interaction, component) + # match component by id + for component in components: + item = value.get_item(component.get("custom_id") or component.get("id")) + print(component, item) + if item is not None: + item.refresh_from_modal(interaction, component) + + # 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 b025df58c97693b95460bcf52ab468ea88bb5955 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 18 Sep 2025 06:22:13 +0000 Subject: [PATCH 04/33] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/components.py | 6 +++--- discord/types/components.py | 4 +++- discord/ui/file_upload.py | 11 +++++++---- discord/ui/modal.py | 8 ++++++-- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/discord/components.py b/discord/components.py index 617a0091e8..17f5c03f01 100644 --- a/discord/components.py +++ b/discord/components.py @@ -50,6 +50,7 @@ from .types.components import Component as ComponentPayload from .types.components import ContainerComponent as ContainerComponentPayload from .types.components import FileComponent as FileComponentPayload + from .types.components import FileUploadComponent as FileUploadComponentPayload from .types.components import InputText as InputTextComponentPayload from .types.components import LabelComponent as LabelComponentPayload from .types.components import MediaGalleryComponent as MediaGalleryComponentPayload @@ -62,7 +63,6 @@ from .types.components import TextDisplayComponent as TextDisplayComponentPayload from .types.components import ThumbnailComponent as ThumbnailComponentPayload from .types.components import UnfurledMediaItem as UnfurledMediaItemPayload - from .types.components import FileUploadComponent as FileUploadComponentPayload __all__ = ( "Component", @@ -989,8 +989,8 @@ def __init__(self, data: ThumbnailComponentPayload, state=None): self.type: ComponentType = try_enum(ComponentType, data["type"]) self.id: int = data.get("id") self.media: UnfurledMediaItem = ( - umi := data.get("media") - ) and UnfurledMediaItem.from_dict(umi, state=state) + umi := data.get("media") + ) and UnfurledMediaItem.from_dict(umi, state=state) self.description: str | None = data.get("description") self.spoiler: bool | None = data.get("spoiler") diff --git a/discord/types/components.py b/discord/types/components.py index db1d85f94a..657291ea32 100644 --- a/discord/types/components.py +++ b/discord/types/components.py @@ -174,7 +174,9 @@ class FileUploadComponent(BaseComponent): required: NotRequired[bool] -Component = Union[ActionRow, ButtonComponent, SelectMenu, InputText, FileUploadComponent] +Component = Union[ + ActionRow, ButtonComponent, SelectMenu, InputText, FileUploadComponent +] AllowedContainerComponents = Union[ diff --git a/discord/ui/file_upload.py b/discord/ui/file_upload.py index 98966ad7d6..da4c3d650f 100644 --- a/discord/ui/file_upload.py +++ b/discord/ui/file_upload.py @@ -3,9 +3,9 @@ import os from typing import TYPE_CHECKING -from ..message import Attachment from ..components import FileUpload as FileUploadComponent from ..enums import ComponentType +from ..message import Attachment __all__ = ("FileUpload",) @@ -171,8 +171,12 @@ def values(self) -> list[Attachment] | None: attachments = [] for attachment_id in self._values: print(attachment_id) - attachment_data = self._interaction.data["resolved"]["attachments"][attachment_id] - attachments.append(Attachment(state=self._interaction._state, data=attachment_data)) + attachment_data = self._interaction.data["resolved"]["attachments"][ + attachment_id + ] + attachments.append( + Attachment(state=self._interaction._state, data=attachment_data) + ) return attachments @property @@ -190,4 +194,3 @@ def refresh_from_modal(self, interaction: Interaction, data: dict) -> None: @staticmethod def uses_label() -> bool: return True - diff --git a/discord/ui/modal.py b/discord/ui/modal.py index a9f14227d7..1e08d83c93 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -251,9 +251,13 @@ def add_item(self, item: ModalItem) -> Self: raise ValueError("You can only have up to 5 items in a modal dialog.") if not isinstance(item, (InputText, FileUpload, Item)): - raise TypeError(f"expected InputText, FileUpload, or Item, not {item.__class__!r}") + raise TypeError( + f"expected InputText, FileUpload, or Item, not {item.__class__!r}" + ) if isinstance(item, (InputText, FileUpload, Select)) and not item.label: - raise ValueError("InputTexts, FileUploads, and Selects must have a label set") + raise ValueError( + "InputTexts, FileUploads, and Selects must have a label set" + ) self._weights.add_item(item) self._children.append(item) From 590a7a2c22601bcf62c2b9994f24db6d8a437620 Mon Sep 17 00:00:00 2001 From: plun1331 Date: Wed, 17 Sep 2025 23:26:27 -0700 Subject: [PATCH 05/33] changelog & docs --- CHANGELOG.md | 2 ++ docs/api/models.rst | 6 ++++++ docs/api/ui_kit.rst | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3098be39c9..0d1c62c3c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,8 @@ These changes are available on the `master` branch, but have not yet been releas - Adds pre-typed and pre-constructed with select_type `ui.Select` aliases for the different select types: `ui.StringSelect`, `ui.UserSelect`, `ui.RoleSelect`, `ui.MentionableSelect`, and `ui.ChannelSelect`. +- Added `ui.FileUpload` for modals and the `FileUpload` component. + ([#2938](https://github.com/Pycord-Development/pycord/pull/2938)) ### Changed diff --git a/docs/api/models.rst b/docs/api/models.rst index 5075d7084b..645eb29690 100644 --- a/docs/api/models.rst +++ b/docs/api/models.rst @@ -445,6 +445,12 @@ Message Components :members: :inherited-members: +.. attributetable:: FileUpload + +.. autoclass:: FileUpload() + :members: + :inherited-members: + Emoji ----- diff --git a/docs/api/ui_kit.rst b/docs/api/ui_kit.rst index 8714f59b85..8b77a2422e 100644 --- a/docs/api/ui_kit.rst +++ b/docs/api/ui_kit.rst @@ -129,3 +129,9 @@ Objects .. autoclass:: discord.ui.InputText :members: :inherited-members: + +.. attributetable:: discord.ui.FileUpload + +.. autoclass:: discord.ui.FileUpload + :members: + :inherited-members: From 9c42603687ce243ba8547b722359429e4f9780f7 Mon Sep 17 00:00:00 2001 From: plun1331 Date: Wed, 17 Sep 2025 23:27:44 -0700 Subject: [PATCH 06/33] ahh! nobody look! my shitty debug code! --- discord/ui/modal.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/discord/ui/modal.py b/discord/ui/modal.py index 1e08d83c93..9cc76ffc86 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -418,12 +418,8 @@ async def dispatch(self, user_id: int, custom_id: str, interaction: Interaction) # match component by id for component in components: item = value.get_item(component.get("custom_id") or component.get("id")) - print(component, item) if item is not None: item.refresh_from_modal(interaction, component) - - # 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 498c28b4ff600c28500e64432deb80ac0583759a Mon Sep 17 00:00:00 2001 From: plun1331 Date: Wed, 17 Sep 2025 23:29:18 -0700 Subject: [PATCH 07/33] add fileupload to __all__ --- discord/components.py | 1 + 1 file changed, 1 insertion(+) diff --git a/discord/components.py b/discord/components.py index 17f5c03f01..dcb1399471 100644 --- a/discord/components.py +++ b/discord/components.py @@ -82,6 +82,7 @@ "Container", "Label", "SelectDefaultValue", + "FileUpload", ) C = TypeVar("C", bound="Component") From 033f6844c164863fc61b674b0b8d7f6188606b7e Mon Sep 17 00:00:00 2001 From: plun1331 Date: Wed, 17 Sep 2025 23:31:24 -0700 Subject: [PATCH 08/33] more changelog!! --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d1c62c3c7..1d6eafc1ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,10 @@ These changes are available on the `master` branch, but have not yet been releas ([#2924](https://github.com/Pycord-Development/pycord/pull/2924)) - Fixed OPUS Decode Error when recording audio. ([#2925](https://github.com/Pycord-Development/pycord/pull/2925)) +- Fixed modal input values being misordered when using the `row` parameter and inserting items out of row order. + ([#2938](https://github.com/Pycord-Development/pycord/pull/2938)) +- Fixed a KeyError when a text input is left blank in a modal. + ([#2938](https://github.com/Pycord-Development/pycord/pull/2938)) ### Removed From 14665b6c18c9a1dd2772649920aa79ac4bcb40fe Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 18 Sep 2025 06:31:57 +0000 Subject: [PATCH 09/33] style(pre-commit): auto fixes from pre-commit.com hooks --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d6eafc1ad..39e90fbcd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,7 +47,8 @@ These changes are available on the `master` branch, but have not yet been releas ([#2924](https://github.com/Pycord-Development/pycord/pull/2924)) - Fixed OPUS Decode Error when recording audio. ([#2925](https://github.com/Pycord-Development/pycord/pull/2925)) -- Fixed modal input values being misordered when using the `row` parameter and inserting items out of row order. +- Fixed modal input values being misordered when using the `row` parameter and inserting + items out of row order. ([#2938](https://github.com/Pycord-Development/pycord/pull/2938)) - Fixed a KeyError when a text input is left blank in a modal. ([#2938](https://github.com/Pycord-Development/pycord/pull/2938)) From 7ba0276b831e2981523e227686cf5ed5c7ec6d1f Mon Sep 17 00:00:00 2001 From: plun1331 Date: Wed, 17 Sep 2025 23:42:39 -0700 Subject: [PATCH 10/33] docs n enums n stuff --- discord/components.py | 2 ++ discord/enums.py | 1 + docs/api/enums.rst | 30 ++++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/discord/components.py b/discord/components.py index dcb1399471..888cdb769d 100644 --- a/discord/components.py +++ b/discord/components.py @@ -1352,6 +1352,8 @@ class FileUpload(Component): """Represents an File Upload field from the Discord Bot UI Kit. This inherits from :class:`Component`. + .. versionadded:: 2.7 + Attributes ---------- custom_id: Optional[:class:`str`] diff --git a/discord/enums.py b/discord/enums.py index c4fcebd092..63557c853b 100644 --- a/discord/enums.py +++ b/discord/enums.py @@ -734,6 +734,7 @@ class ComponentType(Enum): separator = 14 content_inventory_entry = 16 container = 17 + label = 18 file_upload = 19 def __int__(self): diff --git a/docs/api/enums.rst b/docs/api/enums.rst index 20692fce21..2e4f28a644 100644 --- a/docs/api/enums.rst +++ b/docs/api/enums.rst @@ -505,6 +505,36 @@ of :class:`enum.Enum`. .. attribute:: channel_select Represents a channel select component. + .. attribute:: section + + Represents a section component. + .. attribute:: text_display + + Represents a text display component. + .. attribute:: thumbnail + + Represents a thumbnail component. + .. attribute:: media_gallery + + Represents a media gallery component. + .. attribute:: file + + Represents a file component. + .. attribute:: separator + + Represents a separator component. + .. attribute:: content_inventory_entry + + Represents a content inventory entry component. + .. attribute:: container + + Represents a container component. + .. attribute:: label + + Represents a label component. + .. attribute:: file_upload + + Represents a file upload component. .. class:: ButtonStyle From a90300538b5af447673848b5175ea871a8e00ae6 Mon Sep 17 00:00:00 2001 From: plun1331 Date: Wed, 17 Sep 2025 23:45:00 -0700 Subject: [PATCH 11/33] oh god more debug code --- discord/ui/file_upload.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/discord/ui/file_upload.py b/discord/ui/file_upload.py index da4c3d650f..feeea00c36 100644 --- a/discord/ui/file_upload.py +++ b/discord/ui/file_upload.py @@ -87,7 +87,6 @@ def __init__( self._underlying: FileUploadComponent = FileUploadComponent._raw_construct( type=ComponentType.file_upload, custom_id=custom_id, - label=label, min_values=min_values, max_values=max_values, required=required, @@ -170,7 +169,6 @@ def values(self) -> list[Attachment] | None: return None attachments = [] for attachment_id in self._values: - print(attachment_id) attachment_data = self._interaction.data["resolved"]["attachments"][ attachment_id ] @@ -187,7 +185,6 @@ def to_component_dict(self) -> FileUploadComponentPayload: return self._underlying.to_dict() def refresh_from_modal(self, interaction: Interaction, data: dict) -> None: - print(data) self._interaction = interaction self._values = data.get("values", []) From 6e36204b6c4a0d4ff463eb3e946f3c73cef776de Mon Sep 17 00:00:00 2001 From: plun1331 Date: Wed, 17 Sep 2025 23:46:20 -0700 Subject: [PATCH 12/33] that aint right --- discord/ui/file_upload.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/ui/file_upload.py b/discord/ui/file_upload.py index feeea00c36..732a75a3ba 100644 --- a/discord/ui/file_upload.py +++ b/discord/ui/file_upload.py @@ -134,7 +134,7 @@ def min_values(self) -> int | None: def min_values(self, value: int | None): if value and not isinstance(value, int): raise TypeError(f"min_values must be None or int not {value.__class__.__name__}") # type: ignore - if value and (value < 0 or value) > 4000: + if value and (value < 0 or value > 10): raise ValueError("min_values must be between 0 and 10") self._underlying.min_values = value @@ -147,7 +147,7 @@ def max_values(self) -> int | None: def max_values(self, value: int | None): if value and not isinstance(value, int): raise TypeError(f"max_values must be None or int not {value.__class__.__name__}") # type: ignore - if value and (value <= 0 or value > 4000): + if value and (value <= 0 or value > 10): raise ValueError("max_values must be between 1 and 10") self._underlying.max_values = value From 7a66cb7e139333bd099b93f2f12b07e44915aee4 Mon Sep 17 00:00:00 2001 From: plun1331 Date: Wed, 17 Sep 2025 23:47:07 -0700 Subject: [PATCH 13/33] that *still* aint right --- discord/ui/file_upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/ui/file_upload.py b/discord/ui/file_upload.py index 732a75a3ba..caebd76273 100644 --- a/discord/ui/file_upload.py +++ b/discord/ui/file_upload.py @@ -147,7 +147,7 @@ def max_values(self) -> int | None: def max_values(self, value: int | None): if value and not isinstance(value, int): raise TypeError(f"max_values must be None or int not {value.__class__.__name__}") # type: ignore - if value and (value <= 0 or value > 10): + if value and (value < 1 or value > 10): raise ValueError("max_values must be between 1 and 10") self._underlying.max_values = value From 7bff68a6949faf23bbca5302187aa65842043e22 Mon Sep 17 00:00:00 2001 From: plun1331 Date: Thu, 18 Sep 2025 11:00:49 -0700 Subject: [PATCH 14/33] Apply suggestion from @Soheab Co-authored-by: Soheab <33902984+Soheab@users.noreply.github.com> Signed-off-by: plun1331 --- discord/types/components.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/types/components.py b/discord/types/components.py index 657291ea32..50e81ff388 100644 --- a/discord/types/components.py +++ b/discord/types/components.py @@ -163,7 +163,7 @@ class LabelComponent(BaseComponent): type: Literal[18] label: str description: NotRequired[str] - component: SelectMenu | InputText + component: SelectMenu | InputText | FileUploadComponent class FileUploadComponent(BaseComponent): From b356c75383cf89daba286d74155643500594454a Mon Sep 17 00:00:00 2001 From: plun1331 Date: Thu, 18 Sep 2025 11:07:24 -0700 Subject: [PATCH 15/33] suggested review changes --- discord/ui/file_upload.py | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/discord/ui/file_upload.py b/discord/ui/file_upload.py index caebd76273..ba90f2f198 100644 --- a/discord/ui/file_upload.py +++ b/discord/ui/file_upload.py @@ -21,11 +21,11 @@ class FileUpload: Parameters ---------- - custom_id: Optional[:class:`str`] - The ID of the input text field that gets received during an interaction. label: :class:`str` The label for the file upload field. Must be 45 characters or fewer. + custom_id: Optional[:class:`str`] + The ID of the input text field that gets received during an interaction. description: Optional[:class:`str`] The description for the file upload field. Must be 100 characters or fewer. @@ -58,8 +58,8 @@ class FileUpload: def __init__( self, *, - custom_id: str | None = None, label: str, + custom_id: str | None = None, min_values: int | None = None, max_values: int | None = None, required: bool | None = True, @@ -76,7 +76,7 @@ def __init__( raise ValueError("min_values must be between 0 and 10") if max_values and (max_values < 1 or max_values > 10): raise ValueError("max_length must be between 1 and 10") - if not isinstance(custom_id, str) and custom_id is not None: + if custom_id is not None and not isinstance(custom_id, str): raise TypeError( f"expected custom_id to be str, not {custom_id.__class__.__name__}" ) @@ -92,8 +92,7 @@ def __init__( required=required, id=id, ) - self._interaction: Interaction | None = None - self._values: list[str] | None = None + self._attachments: list[Attachment] | None = None self.row = row self._rendered_row: int | None = None @@ -165,17 +164,7 @@ def required(self, value: bool | None): @property def values(self) -> list[Attachment] | None: """The files that were uploaded to the field.""" - if self._interaction is None: - return None - attachments = [] - for attachment_id in self._values: - attachment_data = self._interaction.data["resolved"]["attachments"][ - attachment_id - ] - attachments.append( - Attachment(state=self._interaction._state, data=attachment_data) - ) - return attachments + return self._attachments @property def width(self) -> int: @@ -185,8 +174,13 @@ def to_component_dict(self) -> FileUploadComponentPayload: return self._underlying.to_dict() def refresh_from_modal(self, interaction: Interaction, data: dict) -> None: - self._interaction = interaction - self._values = data.get("values", []) + values = data.get("values", []) + self._attachments = [ + Attachment( + state=interaction._state, + data=interaction.data["resolved"]["attachments"][attachment_id] + ) for attachment_id in values + ] @staticmethod def uses_label() -> bool: From 9a08c321da600a481ed5b5524cacc1a6a2f791a6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 18 Sep 2025 18:08:20 +0000 Subject: [PATCH 16/33] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/ui/file_upload.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/discord/ui/file_upload.py b/discord/ui/file_upload.py index ba90f2f198..377d9dc2be 100644 --- a/discord/ui/file_upload.py +++ b/discord/ui/file_upload.py @@ -178,8 +178,9 @@ def refresh_from_modal(self, interaction: Interaction, data: dict) -> None: self._attachments = [ Attachment( state=interaction._state, - data=interaction.data["resolved"]["attachments"][attachment_id] - ) for attachment_id in values + data=interaction.data["resolved"]["attachments"][attachment_id], + ) + for attachment_id in values ] @staticmethod From a7fa66a968c54fcba12e83bdcb01743fde2e2088 Mon Sep 17 00:00:00 2001 From: plun1331 Date: Thu, 18 Sep 2025 11:14:52 -0700 Subject: [PATCH 17/33] suggested review changes --- discord/components.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/discord/components.py b/discord/components.py index 888cdb769d..8a10a6dac4 100644 --- a/discord/components.py +++ b/discord/components.py @@ -1350,8 +1350,13 @@ def walk_components(self) -> Iterator[Component]: class FileUpload(Component): """Represents an File Upload field from the Discord Bot UI Kit. + This inherits from :class:`Component`. + .. note:: + + This class is not useable by end-users; see :class:`discord.ui.FileUpload` instead. + .. versionadded:: 2.7 Attributes @@ -1392,10 +1397,10 @@ def __init__(self, data: FileUploadComponentPayload): def to_dict(self) -> FileUploadComponentPayload: payload = { "type": 19, - "id": self.id, + "custom_id": self.custom_id, } - if self.custom_id: - payload["custom_id"] = self.custom_id + if self.id is not None: + payload["id"] = self.id if self.min_values: payload["min_values"] = self.min_values From cfc2e5e3802ffae189ba4edfa2ae98edc4475afc Mon Sep 17 00:00:00 2001 From: plun1331 Date: Thu, 18 Sep 2025 11:17:11 -0700 Subject: [PATCH 18/33] they can go in more than messages --- docs/api/models.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/models.rst b/docs/api/models.rst index 645eb29690..2a82c04aac 100644 --- a/docs/api/models.rst +++ b/docs/api/models.rst @@ -378,7 +378,7 @@ Interactions .. autoclass:: InteractionCallback() :members: -Message Components +UI Components ------------------ .. attributetable:: Component From 0b55abaae5ff938790c50229728a4a065f48d449 Mon Sep 17 00:00:00 2001 From: plun1331 Date: Thu, 18 Sep 2025 11:25:25 -0700 Subject: [PATCH 19/33] this is becoming a very large example --- examples/modal_dialogs.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/examples/modal_dialogs.py b/examples/modal_dialogs.py index 4d10ddab0c..b4a122479e 100644 --- a/examples/modal_dialogs.py +++ b/examples/modal_dialogs.py @@ -36,11 +36,18 @@ def __init__(self, *args, **kwargs) -> None: description="If it is not listed, skip this question.", required=False, ), + discord.ui.FileUpload( + label="What's your favorite picture?", + max_values=1, + description="You may only pick one! Chose wisely!", + required=False, + ), *args, **kwargs, ) async def callback(self, interaction: discord.Interaction): + await interaction.response.defer() embed = discord.Embed( title="Your Modal Results", fields=[ @@ -50,10 +57,19 @@ async def callback(self, interaction: discord.Interaction): discord.EmbedField( name="Second Input", value=self.children[1].value, inline=False ), + discord.EmbedField( + name="Favorite Color", value=self.children[2].value, inline=False + ), ], color=discord.Color.random(), ) - await interaction.response.send_message(embeds=[embed]) + attachment = self.children[3].values[0] if self.children[3].values else None + if attachment: + embed.set_image(url=f"attachments://{attachment.filename}") + await interaction.response.send_message( + embeds=[embed], + files=[await attachment.to_file()] if attachment else [], + ) @bot.slash_command(name="modaltest") From 99417f98d4ccf63cb61c33a5d864b83c626411a3 Mon Sep 17 00:00:00 2001 From: plun1331 Date: Thu, 18 Sep 2025 12:09:03 -0700 Subject: [PATCH 20/33] Update examples/modal_dialogs.py Co-authored-by: Soheab <33902984+Soheab@users.noreply.github.com> Signed-off-by: plun1331 --- examples/modal_dialogs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/modal_dialogs.py b/examples/modal_dialogs.py index b4a122479e..ae05871711 100644 --- a/examples/modal_dialogs.py +++ b/examples/modal_dialogs.py @@ -66,7 +66,7 @@ async def callback(self, interaction: discord.Interaction): attachment = self.children[3].values[0] if self.children[3].values else None if attachment: embed.set_image(url=f"attachments://{attachment.filename}") - await interaction.response.send_message( + await interaction.followup.send( embeds=[embed], files=[await attachment.to_file()] if attachment else [], ) From c0af721db06efc3ac441ed07b94c9fc328ebf9c3 Mon Sep 17 00:00:00 2001 From: plun1331 Date: Fri, 26 Sep 2025 09:00:43 -0700 Subject: [PATCH 21/33] Update discord/types/components.py (thanks copilot) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: plun1331 --- discord/types/components.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/types/components.py b/discord/types/components.py index 50e81ff388..f78d3c78a1 100644 --- a/discord/types/components.py +++ b/discord/types/components.py @@ -170,7 +170,7 @@ class FileUploadComponent(BaseComponent): type: Literal[19] custom_id: str max_values: NotRequired[int] - max_values: NotRequired[int] + min_values: NotRequired[int] required: NotRequired[bool] From 46030b3bf85a6d788d32f7ed6c2ad97757ca6fb8 Mon Sep 17 00:00:00 2001 From: plun1331 Date: Wed, 1 Oct 2025 21:15:54 -0700 Subject: [PATCH 22/33] Apply suggestions from code review Co-authored-by: Paillat Signed-off-by: plun1331 --- examples/modal_dialogs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/modal_dialogs.py b/examples/modal_dialogs.py index ae05871711..c6ad5d8d1c 100644 --- a/examples/modal_dialogs.py +++ b/examples/modal_dialogs.py @@ -58,14 +58,14 @@ async def callback(self, interaction: discord.Interaction): name="Second Input", value=self.children[1].value, inline=False ), discord.EmbedField( - name="Favorite Color", value=self.children[2].value, inline=False + name="Favorite Color", value=self.children[3].values[0], inline=False ), ], color=discord.Color.random(), ) - attachment = self.children[3].values[0] if self.children[3].values else None + attachment = self.children[4].values[0] if self.children[4].values else None if attachment: - embed.set_image(url=f"attachments://{attachment.filename}") + embed.set_image(url=f"attachment://{attachment.filename}") await interaction.followup.send( embeds=[embed], files=[await attachment.to_file()] if attachment else [], From f3df97fa5481de7898f0277fa23049063344435b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 04:16:24 +0000 Subject: [PATCH 23/33] style(pre-commit): auto fixes from pre-commit.com hooks --- examples/modal_dialogs.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/modal_dialogs.py b/examples/modal_dialogs.py index c6ad5d8d1c..14ca8ac09e 100644 --- a/examples/modal_dialogs.py +++ b/examples/modal_dialogs.py @@ -58,7 +58,9 @@ async def callback(self, interaction: discord.Interaction): name="Second Input", value=self.children[1].value, inline=False ), discord.EmbedField( - name="Favorite Color", value=self.children[3].values[0], inline=False + name="Favorite Color", + value=self.children[3].values[0], + inline=False, ), ], color=discord.Color.random(), From a76208a5cc6018f6a9e70c6666c327b0fdabd6f5 Mon Sep 17 00:00:00 2001 From: plun1331 Date: Thu, 9 Oct 2025 22:15:59 -0700 Subject: [PATCH 24/33] Apply suggestion from @Soheab Co-authored-by: Soheab <33902984+Soheab@users.noreply.github.com> Signed-off-by: plun1331 --- discord/ui/file_upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/ui/file_upload.py b/discord/ui/file_upload.py index 377d9dc2be..2ab37be597 100644 --- a/discord/ui/file_upload.py +++ b/discord/ui/file_upload.py @@ -22,7 +22,7 @@ class FileUpload: Parameters ---------- label: :class:`str` - The label for the file upload field. + The label for this component Must be 45 characters or fewer. custom_id: Optional[:class:`str`] The ID of the input text field that gets received during an interaction. From 3c4bcb80c82a4106940e5ce7ee0f34533804115d Mon Sep 17 00:00:00 2001 From: plun1331 Date: Thu, 9 Oct 2025 22:17:20 -0700 Subject: [PATCH 25/33] Apply suggestion from @Soheab Co-authored-by: Soheab <33902984+Soheab@users.noreply.github.com> Signed-off-by: plun1331 --- discord/ui/file_upload.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/ui/file_upload.py b/discord/ui/file_upload.py index 2ab37be597..e3db1bf4e7 100644 --- a/discord/ui/file_upload.py +++ b/discord/ui/file_upload.py @@ -108,12 +108,12 @@ def type(self) -> ComponentType: @property def id(self) -> int | None: - """The file upload's ID. If not provided by the user, it is set sequentially by Discord.""" + """The ID of this component. If not provided by the user, it is set sequentially by Discord.""" return self._underlying.id @property def custom_id(self) -> str: - """The ID of the file upload field that gets received during an interaction.""" + """The custom id that gets received during an interaction.""" return self._underlying.custom_id @custom_id.setter From 9c753a4614b3c115bd4a0ad16d1a40fd91f42c32 Mon Sep 17 00:00:00 2001 From: plun1331 Date: Thu, 9 Oct 2025 22:19:46 -0700 Subject: [PATCH 26/33] Update file_upload.py Signed-off-by: plun1331 --- discord/ui/file_upload.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/discord/ui/file_upload.py b/discord/ui/file_upload.py index e3db1bf4e7..8e89ac7d69 100644 --- a/discord/ui/file_upload.py +++ b/discord/ui/file_upload.py @@ -60,9 +60,9 @@ def __init__( *, label: str, custom_id: str | None = None, - min_values: int | None = None, - max_values: int | None = None, - required: bool | None = True, + min_values: int | None = 0, + max_values: int | None = 1, + required: bool = True, row: int | None = None, id: int | None = None, description: str | None = None, @@ -80,6 +80,8 @@ def __init__( raise TypeError( f"expected custom_id to be str, not {custom_id.__class__.__name__}" ) + if not isinstance(required, bool): + raise TypeError(f"required must be bool not {value.__class__.__name__}") # type: ignore custom_id = os.urandom(16).hex() if custom_id is None else custom_id self.label: str = str(label) self.description: str | None = description @@ -151,12 +153,12 @@ def max_values(self, value: int | None): self._underlying.max_values = value @property - def required(self) -> bool | None: + def required(self) -> bool: """Whether the input file upload is required or not. Defaults to ``True``.""" return self._underlying.required @required.setter - def required(self, value: bool | None): + def required(self, value: bool): if not isinstance(value, bool): raise TypeError(f"required must be bool not {value.__class__.__name__}") # type: ignore self._underlying.required = bool(value) From dc011a7a98639e6b8cd070c0835d71c2f4721d35 Mon Sep 17 00:00:00 2001 From: plun1331 Date: Thu, 9 Oct 2025 22:21:31 -0700 Subject: [PATCH 27/33] Apply suggestion from @Soheab Co-authored-by: Soheab <33902984+Soheab@users.noreply.github.com> Signed-off-by: plun1331 --- discord/components.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/components.py b/discord/components.py index 8a10a6dac4..ec58a964d9 100644 --- a/discord/components.py +++ b/discord/components.py @@ -1349,7 +1349,7 @@ def walk_components(self) -> Iterator[Component]: class FileUpload(Component): - """Represents an File Upload field from the Discord Bot UI Kit. + """Represents an File Upload component from the Discord Bot UI Kit. This inherits from :class:`Component`. From 807d4ce9666299673e77fd80a79329725e30c9e3 Mon Sep 17 00:00:00 2001 From: plun1331 Date: Thu, 9 Oct 2025 22:21:48 -0700 Subject: [PATCH 28/33] Apply suggestion from @Soheab Co-authored-by: Soheab <33902984+Soheab@users.noreply.github.com> Signed-off-by: plun1331 --- discord/components.py | 1 - 1 file changed, 1 deletion(-) diff --git a/discord/components.py b/discord/components.py index ec58a964d9..940cc65e00 100644 --- a/discord/components.py +++ b/discord/components.py @@ -1365,7 +1365,6 @@ class FileUpload(Component): The custom ID of the file upload field that gets received during an interaction. min_values: Optional[:class:`int`] The minimum number of files that must be uploaded. - Defaults to 0. max_values: Optional[:class:`int`] The maximum number of files that can be uploaded. required: Optional[:class:`bool`] From 1d89fa27831cbadb59a907082e4ae22c35e6c468 Mon Sep 17 00:00:00 2001 From: plun1331 Date: Thu, 9 Oct 2025 22:23:17 -0700 Subject: [PATCH 29/33] Apply suggestion from @Soheab Co-authored-by: Soheab <33902984+Soheab@users.noreply.github.com> Signed-off-by: plun1331 --- discord/components.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/components.py b/discord/components.py index 940cc65e00..310dd43ebb 100644 --- a/discord/components.py +++ b/discord/components.py @@ -1401,10 +1401,10 @@ def to_dict(self) -> FileUploadComponentPayload: if self.id is not None: payload["id"] = self.id - if self.min_values: + if self.min_values is not None: payload["min_values"] = self.min_values - if self.max_values: + if self.max_values is not None: payload["max_values"] = self.max_values if not self.required: From bcd9bcb6bc498959f7f8b1b957044c5da8340694 Mon Sep 17 00:00:00 2001 From: plun1331 Date: Thu, 9 Oct 2025 22:25:26 -0700 Subject: [PATCH 30/33] Update discord/ui/file_upload.py Co-authored-by: Soheab <33902984+Soheab@users.noreply.github.com> Signed-off-by: plun1331 --- discord/ui/file_upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/ui/file_upload.py b/discord/ui/file_upload.py index 8e89ac7d69..3a2da86e45 100644 --- a/discord/ui/file_upload.py +++ b/discord/ui/file_upload.py @@ -15,7 +15,7 @@ class FileUpload: - """Represents a UI file upload field. + """Represents a UI File Upload component .. versionadded:: 2.7 From c8ae70cb570fc5b46fcda5d224295a0aee500ab8 Mon Sep 17 00:00:00 2001 From: plun1331 Date: Thu, 9 Oct 2025 22:26:18 -0700 Subject: [PATCH 31/33] hey you forgot a period @Soheab Signed-off-by: plun1331 --- discord/ui/file_upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/ui/file_upload.py b/discord/ui/file_upload.py index 3a2da86e45..0566865f08 100644 --- a/discord/ui/file_upload.py +++ b/discord/ui/file_upload.py @@ -15,7 +15,7 @@ class FileUpload: - """Represents a UI File Upload component + """Represents a UI File Upload component. .. versionadded:: 2.7 From c9c7143885674d04cb7cdb95ea1b6a12315403f4 Mon Sep 17 00:00:00 2001 From: Paillat-dev Date: Sat, 11 Oct 2025 13:48:41 +0200 Subject: [PATCH 32/33] :rotating_light: Fix unbound `value` --- discord/ui/file_upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/ui/file_upload.py b/discord/ui/file_upload.py index 0566865f08..f337a64349 100644 --- a/discord/ui/file_upload.py +++ b/discord/ui/file_upload.py @@ -81,7 +81,7 @@ def __init__( f"expected custom_id to be str, not {custom_id.__class__.__name__}" ) if not isinstance(required, bool): - raise TypeError(f"required must be bool not {value.__class__.__name__}") # type: ignore + raise TypeError(f"required must be bool not {required.__class__.__name__}") # type: ignore custom_id = os.urandom(16).hex() if custom_id is None else custom_id self.label: str = str(label) self.description: str | None = description From 6b408441ff81fc42ae1be2a2e03e7264df5dd24a Mon Sep 17 00:00:00 2001 From: Soheab <33902984+Soheab@users.noreply.github.com> Date: Sat, 11 Oct 2025 15:24:22 +0200 Subject: [PATCH 33/33] Default min/max_values to None Signed-off-by: Soheab <33902984+Soheab@users.noreply.github.com> --- discord/ui/file_upload.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/ui/file_upload.py b/discord/ui/file_upload.py index f337a64349..1cf7d47b58 100644 --- a/discord/ui/file_upload.py +++ b/discord/ui/file_upload.py @@ -60,8 +60,8 @@ def __init__( *, label: str, custom_id: str | None = None, - min_values: int | None = 0, - max_values: int | None = 1, + min_values: int | None = None, + max_values: int | None = None, required: bool = True, row: int | None = None, id: int | None = None,