DiscordPYComponents 1.3.2__tar.gz → 1.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {discordpycomponents-1.3.2 → discordpycomponents-1.4.0}/DiscordPYComponents.egg-info/PKG-INFO +184 -28
- {discordpycomponents-1.3.2 → discordpycomponents-1.4.0}/DiscordPYComponents.egg-info/SOURCES.txt +1 -0
- {discordpycomponents-1.3.2 → discordpycomponents-1.4.0}/PKG-INFO +184 -28
- {discordpycomponents-1.3.2 → discordpycomponents-1.4.0}/README.md +183 -27
- {discordpycomponents-1.3.2 → discordpycomponents-1.4.0}/py_components/__init__.py +43 -2
- discordpycomponents-1.4.0/py_components/components.py +1161 -0
- discordpycomponents-1.4.0/py_components/enums.py +115 -0
- {discordpycomponents-1.3.2 → discordpycomponents-1.4.0}/py_components/ids.py +29 -5
- discordpycomponents-1.4.0/py_components/interactions.py +167 -0
- discordpycomponents-1.4.0/py_components/modal.py +76 -0
- {discordpycomponents-1.3.2 → discordpycomponents-1.4.0}/py_components/registry.py +11 -0
- {discordpycomponents-1.3.2 → discordpycomponents-1.4.0}/py_components/ui.py +53 -3
- {discordpycomponents-1.3.2 → discordpycomponents-1.4.0}/py_components/view.py +13 -2
- {discordpycomponents-1.3.2 → discordpycomponents-1.4.0}/pyproject.toml +1 -1
- discordpycomponents-1.3.2/py_components/components.py +0 -839
- discordpycomponents-1.3.2/py_components/enums.py +0 -53
- discordpycomponents-1.3.2/py_components/modal.py +0 -46
- {discordpycomponents-1.3.2 → discordpycomponents-1.4.0}/DiscordPYComponents.egg-info/dependency_links.txt +0 -0
- {discordpycomponents-1.3.2 → discordpycomponents-1.4.0}/DiscordPYComponents.egg-info/requires.txt +0 -0
- {discordpycomponents-1.3.2 → discordpycomponents-1.4.0}/DiscordPYComponents.egg-info/top_level.txt +0 -0
- {discordpycomponents-1.3.2 → discordpycomponents-1.4.0}/py_components/http.py +0 -0
- {discordpycomponents-1.3.2 → discordpycomponents-1.4.0}/setup.cfg +0 -0
{discordpycomponents-1.3.2 → discordpycomponents-1.4.0}/DiscordPYComponents.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: DiscordPYComponents
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: Discord Components V2 for discord.py
|
|
5
5
|
Author: 2gm49
|
|
6
6
|
License: MIT
|
|
@@ -82,6 +82,9 @@ await ui.send(ctx)
|
|
|
82
82
|
| `await ui.send(target, ephemeral=False, files=None)` | channel or interaction |
|
|
83
83
|
| `await ui.reply(interaction, ...)` | same as send on an interaction |
|
|
84
84
|
| `await ui.edit(target, message=None)` | edit interaction original / a Message |
|
|
85
|
+
| `await ui.update(interaction)` | edit the message a button/select was used on |
|
|
86
|
+
| `ui.count()` | total components, nested ones included |
|
|
87
|
+
| `UI.from_payload(data)` | rebuild a UI from a raw payload or components list |
|
|
85
88
|
|
|
86
89
|
`send` / `reply` try discord.py first (`ctx.send`, `interaction.response.send_message`, `followup.send`). Falls back to raw HTTP if that fails.
|
|
87
90
|
|
|
@@ -93,9 +96,21 @@ await ui.edit(interaction)
|
|
|
93
96
|
await ui.edit(message)
|
|
94
97
|
```
|
|
95
98
|
|
|
96
|
-
Also: `await send_ui(target, ui)
|
|
99
|
+
Also: `await send_ui(target, ui)`, same as `ui.send(target)`.
|
|
97
100
|
|
|
98
|
-
Loose `Button`s are auto-wrapped into `ActionRow`s (5 per row).
|
|
101
|
+
Loose `Button`s are auto-wrapped into `ActionRow`s (5 per row). Loose selects get their own `ActionRow`.
|
|
102
|
+
|
|
103
|
+
`to_payload()` raises `ValueError` if the message would go over Discord's 40 component limit (nested components count too).
|
|
104
|
+
|
|
105
|
+
### Send options
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
await ui.send(ctx, silent=True) # no push notification
|
|
109
|
+
await ui.send(ctx, allowed_mentions=discord.AllowedMentions.none())
|
|
110
|
+
await ui.reply(interaction, ephemeral=True)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Pings inside `TextDisplay` follow `allowed_mentions`.
|
|
99
114
|
|
|
100
115
|
---
|
|
101
116
|
|
|
@@ -129,6 +144,8 @@ Thumbnail("https://cdn.discordapp.com/...", description="icon", spoiler=False)
|
|
|
129
144
|
| `spoiler` | blur it |
|
|
130
145
|
| `id` | optional |
|
|
131
146
|
|
|
147
|
+
`url` can be `attachment://name.png` to use a file sent with the message.
|
|
148
|
+
|
|
132
149
|
### MediaGallery / MediaGalleryItem
|
|
133
150
|
|
|
134
151
|
1–10 media items.
|
|
@@ -145,7 +162,12 @@ MediaGallery(
|
|
|
145
162
|
| `*items` | urls and/or `MediaGalleryItem` |
|
|
146
163
|
| `id` | optional |
|
|
147
164
|
|
|
148
|
-
`MediaGalleryItem(url, description=None, spoiler=False)
|
|
165
|
+
`MediaGalleryItem(url, description=None, spoiler=False)`. Description max 1024.
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
gallery = MediaGallery("https://example.com/a.png")
|
|
169
|
+
gallery.add("https://example.com/b.png")
|
|
170
|
+
```
|
|
149
171
|
|
|
150
172
|
### File
|
|
151
173
|
|
|
@@ -162,13 +184,15 @@ File("attachment://report.pdf", spoiler=True)
|
|
|
162
184
|
| `spoiler` | blur it |
|
|
163
185
|
| `id` | optional |
|
|
164
186
|
|
|
187
|
+
When you read a File back from a message (`UI.from_payload`), `.name` and `.size` are filled in from Discord.
|
|
188
|
+
|
|
165
189
|
---
|
|
166
190
|
|
|
167
191
|
## Layout components
|
|
168
192
|
|
|
169
193
|
### ActionRow
|
|
170
194
|
|
|
171
|
-
Holds up to 5 buttons **or** one select.
|
|
195
|
+
Holds up to 5 buttons **or** one select. Mixing them raises `ValueError`.
|
|
172
196
|
|
|
173
197
|
```python
|
|
174
198
|
ActionRow(
|
|
@@ -180,7 +204,7 @@ ActionRow(StringSelect("pick", options=[...]))
|
|
|
180
204
|
|
|
181
205
|
### Section
|
|
182
206
|
|
|
183
|
-
1–3 TextDisplays + optional accessory (Button or Thumbnail).
|
|
207
|
+
1–3 TextDisplays + optional accessory (Button or Thumbnail). Plain strings are turned into TextDisplays.
|
|
184
208
|
|
|
185
209
|
```python
|
|
186
210
|
Section(
|
|
@@ -219,11 +243,13 @@ Container(
|
|
|
219
243
|
)
|
|
220
244
|
```
|
|
221
245
|
|
|
222
|
-
Children can be ActionRow, TextDisplay, Section, MediaGallery, Separator, File.
|
|
246
|
+
Children can be ActionRow, TextDisplay, Section, MediaGallery, Separator, File. Anything else raises `ValueError`.
|
|
247
|
+
|
|
248
|
+
`accent_color` takes an int (`0x000000`–`0xFFFFFF`) or a `discord.Colour`. `accent_colour=` works too.
|
|
223
249
|
|
|
224
250
|
```python
|
|
225
|
-
c = Container(TextDisplay("x"))
|
|
226
|
-
c.add(Separator())
|
|
251
|
+
c = Container(TextDisplay("x"), accent_color=discord.Colour.blurple())
|
|
252
|
+
c.add(Separator(), TextDisplay("more"))
|
|
227
253
|
```
|
|
228
254
|
|
|
229
255
|
---
|
|
@@ -250,7 +276,7 @@ Button(
|
|
|
250
276
|
| `LINK` | 5 | url= |
|
|
251
277
|
| `PREMIUM` | 6 | sku_id= |
|
|
252
278
|
|
|
253
|
-
Lowercase aliases work too: `ButtonStyle.primary`,
|
|
279
|
+
Lowercase aliases work too: `ButtonStyle.primary`, and the discord.py names `blurple`, `grey`, `green`, `red`, `url`.
|
|
254
280
|
|
|
255
281
|
```python
|
|
256
282
|
Button("docs", style=ButtonStyle.LINK, url="https://discord.com/developers/docs")
|
|
@@ -260,7 +286,11 @@ Button("nope", custom_id="x", disabled=True)
|
|
|
260
286
|
|
|
261
287
|
Must sit in an ActionRow or as a Section accessory. UI auto-wraps loose buttons.
|
|
262
288
|
|
|
263
|
-
|
|
289
|
+
Rules (checked when you create the button):
|
|
290
|
+
- needs a `label` or an `emoji` (except PREMIUM)
|
|
291
|
+
- label max 80, custom_id 1–100, url max 512
|
|
292
|
+
- LINK: `url`, no `custom_id`
|
|
293
|
+
- PREMIUM: `sku_id` only, no label, emoji, url or custom_id
|
|
264
294
|
|
|
265
295
|
---
|
|
266
296
|
|
|
@@ -268,6 +298,8 @@ Label max 80 chars. custom_id 1–100 chars.
|
|
|
268
298
|
|
|
269
299
|
All selects need a `custom_id`. In messages they go inside an ActionRow (one select per row). In modals they go inside a Label.
|
|
270
300
|
|
|
301
|
+
Shared rules: `placeholder` max 150, `min_values` 0–25, `max_values` 1–25, `min_values <= max_values`. If you set `required=True`, `min_values` must be at least 1.
|
|
302
|
+
|
|
271
303
|
### SelectOption (StringSelect only)
|
|
272
304
|
|
|
273
305
|
```python
|
|
@@ -278,8 +310,12 @@ SelectOption(
|
|
|
278
310
|
emoji="🔴",
|
|
279
311
|
default=False,
|
|
280
312
|
)
|
|
313
|
+
|
|
314
|
+
SelectOption("Red") # value defaults to the label
|
|
281
315
|
```
|
|
282
316
|
|
|
317
|
+
Label, value and description max 100.
|
|
318
|
+
|
|
283
319
|
### StringSelect
|
|
284
320
|
|
|
285
321
|
```python
|
|
@@ -297,7 +333,7 @@ StringSelect(
|
|
|
297
333
|
)
|
|
298
334
|
```
|
|
299
335
|
|
|
300
|
-
1–25 options.
|
|
336
|
+
1–25 options with unique values. `max_values` can't be more than the number of options. Plain strings work as options: `StringSelect("colour", ["red", "blue"])`.
|
|
301
337
|
|
|
302
338
|
### UserSelect / RoleSelect / MentionableSelect / ChannelSelect
|
|
303
339
|
|
|
@@ -322,8 +358,23 @@ ChannelSelect(
|
|
|
322
358
|
)
|
|
323
359
|
```
|
|
324
360
|
|
|
325
|
-
`default_values`
|
|
361
|
+
`default_values` takes any of:
|
|
362
|
+
|
|
363
|
+
```python
|
|
364
|
+
from py_components import SelectDefaultValue, ChannelType
|
|
326
365
|
|
|
366
|
+
UserSelect("u", default_values=[member, 1234567890]) # objects or ids
|
|
367
|
+
RoleSelect("r", default_values=[SelectDefaultValue.role(role)])
|
|
368
|
+
MentionableSelect("m", default_values=[
|
|
369
|
+
SelectDefaultValue.user(111),
|
|
370
|
+
SelectDefaultValue.role(222),
|
|
371
|
+
])
|
|
372
|
+
ChannelSelect("c", channel_types=[ChannelType.text, ChannelType.voice],
|
|
373
|
+
default_values=[channel])
|
|
374
|
+
{"id": "<snowflake>", "type": "user"} # raw dicts still work
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
The number of defaults has to fit between `min_values` and `max_values`, and the type has to match the select (MentionableSelect needs `SelectDefaultValue.user/role` for bare ids).
|
|
327
378
|
`required` only matters in modals. `disabled` only in messages (modals error if disabled).
|
|
328
379
|
|
|
329
380
|
---
|
|
@@ -337,6 +388,8 @@ make_emoji("🔥")
|
|
|
337
388
|
make_emoji("pepe", id=123456789012345678, animated=True)
|
|
338
389
|
```
|
|
339
390
|
|
|
391
|
+
Anywhere an emoji is accepted you can pass a unicode string, a `<:name:id>` / `<a:name:id>` string, a dict, or a discord.py `Emoji` / `PartialEmoji`.
|
|
392
|
+
|
|
340
393
|
Buttons and SelectOptions also take a plain unicode string:
|
|
341
394
|
|
|
342
395
|
```python
|
|
@@ -370,6 +423,19 @@ replace_by_id(payload["components"], 10, TextDisplay("x", id=10).to_dict())
|
|
|
370
423
|
|
|
371
424
|
Use this when editing a message and only changing one piece.
|
|
372
425
|
|
|
426
|
+
Ids are only added to components, never to media gallery items.
|
|
427
|
+
|
|
428
|
+
### Reading components back
|
|
429
|
+
|
|
430
|
+
```python
|
|
431
|
+
from py_components import UI, component_from_dict
|
|
432
|
+
|
|
433
|
+
ui = UI.from_payload(raw_message_dict) # or a components list
|
|
434
|
+
first = component_from_dict(raw_message_dict["components"][0])
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
Every component class builds from its raw dict. Component types this library doesn't know yet come back as `UnknownComponent`, which keeps the raw data and sends it back unchanged.
|
|
438
|
+
|
|
373
439
|
---
|
|
374
440
|
|
|
375
441
|
## Modals
|
|
@@ -377,7 +443,7 @@ Use this when editing a message and only changing one piece.
|
|
|
377
443
|
```python
|
|
378
444
|
from py_components import (
|
|
379
445
|
Modal, Label, TextInput, TextInputStyle,
|
|
380
|
-
RadioGroup, RadioGroupOption, CheckboxGroup, Checkbox,
|
|
446
|
+
RadioGroup, RadioGroupOption, CheckboxGroup, GroupOption, Checkbox,
|
|
381
447
|
FileUpload, StringSelect, SelectOption,
|
|
382
448
|
)
|
|
383
449
|
|
|
@@ -415,7 +481,9 @@ async def report(interaction):
|
|
|
415
481
|
await modal.open(interaction)
|
|
416
482
|
```
|
|
417
483
|
|
|
418
|
-
Max **5** top-level components. Top-level
|
|
484
|
+
Max **5** top-level components. Top-level must be `Label` or `TextDisplay`. Interactive controls go **inside** a Label. Components inside a modal can't be `disabled`. All of this is checked when you build the modal.
|
|
485
|
+
|
|
486
|
+
`modal.open(interaction)` must be the first response to the interaction. It raises if you've already responded.
|
|
419
487
|
|
|
420
488
|
### Modal
|
|
421
489
|
|
|
@@ -435,7 +503,7 @@ Title max 45 chars. custom_id 1–100.
|
|
|
435
503
|
Label("visible label", child_component, description="optional helper text")
|
|
436
504
|
```
|
|
437
505
|
|
|
438
|
-
Label text max 45. Description max 100. One child.
|
|
506
|
+
Label text max 45. Description max 100. One child: TextInput, any select, FileUpload, RadioGroup, CheckboxGroup or Checkbox.
|
|
439
507
|
|
|
440
508
|
### TextInput
|
|
441
509
|
|
|
@@ -451,6 +519,10 @@ TextInput(
|
|
|
451
519
|
)
|
|
452
520
|
```
|
|
453
521
|
|
|
522
|
+
min_length 0–4000, max_length 1–4000, value max 4000, placeholder max 100.
|
|
523
|
+
|
|
524
|
+
`TextInput(..., label="...")` still works for old ActionRow modals but gives a `DeprecationWarning`. Use `Label` instead.
|
|
525
|
+
|
|
454
526
|
### FileUpload
|
|
455
527
|
|
|
456
528
|
```python
|
|
@@ -463,6 +535,8 @@ FileUpload(
|
|
|
463
535
|
)
|
|
464
536
|
```
|
|
465
537
|
|
|
538
|
+
Up to 10 files, up to 10 `file_types` (`image`, `video`, `audio` or a `.ext`). File size follows the user's own upload limit.
|
|
539
|
+
|
|
466
540
|
### RadioGroup / CheckboxGroup / Checkbox
|
|
467
541
|
|
|
468
542
|
```python
|
|
@@ -478,10 +552,12 @@ CheckboxGroup(
|
|
|
478
552
|
required=True,
|
|
479
553
|
)
|
|
480
554
|
|
|
481
|
-
# single yes/no
|
|
555
|
+
# single yes/no, label comes from the Label wrapper
|
|
482
556
|
Checkbox("agree", default=False)
|
|
483
557
|
```
|
|
484
558
|
|
|
559
|
+
A Checkbox can't be required. For a "must tick" box, use a CheckboxGroup with one option and `required=True`. RadioGroup allows one `default` option.
|
|
560
|
+
|
|
485
561
|
`GroupOption` / `RadioGroupOption` / `CheckboxGroupOption` are the same thing:
|
|
486
562
|
|
|
487
563
|
```python
|
|
@@ -490,7 +566,39 @@ GroupOption("label", "value", description="...", default=False)
|
|
|
490
566
|
|
|
491
567
|
Selects also work inside Label in modals (StringSelect, UserSelect, etc.).
|
|
492
568
|
|
|
493
|
-
|
|
569
|
+
### Reading a modal submit
|
|
570
|
+
|
|
571
|
+
```python
|
|
572
|
+
from py_components import ModalSubmit
|
|
573
|
+
|
|
574
|
+
@bot.event
|
|
575
|
+
async def on_interaction(interaction):
|
|
576
|
+
if interaction.type.name == "modal_submit" and interaction.data["custom_id"] == "apply":
|
|
577
|
+
form = ModalSubmit(interaction)
|
|
578
|
+
|
|
579
|
+
why = form.text("why") # TextInput -> str
|
|
580
|
+
level = form.get("exp", "none") # RadioGroup -> str or None
|
|
581
|
+
teams = form.selected("teams") # CheckboxGroup / selects -> list[str]
|
|
582
|
+
agreed = form.checked("rules") # Checkbox -> bool
|
|
583
|
+
files = form.attachments("shots") # FileUpload -> attachment dicts
|
|
584
|
+
refs = form.users("ref") # UserSelect -> resolved user dicts
|
|
585
|
+
|
|
586
|
+
await interaction.response.send_message(f"thanks, got {len(files)} files", ephemeral=True)
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
| method | returns |
|
|
590
|
+
|--------|---------|
|
|
591
|
+
| `form["id"]` | raw value |
|
|
592
|
+
| `form.get("id", default)` | value, or default if empty |
|
|
593
|
+
| `form.text("id")` | str |
|
|
594
|
+
| `form.checked("id")` | bool |
|
|
595
|
+
| `form.selected("id")` | list of str |
|
|
596
|
+
| `form.attachments("id")` | uploaded attachment dicts |
|
|
597
|
+
| `form.users / members / roles / channels("id")` | resolved objects for selects |
|
|
598
|
+
| `form.values()` | dict of every custom_id and value |
|
|
599
|
+
| `form.fields` | `SubmittedField` objects (custom_id, type, id, value, raw) |
|
|
600
|
+
|
|
601
|
+
Works with a discord.py `Interaction` or a raw interaction dict. `parse_modal(x)` is the same as `ModalSubmit(x)`.
|
|
494
602
|
|
|
495
603
|
---
|
|
496
604
|
|
|
@@ -553,9 +661,29 @@ Wire it up in your bot:
|
|
|
553
661
|
```python
|
|
554
662
|
@bot.event
|
|
555
663
|
async def on_interaction(interaction):
|
|
556
|
-
if interaction.type.name
|
|
557
|
-
|
|
558
|
-
|
|
664
|
+
if interaction.type.name in ("component", "modal_submit"):
|
|
665
|
+
await default_registry.dispatch_interaction(interaction)
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
`dispatch_interaction` reads the custom_id for you and returns False if nothing matched. Views have the same method, plus `view.add_select(select, callback=...)`.
|
|
669
|
+
|
|
670
|
+
### Reading a button or select
|
|
671
|
+
|
|
672
|
+
```python
|
|
673
|
+
from py_components import ComponentClick
|
|
674
|
+
|
|
675
|
+
click = ComponentClick(interaction)
|
|
676
|
+
click.custom_id # "range"
|
|
677
|
+
click.values # ["7d"] for selects, [] for buttons
|
|
678
|
+
click.value # first value or None
|
|
679
|
+
click.is_select # True
|
|
680
|
+
click.resolved # users / roles / channels for entity selects
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
Answer by editing the message in place:
|
|
684
|
+
|
|
685
|
+
```python
|
|
686
|
+
await new_ui.update(interaction)
|
|
559
687
|
```
|
|
560
688
|
|
|
561
689
|
---
|
|
@@ -583,11 +711,18 @@ API base: `https://discord.com/api/v10`.
|
|
|
583
711
|
|
|
584
712
|
```python
|
|
585
713
|
from py_components import (
|
|
586
|
-
ComponentType,
|
|
587
|
-
ButtonStyle,
|
|
588
|
-
TextInputStyle,
|
|
589
|
-
SeparatorSpacing,
|
|
590
|
-
|
|
714
|
+
ComponentType, # ACTION_ROW=1, BUTTON=2, ... CHECKBOX=23
|
|
715
|
+
ButtonStyle, # PRIMARY..PREMIUM
|
|
716
|
+
TextInputStyle, # SHORT=1, PARAGRAPH=2
|
|
717
|
+
SeparatorSpacing, # SMALL=1, LARGE=2
|
|
718
|
+
ChannelType, # GUILD_TEXT=0, GUILD_VOICE=2, ... (for ChannelSelect)
|
|
719
|
+
InteractionResponseType, # UPDATE_MESSAGE=7, MODAL=9, ...
|
|
720
|
+
MessageFlags, # EPHEMERAL, SUPPRESS_NOTIFICATIONS, IS_COMPONENTS_V2
|
|
721
|
+
IS_COMPONENTS_V2, # 1 << 15
|
|
722
|
+
EPHEMERAL, # 1 << 6
|
|
723
|
+
SUPPRESS_NOTIFICATIONS, # 1 << 12
|
|
724
|
+
MAX_MESSAGE_COMPONENTS, # 40
|
|
725
|
+
MAX_MODAL_COMPONENTS, # 5
|
|
591
726
|
)
|
|
592
727
|
```
|
|
593
728
|
|
|
@@ -685,9 +820,30 @@ await modal.open(interaction)
|
|
|
685
820
|
- 40 components max per message
|
|
686
821
|
- buttons: ActionRow or Section accessory
|
|
687
822
|
- one select per ActionRow
|
|
688
|
-
- modals: max 5 top-level components
|
|
823
|
+
- modals: max 5 top-level components, title ≤45
|
|
689
824
|
- Label label ≤45, description ≤100
|
|
690
|
-
- Button label ≤80, custom_id ≤100
|
|
825
|
+
- Button label ≤80, custom_id ≤100, url ≤512
|
|
826
|
+
- select placeholder ≤150, 0–25 values, 25 options
|
|
827
|
+
- TextDisplay ≤4000, thumbnail/gallery descriptions ≤1024
|
|
828
|
+
- component `id` is a 32-bit int, unique in the message
|
|
829
|
+
|
|
830
|
+
The library checks these when you build components, so mistakes show up as a `ValueError` in your code instead of a 400 from Discord.
|
|
831
|
+
|
|
832
|
+
## Changelog
|
|
833
|
+
|
|
834
|
+
### 1.4.0
|
|
835
|
+
- Read components back: `UI.from_payload`, `component_from_dict`, `UnknownComponent` for future types
|
|
836
|
+
- `ModalSubmit` / `parse_modal` to read modal answers, including file uploads and resolved users, roles and channels
|
|
837
|
+
- `ComponentClick` / `parse_component` for buttons and selects
|
|
838
|
+
- `Registry.dispatch_interaction` and `View.dispatch_interaction` for components and modals
|
|
839
|
+
- `SelectDefaultValue` and object/id support for select `default_values`
|
|
840
|
+
- `ChannelType`, `InteractionResponseType`, `MessageFlags` enums
|
|
841
|
+
- `ui.send(silent=True, allowed_mentions=...)`, `ui.update()`, `ui.count()`
|
|
842
|
+
- Loose selects in a `UI` or `View` are wrapped in an ActionRow automatically
|
|
843
|
+
- Validation for every Discord limit: 40 component cap, placeholder, option counts, min/max values, file types, button rules, container and section children, modal children, accent colour range
|
|
844
|
+
- Emoji accepts `<:name:id>` strings and discord.py emoji objects; Container accepts `discord.Colour`
|
|
845
|
+
- `TextInput(label=...)` kept for legacy modals with a deprecation warning
|
|
846
|
+
- Fixed: ids were being added to media gallery items
|
|
691
847
|
|
|
692
848
|
## License
|
|
693
849
|
|