DiscordPYComponents 0.2.31__tar.gz → 1.0.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.0.0/DiscordPYComponents.egg-info/PKG-INFO +257 -0
- discordpycomponents-1.0.0/PKG-INFO +257 -0
- discordpycomponents-1.0.0/README.md +245 -0
- discordpycomponents-1.0.0/py_components/__init__.py +73 -0
- discordpycomponents-1.0.0/py_components/components.py +556 -0
- discordpycomponents-1.0.0/py_components/enums.py +55 -0
- discordpycomponents-1.0.0/py_components/http.py +88 -0
- discordpycomponents-1.0.0/py_components/registry.py +43 -0
- discordpycomponents-1.0.0/py_components/ui.py +134 -0
- discordpycomponents-1.0.0/py_components/view.py +102 -0
- discordpycomponents-1.0.0/pyproject.toml +17 -0
- discordpycomponents-0.2.31/DiscordPYComponents.egg-info/PKG-INFO +0 -64
- discordpycomponents-0.2.31/PKG-INFO +0 -64
- discordpycomponents-0.2.31/README.md +0 -56
- discordpycomponents-0.2.31/py_components/__init__.py +0 -4
- discordpycomponents-0.2.31/py_components/components.py +0 -26
- discordpycomponents-0.2.31/py_components/enums.py +0 -5
- discordpycomponents-0.2.31/py_components/http.py +0 -15
- discordpycomponents-0.2.31/py_components/registry.py +0 -10
- discordpycomponents-0.2.31/py_components/ui.py +0 -48
- discordpycomponents-0.2.31/py_components/view.py +0 -33
- discordpycomponents-0.2.31/pyproject.toml +0 -11
- {discordpycomponents-0.2.31 → discordpycomponents-1.0.0}/DiscordPYComponents.egg-info/SOURCES.txt +0 -0
- {discordpycomponents-0.2.31 → discordpycomponents-1.0.0}/DiscordPYComponents.egg-info/dependency_links.txt +0 -0
- {discordpycomponents-0.2.31 → discordpycomponents-1.0.0}/DiscordPYComponents.egg-info/requires.txt +0 -0
- {discordpycomponents-0.2.31 → discordpycomponents-1.0.0}/DiscordPYComponents.egg-info/top_level.txt +0 -0
- {discordpycomponents-0.2.31 → discordpycomponents-1.0.0}/setup.cfg +0 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: DiscordPYComponents
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Discord Components V2 for discord.py — full layout, content and interactive components
|
|
5
|
+
Author: 2gm49
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://pypi.org/project/DiscordPYComponents/
|
|
8
|
+
Project-URL: Documentation, https://docs.discord.com/developers/components/overview
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: aiohttp
|
|
12
|
+
|
|
13
|
+
# DiscordPYComponents
|
|
14
|
+
|
|
15
|
+
**Discord Components V2 for discord.py** — a lightweight helper library for building rich, interactive messages with the modern Components V2 system.
|
|
16
|
+
|
|
17
|
+
> **Requires the `IS_COMPONENTS_V2` flag (`1 << 15`).**
|
|
18
|
+
> When this flag is set, traditional `content` and `embeds` are disabled; all content must be sent as components.
|
|
19
|
+
|
|
20
|
+
PyPI: https://pypi.org/project/DiscordPYComponents/
|
|
21
|
+
|
|
22
|
+
Official Discord docs:
|
|
23
|
+
- [Components Overview](https://docs.discord.com/developers/components/overview)
|
|
24
|
+
- [Using Message Components](https://docs.discord.com/developers/components/using-message-components)
|
|
25
|
+
- [Component Reference](https://docs.discord.com/developers/components/reference)
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install DiscordPYComponents
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Requirements
|
|
34
|
+
|
|
35
|
+
- Python 3.10+
|
|
36
|
+
- discord.py (or any library that gives you a bot token + channel/interaction)
|
|
37
|
+
- aiohttp
|
|
38
|
+
|
|
39
|
+
## Quick start
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from py_components import (
|
|
43
|
+
UI, TextDisplay, Button, ButtonStyle, Container, Separator, Section, Thumbnail
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
@bot.command()
|
|
47
|
+
async def menu(ctx):
|
|
48
|
+
ui = UI(
|
|
49
|
+
TextDisplay("# Welcome"),
|
|
50
|
+
TextDisplay("Choose an option below:"),
|
|
51
|
+
Button("Confirm", custom_id="confirm", style=ButtonStyle.SUCCESS),
|
|
52
|
+
Button("Cancel", custom_id="cancel", style=ButtonStyle.DANGER),
|
|
53
|
+
)
|
|
54
|
+
await ui.send(ctx)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Component types
|
|
58
|
+
|
|
59
|
+
### Content components
|
|
60
|
+
|
|
61
|
+
| Class | Type | Description |
|
|
62
|
+
|-------|------|-------------|
|
|
63
|
+
| `TextDisplay` / `Text` | 10 | Markdown text (1–4000 chars) |
|
|
64
|
+
| `Thumbnail` | 11 | Small image (Section accessory only) |
|
|
65
|
+
| `MediaGallery` | 12 | 1–10 images / media items |
|
|
66
|
+
| `File` | 13 | Displays an uploaded attachment |
|
|
67
|
+
|
|
68
|
+
### Layout components
|
|
69
|
+
|
|
70
|
+
| Class | Type | Description |
|
|
71
|
+
|-------|------|-------------|
|
|
72
|
+
| `ActionRow` | 1 | Holds up to 5 Buttons **or** one Select menu |
|
|
73
|
+
| `Section` | 9 | 1–3 TextDisplays + optional accessory (Button or Thumbnail) |
|
|
74
|
+
| `Separator` | 14 | Vertical padding / visual divider |
|
|
75
|
+
| `Container` | 17 | Groups children with optional accent colour bar |
|
|
76
|
+
|
|
77
|
+
### Interactive components
|
|
78
|
+
|
|
79
|
+
| Class | Type | Description |
|
|
80
|
+
|-------|------|-------------|
|
|
81
|
+
| `Button` | 2 | Clickable button |
|
|
82
|
+
| `StringSelect` | 3 | Dropdown of string options |
|
|
83
|
+
| `UserSelect` | 5 | Select users |
|
|
84
|
+
| `RoleSelect` | 6 | Select roles |
|
|
85
|
+
| `MentionableSelect` | 7 | Select users **or** roles |
|
|
86
|
+
| `ChannelSelect` | 8 | Select channels |
|
|
87
|
+
|
|
88
|
+
## Button styles
|
|
89
|
+
|
|
90
|
+
| Style | Value | Colour / behaviour |
|
|
91
|
+
|-------|-------|--------------------|
|
|
92
|
+
| `ButtonStyle.PRIMARY` | 1 | Blurple |
|
|
93
|
+
| `ButtonStyle.SECONDARY` | 2 | Grey |
|
|
94
|
+
| `ButtonStyle.SUCCESS` | 3 | Green |
|
|
95
|
+
| `ButtonStyle.DANGER` | 4 | Red |
|
|
96
|
+
| `ButtonStyle.LINK` | 5 | Grey – opens a URL (requires `url=`) |
|
|
97
|
+
| `ButtonStyle.PREMIUM` | 6 | Blurple – requires `sku_id=` |
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
Button("Click me", custom_id="btn1", style=ButtonStyle.PRIMARY)
|
|
101
|
+
Button("Docs", style=ButtonStyle.LINK, url="https://discord.com/developers/docs")
|
|
102
|
+
Button("Buy", style=ButtonStyle.PREMIUM, sku_id="1234567890")
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Building a rich layout
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from py_components import (
|
|
109
|
+
UI, Container, TextDisplay, Section, Thumbnail,
|
|
110
|
+
Separator, SeparatorSpacing, MediaGallery, Button, ButtonStyle
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
ui = UI(
|
|
114
|
+
Container(
|
|
115
|
+
TextDisplay("# Server Status"),
|
|
116
|
+
Separator(divider=True, spacing=SeparatorSpacing.SMALL),
|
|
117
|
+
Section(
|
|
118
|
+
TextDisplay("**Online** — 1 204 members"),
|
|
119
|
+
TextDisplay("Peak today: 312"),
|
|
120
|
+
accessory=Thumbnail("https://cdn.discordapp.com/icons/.../icon.png"),
|
|
121
|
+
),
|
|
122
|
+
Separator(),
|
|
123
|
+
MediaGallery(
|
|
124
|
+
"https://example.com/chart1.png",
|
|
125
|
+
"https://example.com/chart2.png",
|
|
126
|
+
),
|
|
127
|
+
accent_color=0x5865F2, # Discord blurple
|
|
128
|
+
),
|
|
129
|
+
Button("Refresh", custom_id="refresh", style=ButtonStyle.PRIMARY),
|
|
130
|
+
)
|
|
131
|
+
await ui.send(ctx)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Top-level components can be mixed freely. The `UI` class automatically wraps loose `Button`s into `ActionRow`s (max 5 per row). Place select menus inside an explicit `ActionRow` yourself:
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from py_components import ActionRow, StringSelect, SelectOption
|
|
138
|
+
|
|
139
|
+
ui = UI(
|
|
140
|
+
TextDisplay("Pick a colour:"),
|
|
141
|
+
ActionRow(
|
|
142
|
+
StringSelect(
|
|
143
|
+
custom_id="colour",
|
|
144
|
+
options=[
|
|
145
|
+
SelectOption("Red", "red"),
|
|
146
|
+
SelectOption("Green", "green"),
|
|
147
|
+
SelectOption("Blue", "blue"),
|
|
148
|
+
],
|
|
149
|
+
placeholder="Choose…",
|
|
150
|
+
)
|
|
151
|
+
),
|
|
152
|
+
)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## View & callbacks
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
from py_components import View, ButtonStyle
|
|
159
|
+
|
|
160
|
+
view = View()
|
|
161
|
+
|
|
162
|
+
async def on_confirm(interaction):
|
|
163
|
+
await interaction.response.send_message("Confirmed!", ephemeral=True)
|
|
164
|
+
|
|
165
|
+
view.add_button("Confirm", custom_id="confirm", style=ButtonStyle.SUCCESS, callback=on_confirm)
|
|
166
|
+
view.add_button("Cancel", custom_id="cancel", style=ButtonStyle.SECONDARY)
|
|
167
|
+
|
|
168
|
+
# Send the payload yourself or combine with UI
|
|
169
|
+
payload = view.to_payload()
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
You can also use the decorator helper:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
from py_components import button, View
|
|
176
|
+
|
|
177
|
+
@button("Click Me", custom_id="my_btn")
|
|
178
|
+
async def on_click(interaction):
|
|
179
|
+
await interaction.response.send_message("Clicked!")
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Global registry
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from py_components import default_registry
|
|
186
|
+
|
|
187
|
+
@default_registry.route("confirm")
|
|
188
|
+
async def handle_confirm(interaction):
|
|
189
|
+
await interaction.response.send_message("OK")
|
|
190
|
+
|
|
191
|
+
# Later, in your interaction listener:
|
|
192
|
+
await default_registry.dispatch(interaction.data["custom_id"], interaction)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## File attachments
|
|
196
|
+
|
|
197
|
+
Upload the file with the message and reference it with the `File` component:
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
from py_components import UI, File, TextDisplay
|
|
201
|
+
|
|
202
|
+
ui = UI(
|
|
203
|
+
TextDisplay("Here is the report:"),
|
|
204
|
+
File("report.pdf"), # becomes attachment://report.pdf
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# When calling the raw HTTP helper you would also pass the file bytes;
|
|
208
|
+
# with discord.py you can combine the payload with files=...
|
|
209
|
+
await ui.send(ctx)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Responding to interactions
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
@bot.event
|
|
216
|
+
async def on_interaction(interaction):
|
|
217
|
+
if interaction.type.name == "component":
|
|
218
|
+
custom_id = interaction.data.get("custom_id")
|
|
219
|
+
# … route to your callbacks …
|
|
220
|
+
# or reply with a new V2 message:
|
|
221
|
+
ui = UI(TextDisplay(f"You pressed `{custom_id}`"))
|
|
222
|
+
await ui.reply(interaction, ephemeral=True)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Payload shape
|
|
226
|
+
|
|
227
|
+
Every payload produced by this library looks like:
|
|
228
|
+
|
|
229
|
+
```json
|
|
230
|
+
{
|
|
231
|
+
"flags": 32768,
|
|
232
|
+
"components": [
|
|
233
|
+
{ "type": 10, "content": "Hello" },
|
|
234
|
+
{
|
|
235
|
+
"type": 1,
|
|
236
|
+
"components": [
|
|
237
|
+
{ "type": 2, "style": 1, "label": "OK", "custom_id": "ok" }
|
|
238
|
+
]
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
You can also obtain the dict with `ui.to_payload()` / `view.to_payload()` and send it through any HTTP client or discord.py’s low-level methods.
|
|
245
|
+
|
|
246
|
+
## Limitations (Discord-side)
|
|
247
|
+
|
|
248
|
+
- Once a message is sent with `IS_COMPONENTS_V2` the flag cannot be removed.
|
|
249
|
+
- `content`, `embeds`, `stickers` and `poll` are disabled on V2 messages.
|
|
250
|
+
- Attachments only appear when referenced by a `File` or media component.
|
|
251
|
+
- Max 40 components per message.
|
|
252
|
+
- Buttons must live in an ActionRow (or as a Section accessory).
|
|
253
|
+
- Only one select menu per ActionRow.
|
|
254
|
+
|
|
255
|
+
## License
|
|
256
|
+
|
|
257
|
+
MIT
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: DiscordPYComponents
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Discord Components V2 for discord.py — full layout, content and interactive components
|
|
5
|
+
Author: 2gm49
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://pypi.org/project/DiscordPYComponents/
|
|
8
|
+
Project-URL: Documentation, https://docs.discord.com/developers/components/overview
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: aiohttp
|
|
12
|
+
|
|
13
|
+
# DiscordPYComponents
|
|
14
|
+
|
|
15
|
+
**Discord Components V2 for discord.py** — a lightweight helper library for building rich, interactive messages with the modern Components V2 system.
|
|
16
|
+
|
|
17
|
+
> **Requires the `IS_COMPONENTS_V2` flag (`1 << 15`).**
|
|
18
|
+
> When this flag is set, traditional `content` and `embeds` are disabled; all content must be sent as components.
|
|
19
|
+
|
|
20
|
+
PyPI: https://pypi.org/project/DiscordPYComponents/
|
|
21
|
+
|
|
22
|
+
Official Discord docs:
|
|
23
|
+
- [Components Overview](https://docs.discord.com/developers/components/overview)
|
|
24
|
+
- [Using Message Components](https://docs.discord.com/developers/components/using-message-components)
|
|
25
|
+
- [Component Reference](https://docs.discord.com/developers/components/reference)
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install DiscordPYComponents
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Requirements
|
|
34
|
+
|
|
35
|
+
- Python 3.10+
|
|
36
|
+
- discord.py (or any library that gives you a bot token + channel/interaction)
|
|
37
|
+
- aiohttp
|
|
38
|
+
|
|
39
|
+
## Quick start
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from py_components import (
|
|
43
|
+
UI, TextDisplay, Button, ButtonStyle, Container, Separator, Section, Thumbnail
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
@bot.command()
|
|
47
|
+
async def menu(ctx):
|
|
48
|
+
ui = UI(
|
|
49
|
+
TextDisplay("# Welcome"),
|
|
50
|
+
TextDisplay("Choose an option below:"),
|
|
51
|
+
Button("Confirm", custom_id="confirm", style=ButtonStyle.SUCCESS),
|
|
52
|
+
Button("Cancel", custom_id="cancel", style=ButtonStyle.DANGER),
|
|
53
|
+
)
|
|
54
|
+
await ui.send(ctx)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Component types
|
|
58
|
+
|
|
59
|
+
### Content components
|
|
60
|
+
|
|
61
|
+
| Class | Type | Description |
|
|
62
|
+
|-------|------|-------------|
|
|
63
|
+
| `TextDisplay` / `Text` | 10 | Markdown text (1–4000 chars) |
|
|
64
|
+
| `Thumbnail` | 11 | Small image (Section accessory only) |
|
|
65
|
+
| `MediaGallery` | 12 | 1–10 images / media items |
|
|
66
|
+
| `File` | 13 | Displays an uploaded attachment |
|
|
67
|
+
|
|
68
|
+
### Layout components
|
|
69
|
+
|
|
70
|
+
| Class | Type | Description |
|
|
71
|
+
|-------|------|-------------|
|
|
72
|
+
| `ActionRow` | 1 | Holds up to 5 Buttons **or** one Select menu |
|
|
73
|
+
| `Section` | 9 | 1–3 TextDisplays + optional accessory (Button or Thumbnail) |
|
|
74
|
+
| `Separator` | 14 | Vertical padding / visual divider |
|
|
75
|
+
| `Container` | 17 | Groups children with optional accent colour bar |
|
|
76
|
+
|
|
77
|
+
### Interactive components
|
|
78
|
+
|
|
79
|
+
| Class | Type | Description |
|
|
80
|
+
|-------|------|-------------|
|
|
81
|
+
| `Button` | 2 | Clickable button |
|
|
82
|
+
| `StringSelect` | 3 | Dropdown of string options |
|
|
83
|
+
| `UserSelect` | 5 | Select users |
|
|
84
|
+
| `RoleSelect` | 6 | Select roles |
|
|
85
|
+
| `MentionableSelect` | 7 | Select users **or** roles |
|
|
86
|
+
| `ChannelSelect` | 8 | Select channels |
|
|
87
|
+
|
|
88
|
+
## Button styles
|
|
89
|
+
|
|
90
|
+
| Style | Value | Colour / behaviour |
|
|
91
|
+
|-------|-------|--------------------|
|
|
92
|
+
| `ButtonStyle.PRIMARY` | 1 | Blurple |
|
|
93
|
+
| `ButtonStyle.SECONDARY` | 2 | Grey |
|
|
94
|
+
| `ButtonStyle.SUCCESS` | 3 | Green |
|
|
95
|
+
| `ButtonStyle.DANGER` | 4 | Red |
|
|
96
|
+
| `ButtonStyle.LINK` | 5 | Grey – opens a URL (requires `url=`) |
|
|
97
|
+
| `ButtonStyle.PREMIUM` | 6 | Blurple – requires `sku_id=` |
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
Button("Click me", custom_id="btn1", style=ButtonStyle.PRIMARY)
|
|
101
|
+
Button("Docs", style=ButtonStyle.LINK, url="https://discord.com/developers/docs")
|
|
102
|
+
Button("Buy", style=ButtonStyle.PREMIUM, sku_id="1234567890")
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Building a rich layout
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from py_components import (
|
|
109
|
+
UI, Container, TextDisplay, Section, Thumbnail,
|
|
110
|
+
Separator, SeparatorSpacing, MediaGallery, Button, ButtonStyle
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
ui = UI(
|
|
114
|
+
Container(
|
|
115
|
+
TextDisplay("# Server Status"),
|
|
116
|
+
Separator(divider=True, spacing=SeparatorSpacing.SMALL),
|
|
117
|
+
Section(
|
|
118
|
+
TextDisplay("**Online** — 1 204 members"),
|
|
119
|
+
TextDisplay("Peak today: 312"),
|
|
120
|
+
accessory=Thumbnail("https://cdn.discordapp.com/icons/.../icon.png"),
|
|
121
|
+
),
|
|
122
|
+
Separator(),
|
|
123
|
+
MediaGallery(
|
|
124
|
+
"https://example.com/chart1.png",
|
|
125
|
+
"https://example.com/chart2.png",
|
|
126
|
+
),
|
|
127
|
+
accent_color=0x5865F2, # Discord blurple
|
|
128
|
+
),
|
|
129
|
+
Button("Refresh", custom_id="refresh", style=ButtonStyle.PRIMARY),
|
|
130
|
+
)
|
|
131
|
+
await ui.send(ctx)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Top-level components can be mixed freely. The `UI` class automatically wraps loose `Button`s into `ActionRow`s (max 5 per row). Place select menus inside an explicit `ActionRow` yourself:
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from py_components import ActionRow, StringSelect, SelectOption
|
|
138
|
+
|
|
139
|
+
ui = UI(
|
|
140
|
+
TextDisplay("Pick a colour:"),
|
|
141
|
+
ActionRow(
|
|
142
|
+
StringSelect(
|
|
143
|
+
custom_id="colour",
|
|
144
|
+
options=[
|
|
145
|
+
SelectOption("Red", "red"),
|
|
146
|
+
SelectOption("Green", "green"),
|
|
147
|
+
SelectOption("Blue", "blue"),
|
|
148
|
+
],
|
|
149
|
+
placeholder="Choose…",
|
|
150
|
+
)
|
|
151
|
+
),
|
|
152
|
+
)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## View & callbacks
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
from py_components import View, ButtonStyle
|
|
159
|
+
|
|
160
|
+
view = View()
|
|
161
|
+
|
|
162
|
+
async def on_confirm(interaction):
|
|
163
|
+
await interaction.response.send_message("Confirmed!", ephemeral=True)
|
|
164
|
+
|
|
165
|
+
view.add_button("Confirm", custom_id="confirm", style=ButtonStyle.SUCCESS, callback=on_confirm)
|
|
166
|
+
view.add_button("Cancel", custom_id="cancel", style=ButtonStyle.SECONDARY)
|
|
167
|
+
|
|
168
|
+
# Send the payload yourself or combine with UI
|
|
169
|
+
payload = view.to_payload()
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
You can also use the decorator helper:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
from py_components import button, View
|
|
176
|
+
|
|
177
|
+
@button("Click Me", custom_id="my_btn")
|
|
178
|
+
async def on_click(interaction):
|
|
179
|
+
await interaction.response.send_message("Clicked!")
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Global registry
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from py_components import default_registry
|
|
186
|
+
|
|
187
|
+
@default_registry.route("confirm")
|
|
188
|
+
async def handle_confirm(interaction):
|
|
189
|
+
await interaction.response.send_message("OK")
|
|
190
|
+
|
|
191
|
+
# Later, in your interaction listener:
|
|
192
|
+
await default_registry.dispatch(interaction.data["custom_id"], interaction)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## File attachments
|
|
196
|
+
|
|
197
|
+
Upload the file with the message and reference it with the `File` component:
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
from py_components import UI, File, TextDisplay
|
|
201
|
+
|
|
202
|
+
ui = UI(
|
|
203
|
+
TextDisplay("Here is the report:"),
|
|
204
|
+
File("report.pdf"), # becomes attachment://report.pdf
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# When calling the raw HTTP helper you would also pass the file bytes;
|
|
208
|
+
# with discord.py you can combine the payload with files=...
|
|
209
|
+
await ui.send(ctx)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Responding to interactions
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
@bot.event
|
|
216
|
+
async def on_interaction(interaction):
|
|
217
|
+
if interaction.type.name == "component":
|
|
218
|
+
custom_id = interaction.data.get("custom_id")
|
|
219
|
+
# … route to your callbacks …
|
|
220
|
+
# or reply with a new V2 message:
|
|
221
|
+
ui = UI(TextDisplay(f"You pressed `{custom_id}`"))
|
|
222
|
+
await ui.reply(interaction, ephemeral=True)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Payload shape
|
|
226
|
+
|
|
227
|
+
Every payload produced by this library looks like:
|
|
228
|
+
|
|
229
|
+
```json
|
|
230
|
+
{
|
|
231
|
+
"flags": 32768,
|
|
232
|
+
"components": [
|
|
233
|
+
{ "type": 10, "content": "Hello" },
|
|
234
|
+
{
|
|
235
|
+
"type": 1,
|
|
236
|
+
"components": [
|
|
237
|
+
{ "type": 2, "style": 1, "label": "OK", "custom_id": "ok" }
|
|
238
|
+
]
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
You can also obtain the dict with `ui.to_payload()` / `view.to_payload()` and send it through any HTTP client or discord.py’s low-level methods.
|
|
245
|
+
|
|
246
|
+
## Limitations (Discord-side)
|
|
247
|
+
|
|
248
|
+
- Once a message is sent with `IS_COMPONENTS_V2` the flag cannot be removed.
|
|
249
|
+
- `content`, `embeds`, `stickers` and `poll` are disabled on V2 messages.
|
|
250
|
+
- Attachments only appear when referenced by a `File` or media component.
|
|
251
|
+
- Max 40 components per message.
|
|
252
|
+
- Buttons must live in an ActionRow (or as a Section accessory).
|
|
253
|
+
- Only one select menu per ActionRow.
|
|
254
|
+
|
|
255
|
+
## License
|
|
256
|
+
|
|
257
|
+
MIT
|