|
1 | 1 | import uuid
|
2 | 2 | from enum import IntEnum
|
3 | 3 | from typing import Union, Optional, Any, TypeVar
|
| 4 | +from typing_extensions import Self |
4 | 5 |
|
5 | 6 | import discord_typings
|
6 | 7 |
|
7 | 8 | from interactions.client.const import MISSING
|
8 | 9 | from interactions.client.mixins.serialization import DictSerializationMixin
|
9 |
| -from interactions.client.utils import dict_filter |
10 |
| -from interactions.models.discord.components import ComponentType |
| 10 | +from interactions.client.utils import dict_filter, dict_filter_none |
| 11 | +from interactions.models.discord.components import ComponentType, BaseComponent, StringSelectMenu |
11 | 12 | from interactions.models.internal.application_commands import CallbackType
|
12 | 13 |
|
13 |
| -__all__ = ("InputText", "Modal", "ParagraphText", "ShortText", "TextStyles") |
| 14 | +__all__ = ("InputText", "Modal", "ParagraphText", "ShortText", "TextStyles", "LabelComponent") |
14 | 15 |
|
15 | 16 | T = TypeVar("T", bound="InputText")
|
16 | 17 |
|
@@ -62,7 +63,7 @@ def to_dict(
|
62 | 63 | )
|
63 | 64 |
|
64 | 65 | @classmethod
|
65 |
| - def from_dict(cls, data: dict[str, Any]) -> T: |
| 66 | + def from_dict(cls, data: dict[str, Any]) -> Self: |
66 | 67 | if data["style"] == TextStyles.SHORT:
|
67 | 68 | cls = ShortText
|
68 | 69 | elif data["style"] == TextStyles.PARAGRAPH:
|
@@ -127,36 +128,84 @@ def __init__(
|
127 | 128 | )
|
128 | 129 |
|
129 | 130 |
|
| 131 | +class LabelComponent(BaseComponent): |
| 132 | + def __init__( |
| 133 | + self, |
| 134 | + *, |
| 135 | + label: str, |
| 136 | + description: Optional[str] = None, |
| 137 | + component: StringSelectMenu | InputText, |
| 138 | + ): |
| 139 | + self.label = label |
| 140 | + self.component = component |
| 141 | + self.description = description |
| 142 | + self.type = ComponentType.LABEL |
| 143 | + |
| 144 | + def to_dict(self) -> dict: |
| 145 | + return dict_filter_none( |
| 146 | + { |
| 147 | + "type": self.type, |
| 148 | + "label": self.label, |
| 149 | + "description": self.description, |
| 150 | + "component": self.component.to_dict() if hasattr(self.component, "to_dict") else self.component, |
| 151 | + } |
| 152 | + ) |
| 153 | + |
| 154 | + @classmethod |
| 155 | + def from_dict(cls, data: dict) -> Self: |
| 156 | + return cls( |
| 157 | + label=data["label"], |
| 158 | + description=data.get("description"), |
| 159 | + component=BaseComponent.from_dict_factory( |
| 160 | + data["component"], |
| 161 | + alternate_mapping={ |
| 162 | + ComponentType.INPUT_TEXT: InputText, |
| 163 | + ComponentType.STRING_SELECT: StringSelectMenu, |
| 164 | + }, |
| 165 | + ), |
| 166 | + ) |
| 167 | + |
| 168 | + |
130 | 169 | class Modal:
|
131 | 170 | def __init__(
|
132 | 171 | self,
|
133 |
| - *components: InputText, |
| 172 | + *components: InputText | LabelComponent, |
134 | 173 | title: str,
|
135 | 174 | custom_id: Optional[str] = None,
|
136 | 175 | ) -> None:
|
137 | 176 | self.title: str = title
|
138 |
| - self.components: list[InputText] = list(components) |
| 177 | + self.components: list[InputText | LabelComponent] = list(components) |
139 | 178 | self.custom_id: str = custom_id or str(uuid.uuid4())
|
140 | 179 |
|
141 | 180 | self.type = CallbackType.MODAL
|
142 | 181 |
|
143 | 182 | def to_dict(self) -> discord_typings.ModalInteractionData:
|
| 183 | + dict_components: list[dict] = [] |
| 184 | + |
| 185 | + for component in self.components: |
| 186 | + if isinstance(component, InputText): |
| 187 | + dict_components.append({"type": ComponentType.ACTION_ROW, "components": [component.to_dict()]}) |
| 188 | + elif isinstance(component, LabelComponent): |
| 189 | + dict_components.append(component.to_dict()) |
| 190 | + else: |
| 191 | + # backwards compatibility behavior, remove in v6 |
| 192 | + dict_components.append( |
| 193 | + { |
| 194 | + "type": ComponentType.ACTION_ROW, |
| 195 | + "components": [component], |
| 196 | + } |
| 197 | + ) |
| 198 | + |
144 | 199 | return {
|
145 | 200 | "type": self.type,
|
146 | 201 | "data": {
|
147 | 202 | "title": self.title,
|
148 | 203 | "custom_id": self.custom_id,
|
149 |
| - "components": [ |
150 |
| - { |
151 |
| - "type": ComponentType.ACTION_ROW, |
152 |
| - "components": [c.to_dict() if hasattr(c, "to_dict") else c], |
153 |
| - } |
154 |
| - for c in self.components |
155 |
| - ], |
| 204 | + "components": dict_components, |
156 | 205 | },
|
157 | 206 | }
|
158 | 207 |
|
159 |
| - def add_components(self, *components: InputText) -> None: |
| 208 | + def add_components(self, *components: InputText | LabelComponent) -> None: |
160 | 209 | """
|
161 | 210 | Add components to the modal.
|
162 | 211 |
|
|
0 commit comments