Skip to content
This repository was archived by the owner on Mar 5, 2023. It is now read-only.

Commit b818826

Browse files
authored
Auto populated select menus (bwmarrin#1269)
* feat(components): auto-populated selects * Add component types for user, channel, role and mentionable selects * Add MenuType field to SelectMenu for customization of select type * Add basic example for auto-populated selects * feat: implement SelectMenuType to restrict component types Implement SelectMenuType to restrict component types that can be used in MenuType field of SelectMenu structure. * fix(SelectMenu): default type Default to SelectMenuComponent type when MenuType is not specified. * feat(examples/components): add ephemeral Add ephemeral flag into response to match other component examples. * feat(examples): option response and refactoring * Add a response to the selected option. * Refactor the command to match others. * Remove showcase of multiple menu types.
1 parent 2998b2c commit b818826

File tree

2 files changed

+102
-37
lines changed

2 files changed

+102
-37
lines changed

components.go

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,14 @@ type ComponentType uint
1010

1111
// MessageComponent types.
1212
const (
13-
ActionsRowComponent ComponentType = 1
14-
ButtonComponent ComponentType = 2
15-
SelectMenuComponent ComponentType = 3
16-
TextInputComponent ComponentType = 4
13+
ActionsRowComponent ComponentType = 1
14+
ButtonComponent ComponentType = 2
15+
SelectMenuComponent ComponentType = 3
16+
TextInputComponent ComponentType = 4
17+
UserSelectMenuComponent ComponentType = 5
18+
RoleSelectMenuComponent ComponentType = 6
19+
MentionableSelectMenuComponent ComponentType = 7
20+
ChannelSelectMenuComponent ComponentType = 8
1721
)
1822

1923
// MessageComponent is a base interface for all message components.
@@ -41,7 +45,8 @@ func (umc *unmarshalableMessageComponent) UnmarshalJSON(src []byte) error {
4145
umc.MessageComponent = &ActionsRow{}
4246
case ButtonComponent:
4347
umc.MessageComponent = &Button{}
44-
case SelectMenuComponent:
48+
case SelectMenuComponent, ChannelSelectMenuComponent, UserSelectMenuComponent,
49+
RoleSelectMenuComponent, MentionableSelectMenuComponent:
4550
umc.MessageComponent = &SelectMenu{}
4651
case TextInputComponent:
4752
umc.MessageComponent = &TextInput{}
@@ -169,8 +174,23 @@ type SelectMenuOption struct {
169174
Default bool `json:"default"`
170175
}
171176

177+
// SelectMenuType represents select menu type.
178+
type SelectMenuType ComponentType
179+
180+
// SelectMenu types.
181+
const (
182+
StringSelectMenu = SelectMenuType(SelectMenuComponent)
183+
UserSelectMenu = SelectMenuType(UserSelectMenuComponent)
184+
RoleSelectMenu = SelectMenuType(RoleSelectMenuComponent)
185+
MentionableSelectMenu = SelectMenuType(MentionableSelectMenuComponent)
186+
ChannelSelectMenu = SelectMenuType(ChannelSelectMenuComponent)
187+
)
188+
172189
// SelectMenu represents select menu component.
173190
type SelectMenu struct {
191+
// Type of the select menu.
192+
MenuType SelectMenuType `json:"type,omitempty"`
193+
// CustomID is a developer-defined identifier for the select menu.
174194
CustomID string `json:"custom_id,omitempty"`
175195
// The text which will be shown in the menu if there's no default options or all options was deselected and component was closed.
176196
Placeholder string `json:"placeholder"`
@@ -179,25 +199,31 @@ type SelectMenu struct {
179199
// This value determines the maximal amount of selected items in the menu.
180200
// If MaxValues or MinValues are greater than one then the user can select multiple items in the component.
181201
MaxValues int `json:"max_values,omitempty"`
182-
Options []SelectMenuOption `json:"options"`
202+
Options []SelectMenuOption `json:"options,omitempty"`
183203
Disabled bool `json:"disabled"`
204+
205+
// NOTE: Can only be used in SelectMenu with Channel menu type.
206+
ChannelTypes []ChannelType `json:"channel_types,omitempty"`
184207
}
185208

186209
// Type is a method to get the type of a component.
187-
func (SelectMenu) Type() ComponentType {
210+
func (s SelectMenu) Type() ComponentType {
211+
if s.MenuType != 0 {
212+
return ComponentType(s.MenuType)
213+
}
188214
return SelectMenuComponent
189215
}
190216

191217
// MarshalJSON is a method for marshaling SelectMenu to a JSON object.
192-
func (m SelectMenu) MarshalJSON() ([]byte, error) {
218+
func (s SelectMenu) MarshalJSON() ([]byte, error) {
193219
type selectMenu SelectMenu
194220

195221
return Marshal(struct {
196222
selectMenu
197223
Type ComponentType `json:"type"`
198224
}{
199-
selectMenu: selectMenu(m),
200-
Type: m.Type(),
225+
selectMenu: selectMenu(s),
226+
Type: s.Type(),
201227
})
202228
}
203229

examples/components/main.go

Lines changed: 66 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -165,38 +165,52 @@ var (
165165
}
166166
time.Sleep(time.Second) // Doing that so user won't see instant response.
167167
_, err = s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{
168-
Content: "Now you know everything about select component. If you want to know more or ask a question - feel free to.",
169-
Components: []discordgo.MessageComponent{
170-
discordgo.ActionsRow{
171-
Components: []discordgo.MessageComponent{
172-
discordgo.Button{
173-
Emoji: discordgo.ComponentEmoji{
174-
Name: "📜",
168+
Content: "But wait, there is more! You can also auto populate the select menu. Try executing `/selects auto-populated`.",
169+
Flags: discordgo.MessageFlagsEphemeral,
170+
})
171+
if err != nil {
172+
panic(err)
173+
}
174+
},
175+
"channel_select": func(s *discordgo.Session, i *discordgo.InteractionCreate) {
176+
err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
177+
Type: discordgo.InteractionResponseChannelMessageWithSource,
178+
Data: &discordgo.InteractionResponseData{
179+
Content: "This is it. You've reached your destination. Your choice was <#" + i.MessageComponentData().Values[0] + ">\n" +
180+
"If you want to know more, check out the links below",
181+
Components: []discordgo.MessageComponent{
182+
discordgo.ActionsRow{
183+
Components: []discordgo.MessageComponent{
184+
discordgo.Button{
185+
Emoji: discordgo.ComponentEmoji{
186+
Name: "📜",
187+
},
188+
Label: "Documentation",
189+
Style: discordgo.LinkButton,
190+
URL: "https://discord.com/developers/docs/interactions/message-components#select-menus",
175191
},
176-
Label: "Documentation",
177-
Style: discordgo.LinkButton,
178-
URL: "https://discord.com/developers/docs/interactions/message-components#select-menus",
179-
},
180-
discordgo.Button{
181-
Emoji: discordgo.ComponentEmoji{
182-
Name: "🔧",
192+
discordgo.Button{
193+
Emoji: discordgo.ComponentEmoji{
194+
Name: "🔧",
195+
},
196+
Label: "Discord developers",
197+
Style: discordgo.LinkButton,
198+
URL: "https://discord.gg/discord-developers",
183199
},
184-
Label: "Discord developers",
185-
Style: discordgo.LinkButton,
186-
URL: "https://discord.gg/discord-developers",
187-
},
188-
discordgo.Button{
189-
Emoji: discordgo.ComponentEmoji{
190-
Name: "🦫",
200+
discordgo.Button{
201+
Emoji: discordgo.ComponentEmoji{
202+
Name: "🦫",
203+
},
204+
Label: "Discord Gophers",
205+
Style: discordgo.LinkButton,
206+
URL: "https://discord.gg/7RuRrVHyXF",
191207
},
192-
Label: "Discord Gophers",
193-
Style: discordgo.LinkButton,
194-
URL: "https://discord.gg/7RuRrVHyXF",
195208
},
196209
},
197210
},
211+
212+
Flags: discordgo.MessageFlagsEphemeral,
198213
},
199-
Flags: discordgo.MessageFlagsEphemeral,
200214
})
201215
if err != nil {
202216
panic(err)
@@ -318,7 +332,7 @@ var (
318332
response = &discordgo.InteractionResponse{
319333
Type: discordgo.InteractionResponseChannelMessageWithSource,
320334
Data: &discordgo.InteractionResponseData{
321-
Content: "The tastiest things are left for the end. Let's see how the multi-item select menu works: " +
335+
Content: "Now let's see how the multi-item select menu works: " +
322336
"try generating your own stackoverflow search link",
323337
Flags: discordgo.MessageFlagsEphemeral,
324338
Components: []discordgo.MessageComponent{
@@ -381,7 +395,27 @@ var (
381395
},
382396
},
383397
}
384-
398+
case "auto-populated":
399+
response = &discordgo.InteractionResponse{
400+
Type: discordgo.InteractionResponseChannelMessageWithSource,
401+
Data: &discordgo.InteractionResponseData{
402+
Content: "The tastiest things are left for the end. Meet auto populated select menus.\n" +
403+
"By setting `MenuType` on the select menu you can tell Discord to automatically populate the menu with entities of your choice: roles, members, channels. Try one below.",
404+
Flags: discordgo.MessageFlagsEphemeral,
405+
Components: []discordgo.MessageComponent{
406+
discordgo.ActionsRow{
407+
Components: []discordgo.MessageComponent{
408+
discordgo.SelectMenu{
409+
MenuType: discordgo.ChannelSelectMenu,
410+
CustomID: "channel_select",
411+
Placeholder: "Pick your favorite channel!",
412+
ChannelTypes: []discordgo.ChannelType{discordgo.ChannelTypeGuildText},
413+
},
414+
},
415+
},
416+
},
417+
},
418+
}
385419
}
386420
err := s.InteractionRespond(i.Interaction, response)
387421
if err != nil {
@@ -430,6 +464,11 @@ func main() {
430464
Name: "single",
431465
Description: "Single-item select menu",
432466
},
467+
{
468+
Type: discordgo.ApplicationCommandOptionSubCommand,
469+
Name: "auto-populated",
470+
Description: "Automatically populated select menu, which lets you pick a member, channel or role",
471+
},
433472
},
434473
Description: "Lo and behold: dropdowns are coming",
435474
})

0 commit comments

Comments
 (0)