Skip to content

Commit 7916299

Browse files
committed
reference,learn: move custom interactions to learn
Signed-off-by: Luca Zeuch <[email protected]>
1 parent b6db845 commit 7916299

15 files changed

+578
-0
lines changed

content/learn/advanced/_index.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
+++
2+
title = "Advanced"
3+
weight = 400
4+
+++
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
+++
2+
title = "Custom Interactions"
3+
weight = 410
4+
+++
Loading
Loading
Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
+++
2+
title = "Creating Interactive Elements"
3+
weight = 412
4+
+++
5+
6+
Before you can start triggering custom commands with interactive elements---such as buttons---you'll obviously need
7+
to have elements to interact with. In this section, we'll cover how to create these elements and explore their
8+
differences.
9+
10+
## Message Components
11+
12+
### Buttons
13+
14+
Buttons are probably the simplest interactive element to create, so we'll start with them. To create a button, we use
15+
the [`cbutton`](docs/reference/templates/functions#cbutton) function. In and of itself, that is rather useless, so we'll
16+
also have to attach it to a message. We do that by calling the
17+
[`complexMessage`](docs/reference/templates/functions#complexmessage) builder and adding the result of `cbutton` to it.
18+
Finally, we send the message.
19+
20+
```yag
21+
{{ $button := cbutton "label" "My Cool Button" "custom-id" "buttons-duck" }}
22+
{{ $m := complexMessage "buttons" $button }}
23+
{{ sendMessage nil $m }}
24+
```
25+
26+
Result:
27+
28+
![A basic button, sent with the above code.](basic_button.png)
29+
30+
We've successfully crated a basic button! It doesn't do anything yet, but we'll cover that in a later section.
31+
In the meantime, play around with the values `cbutton` takes. Try to attach an emoji to it, or change its style!
32+
33+
### Select Menus
34+
35+
Select menus act as dropdowns, allowing users to select one or more of several options. To further complicate things,
36+
select menus come in different types, each offering different functionality. We'll first focus on the most intuitive
37+
type, the **Text** select menu. This type allows you to define a custom list of options.
38+
39+
Available select menu types are as follows:
40+
41+
- **Text** - The options available to be selected are defined when creating the select menu. Options have labels, and
42+
can optionally have emojis and longer-form descriptions.
43+
- **User** - The options are populated with users on the server by Discord.
44+
- **Role** - The options are populated with roles on the server by Discord.
45+
- **Mentionable** - The options are auto-populated with both users and roles on the server by Discord, allowing members
46+
to select both.
47+
- **Channel** - The options are populated with channels on the server by Discord. You can limit which channel types
48+
appear as options when creating the select menu.
49+
50+
#### Text Select Menus
51+
52+
To create a text select menu, we use the [`cmenu`](docs/reference/templates/functions#cmenu) function. Then, just like
53+
with a button, we attach it to a message and send it.
54+
55+
```yag
56+
{{ $menu := cmenu
57+
"type" "text"
58+
"placeholder" "Choose a terrible thing"
59+
"custom_id" "menus-duck"
60+
"options" (cslice
61+
(sdict "label" "Two Ducks" "value" "opt-1" "default" true)
62+
(sdict "label" "A Duck" "value" "duck-option" "emoji" (sdict "name" "🦆"))
63+
(sdict "label" "Half a Duck" "value" "third-option" "description" "Don't let the smaller amount fool you."))
64+
"max_values" 3
65+
}}
66+
67+
{{ sendMessage nil (complexMessage "menus" $menu) }}
68+
```
69+
70+
Opening the select menu that was sent using the above code should yield the following result:
71+
72+
![A screenshot of the above menu](text_menu_example.png)
73+
74+
In this menu, our first option (Ducks) is defined as `default`, which is why it is already selected when we look at the
75+
menu on our server. You can define multiple default options, however the amount of default options you define must fall
76+
between your `min_values` and `max_values`.
77+
78+
We have also set the `max_values` to 3, and we haven't set a `min_values` argument. This means the server member could
79+
select anywhere between 1 and 3 of these options.
80+
81+
#### Other Select Menu Types
82+
83+
The other select menu types are created in the same way as the text select menu, but with a few differences. As Discord
84+
automatically populates the options, you need not---nor can you---define these options.
85+
86+
```yag
87+
{{ $menu := cmenu
88+
"type" "role"
89+
"placeholder" "Choose roles who are secretly ducks"
90+
"custom_id" "menus-duck-roles"
91+
"max_values" 3
92+
}}
93+
94+
{{ sendMessage nil (complexMessage "menus" $menu) }}
95+
```
96+
97+
![A screenshot of the above role select menu.](role_select_menu_example.png)
98+
99+
## Modals
100+
101+
Modals are a pop-up form that YAGPDB can send in response to an interaction. It allows users to privately input text
102+
which is sent directly to YAGPDB for use in your custom command. Although modals are not interactive elements in the
103+
same way buttons and menus are, we will cover them here for completeness' sake.
104+
105+
Modals are created using the [`cmodal`](docs/reference/templates/functions#cmodal) function. The modal is then sent
106+
with the [`sendModal`](docs/reference/templates/functions#sendmodal) function. Sending a modal is strictly a response,
107+
meaning it can only be sent once a user clicks a button or uses a select menu. You cannot send a modal as a response to
108+
a user submitting a modal.
109+
110+
### Modal Structure
111+
112+
| Field | Description |
113+
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
114+
| Title | The modal's title, appears at the top of the modal while a user is filling it out. |
115+
| Custom ID | The Custom ID is referenced to trigger a custom command when the modal is submitted (which you'll need to do if you care about retrieving what the user inputted). |
116+
| Fields | A slice of [discordgo.TextInputComponent](https://discord.com/developers/docs/components/reference#text-input) objects. |
117+
118+
Let's consider the following piece of code:
119+
120+
```yag
121+
{{ $modal := cmodal
122+
"title" "My Custom Modal"
123+
"custom_id" "modals-my_first_modal"
124+
"fields" (cslice
125+
(sdict "label" "Name" "placeholder" "Duck" "required" true)
126+
(sdict "label" "Do you like ducks?" "value" "Heck no")
127+
(sdict "label" "Duck hate essay" "min_length" 100 "style" 2)) }}
128+
{{ sendModal $modal }}
129+
```
130+
131+
This code will send a modal with three fields. The first field is a required text input, with "Duck" as a placeholder,
132+
the second field is a text input with a default value, and the third field is a long-form text input that requires at
133+
least 100 characters. The custom ID is set to `modals-my_first_modal`, which helps us identifying the modal when we use
134+
a modal submission trigger for a custom command.
135+
136+
## Multiple Components
137+
138+
You learnt how to create messages with just one component attached to it, but the whole point of components is to have
139+
*a lot* of them available. Let's start with adding some more buttons!
140+
141+
### More Buttons
142+
143+
{{< callout context="note" title="Note: Button Type Limitations" icon="outline/info-circle" >}}
144+
145+
Buttons with the "link" style cannot have a Custom ID, and instead require a URL field.
146+
147+
Link style buttons do not trigger interactions.
148+
149+
{{< /callout >}}
150+
151+
To add more buttons to a single row, simply toss them all into a slice, like so.
152+
153+
```yag
154+
{{ $button1 := cbutton "label" "Duck One" "custom_id" "buttons-duck-alpha" "style" "success" }}
155+
{{ $button2 := cbutton "emoji" (sdict "name" "🦆") "custom_id" "buttons-duck-beta" "style" "danger" }}
156+
{{ $button3 := cbutton "label" "Duck Three" "emoji" (sdict "name" "🦆") "url" "https://yagpdb.xyz" "style" "link" }}
157+
{{ $message := complexMessage "buttons" (cslice $button1 $button2 $button3) }}
158+
{{ sendMessage nil $message }}
159+
```
160+
161+
![Three buttons in a single action row.](many_buttons.png)
162+
163+
At this stage we have three buttons. Both of the first two buttons will trigger our duck trigger custom command, but the
164+
third button will not trigger any custom command. Link buttons do not create _interactions_.
165+
166+
We can differentiate between the two buttons using `.StrippedID`, which, just like `.StrippedMsg`, returns our Custom ID
167+
without the trigger and everything else before that. In our example, `.StrippedID` will return `-alpha` for the first
168+
button and `-beta` for the second button.
169+
170+
Confirming this behavior will be left as an exercise to the reader (you).
171+
172+
Let's add a select menu as well, now.
173+
174+
### Buttons and a Select Menu
175+
176+
We start by copying the previous code over, and define our menu using `cmenu`. Then, it's just simply adding our newly
177+
created menu to the `"menu"` key of the complex message builder, and sending said message.
178+
179+
```yag
180+
{{ $button1 := cbutton "label" "Duck One" "custom_id" "buttons-duck-alpha" "style" "success" }}
181+
{{ $button2 := cbutton "emoji" (sdict "name" "🦆") "custom_id" "buttons-duck-beta" "style" "danger" }}
182+
{{ $button3 := cbutton "label" "Duck Three" "emoji" (sdict "name" "🦆") "url" "https://yagpdb.xyz" "style" "link" }}
183+
184+
{{ $menu := cmenu
185+
"type" "text"
186+
"placeholder" "Choose a terrible thing"
187+
"custom_id" "menus-duck-alpha"
188+
"options" (cslice
189+
(sdict "label" "Ducks" "value" "opt-1" "default" true)
190+
(sdict "label" "Duck" "value" "opt-2" "emoji" (sdict "name" "🦆"))
191+
(sdict "label" "Half a Duck" "value" "opt-3" "description" "Don't let the smaller amount fool you."))
192+
"max_values" 3 }}
193+
194+
{{ $message := complexMessage "buttons" (cslice $button1 $button2 $button3) "menus" $menu }}
195+
{{ sendMessage nil $message }}
196+
```
197+
198+
![Three buttons and a menu in one message.](buttons_and_menus.png)
199+
200+
### Ordering Components
201+
202+
Let's say we want to play Tic Tac Toe. If we just add nine buttons into the same slice in our complex message builder,
203+
they will just fill the first row with five buttons, and the second row with four buttons, which is definitely not what
204+
we are looking for. The solution is to tell YAGPDB precisely how the rows look like and then pass each row to the
205+
`"button"` or `"menu"` key.
206+
207+
```yag
208+
{{ $blankEmoji := sdict "name" "⬜" }}
209+
210+
{{ $row1 := cslice (cbutton "emoji" $blankEmoji "custom_id" "tictactoe-button-1" "style" "secondary") (cbutton "emoji" $blankEmoji "custom_id" "tictactoe-button-2" "style" "secondary") (cbutton "emoji" $blankEmoji "custom_id" "tictactoe-button-3" "style" "secondary") }}
211+
{{ $row2 := cslice (cbutton "emoji" $blankEmoji "custom_id" "tictactoe-button-4" "style" "secondary") (cbutton "emoji" $blankEmoji "custom_id" "tictactoe-button-5" "style" "secondary") (cbutton "emoji" $blankEmoji "custom_id" "tictactoe-button-6" "style" "secondary") }}
212+
{{ $row3 := cslice (cbutton "emoji" $blankEmoji "custom_id" "tictactoe-button-7" "style" "secondary") (cbutton "emoji" $blankEmoji "custom_id" "tictactoe-button-8" "style" "secondary") (cbutton "emoji" $blankEmoji "custom_id" "tictactoe-button-9" "style" "secondary") }}
213+
214+
{{ $message := complexMessage "buttons" $row1 "buttons" $row2 "buttons" $row3 "menus" }}
215+
{{ sendMessage nil $message }}
216+
```
217+
218+
![Tic Tac Toe](tictactoe.png)
219+
220+
#### Action Rows
221+
222+
As you continue to attach more components, YAGPDB will automatically overflow into new rows. However, this may be
223+
undesirable in some cases, such as when you want to have a specific layout for your buttons and/or menus. For that
224+
purpose, `complexMessage` accepts another key, `"components"`, which is essentially just you providing all the action
225+
rows you want.
226+
227+
To illustrate, we'll create a message with five action rows, each with a different number of buttons or menus in it.
228+
229+
```yag
230+
{{ $row1 := cslice (cbutton "label" "Row 1 - Button 1") (cbutton "label" "Row 1 - Button 2") (cbutton "label" "Row 1 - Button 3") (cbutton "label" "Row 1 - Button 4") }}
231+
{{ $row2 := cslice (cbutton "label" "Row 2 - Button 1") (cbutton "label" "Row 2 - Button 1") }}
232+
{{ $row3 := cslice (cmenu "type" "mentionable") }}
233+
{{ $row4 := cslice (cbutton "label" "Row 3 - Button 1") (cbutton "label" "Row 3 - Button 2") (cbutton "label" "Row 3 - Button 3") }}
234+
{{ $row5 := cslice (cmenu "type" "channel") }}
235+
236+
{{ $rows := cslice $row1 $row2 $row3 $row4 $row5 }}
237+
238+
{{ $message := complexMessage "components" $rows }}
239+
{{ sendMessage nil $message }}
240+
```
241+
242+
![Screenshot of message generated by above code.](rows_example.png)
243+
244+
## Final Notes
245+
246+
### Using emojis
247+
248+
Buttons and Select Menu Options both have an `"emoji"` field, but this field does not accept the regular unicode/name:id
249+
formula like reactions do. Emojis in components follow the [partial emoji
250+
object](https://discord.com/developers/docs/resources/emoji#emoji-object) structure, however only the ID *or* the Name
251+
fields are required, depending on if you are using a custom emoji or not.
252+
253+
| Field | Description |
254+
| ----- | ---------------------------------------------------------------------------------------------------- |
255+
| ID | ID of the emoji, only necessary when using Custom Emoji. |
256+
| Name | Name of the emoji, use the unicode character here. Only necessary when using builtin unicode emojis. |
257+
258+
```yag
259+
{{ $unicodeEmojiButton := cbutton "emoji" (sdict "name" "😀") }}
260+
{{ $customEmojiButton := cbutton "emoji" (sdict "id" "733037741532643428") }}
261+
{{ $animatedEmojiButton := cbutton "emoji" (sdict "id" "786307104247775302") }}
262+
263+
{{ $components := cslice $unicodeEmojiButton $customEmojiButton $animatedEmojiButton }}
264+
{{ sendMessage nil (complexMessage "components" $components)}}
265+
```
266+
267+
### Using dictionaries instead
268+
269+
As you probably already discovered when building embeds, you can use `sdict` in favor of `cembed` to create some sort of
270+
skeleton of your embed for later adjustments, with YAGPDB's template engine automagically handling the conversion for
271+
you. This is also true for all the elements we covered in this section, which makes it far easier to conditionally
272+
change certain aspects of your interactive elements.
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
+++
2+
title = "Introduction"
3+
weight = 411
4+
description = "Learn how to use buttons, modals, and select menus in custom commnads."
5+
+++
6+
7+
{{< callout context="danger" title="Danger: Advanced Topic" icon="outline/alert-octagon" >}}
8+
9+
Use of interactions within YAGPDB is an advanced topic; you will need a thorough understanding of YAGPDB's scripting
10+
language before learning interactions.
11+
12+
{{< /callout >}}
13+
14+
Interactions within Discord allow server members to use alternative, built-in features to trigger bots to take action
15+
other than messages or reactions. These features include builtin buttons, dropdown selection menus, or submitting a
16+
modal (basically a pop-up form). Within custom commands it is possible to not only create and customize these new
17+
interactive features, but respond to them as well, opening up new possibilities for ephemeral message responses, modals,
18+
and more within custom commands.
19+
20+
## Interaction Lifetime
21+
22+
An interaction's lifetime starts with the initial *interaction* with an *interactive element*.
23+
24+
1. A server member clicks on a *button*, uses a *menu*, or submits a *modal* after filling it out.
25+
2. This interaction is sent to YAGPDB, and becomes available to trigger any custom commands which match it.
26+
3. Within the triggered custom command(s), YAGPDB should then *respond* once to the interaction, sending a message,
27+
updating the triggering message, or sending a modal. This may only be done within the CC which was triggered by the
28+
interaction.
29+
4. (optional) Continue to send followup responses for up to 15 minutes until the interaction token expires.
30+
31+
```mermaid
32+
graph LR;
33+
A[Button pressed] --> B{CC Triggered}
34+
C[Menu used] --> B
35+
D[Modal submitted] --> B
36+
B --> E[Bot sends message response]
37+
B --> G[Bot sends modal response]
38+
B --> H[Bot updates message]
39+
E -.-> F(Bot sends followups)
40+
G -.-> F
41+
H -.-> F
42+
```
43+
44+
## Definitions
45+
46+
On the following pages, we will use the listed terms with their respective definitions, which are also used as such by
47+
the Discord API.
48+
49+
Interaction
50+
: A user engaging with YAGPDB through one of Discord's builtin features: Clicking a button, Making a
51+
selection with a select menu, or Submitting a modal.
52+
53+
Response
54+
: YAGPDB is required to respond promptly after receiving an interaction by either sending a message or modal, or by
55+
updating the message on which the interaction was triggered. If it does not do this, the user triggering the interaction
56+
will see a "This application did not respond" error. The bot cannot respond to an interaction more than once.
57+
58+
Followup
59+
: Since YAGPDB may only *respond* to an *interaction* once, it is subsequently required to send an interaction
60+
followup if it still needs to interface with the interaction. These followups can be sent up to 15 minutes after the
61+
initial interaction, and you can send as many as you want. YAGPDB may only send a followup in one of the following ways:
62+
Sending a followup message, editing an initial response or previous followup message, or getting an initial response or
63+
previous followup message.
64+
65+
Interactive Elements
66+
: Elements users can interact with to send *interactions*, i.e. buttons, menus, and modals.
67+
68+
Message Components
69+
: *Interactive Elements* which can be attached to YAGPDB's Discord messages, i.e. buttons and menus.
70+
71+
Button
72+
: A button appearing in or under a Discord message sent by YAGPDB. You can create and customize these
73+
buttons' appearance and behavior with color, emoji, label text, etc. When a button is clicked, an *interaction* is sent
74+
to the bot.
75+
76+
Menu
77+
: A dropdown select menu appearing in or under a Discord message sent by YAGPDB. You can create and customize these
78+
menus' appearance and behavior with placeholder text, predefined options with labels, descriptions, and/or emojis,
79+
designate the entire menu as a user or role select menu instead, etc. When a select menu is used, an *interaction* is
80+
sent to the bot.
81+
82+
Modal
83+
: A pop-up form YAGPDB can send in response to an interaction. It allows users to privately input text which
84+
is sent directly to YAGPDB for use in CC scripting. You can create and customize these modals' appearance and
85+
behavior with a title and fields. YAGPDB can both **receive a submitted modal** (which is an
86+
*interaction*), and **send a modal** for a member to fill out, (which is an interaction *response*).
87+
88+
Ephemeral
89+
: An ephemeral message is sent to a server channel but only appears to a single user. YAGPDB cannot send
90+
these ephemeral messages to users except in response to an *interaction*. Both *response* messages and *followup*
91+
messages can be ephemeral.
Loading
Loading

0 commit comments

Comments
 (0)