Skip to content

Commit

Permalink
Auto populated select menus (#1269)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
FedorLap2006 committed Dec 2, 2022
1 parent 2998b2c commit b818826
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 37 deletions.
46 changes: 36 additions & 10 deletions components.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ type ComponentType uint

// MessageComponent types.
const (
ActionsRowComponent ComponentType = 1
ButtonComponent ComponentType = 2
SelectMenuComponent ComponentType = 3
TextInputComponent ComponentType = 4
ActionsRowComponent ComponentType = 1
ButtonComponent ComponentType = 2
SelectMenuComponent ComponentType = 3
TextInputComponent ComponentType = 4
UserSelectMenuComponent ComponentType = 5
RoleSelectMenuComponent ComponentType = 6
MentionableSelectMenuComponent ComponentType = 7
ChannelSelectMenuComponent ComponentType = 8
)

// MessageComponent is a base interface for all message components.
Expand Down Expand Up @@ -41,7 +45,8 @@ func (umc *unmarshalableMessageComponent) UnmarshalJSON(src []byte) error {
umc.MessageComponent = &ActionsRow{}
case ButtonComponent:
umc.MessageComponent = &Button{}
case SelectMenuComponent:
case SelectMenuComponent, ChannelSelectMenuComponent, UserSelectMenuComponent,
RoleSelectMenuComponent, MentionableSelectMenuComponent:
umc.MessageComponent = &SelectMenu{}
case TextInputComponent:
umc.MessageComponent = &TextInput{}
Expand Down Expand Up @@ -169,8 +174,23 @@ type SelectMenuOption struct {
Default bool `json:"default"`
}

// SelectMenuType represents select menu type.
type SelectMenuType ComponentType

// SelectMenu types.
const (
StringSelectMenu = SelectMenuType(SelectMenuComponent)
UserSelectMenu = SelectMenuType(UserSelectMenuComponent)
RoleSelectMenu = SelectMenuType(RoleSelectMenuComponent)
MentionableSelectMenu = SelectMenuType(MentionableSelectMenuComponent)
ChannelSelectMenu = SelectMenuType(ChannelSelectMenuComponent)
)

// SelectMenu represents select menu component.
type SelectMenu struct {
// Type of the select menu.
MenuType SelectMenuType `json:"type,omitempty"`
// CustomID is a developer-defined identifier for the select menu.
CustomID string `json:"custom_id,omitempty"`
// The text which will be shown in the menu if there's no default options or all options was deselected and component was closed.
Placeholder string `json:"placeholder"`
Expand All @@ -179,25 +199,31 @@ type SelectMenu struct {
// This value determines the maximal amount of selected items in the menu.
// If MaxValues or MinValues are greater than one then the user can select multiple items in the component.
MaxValues int `json:"max_values,omitempty"`
Options []SelectMenuOption `json:"options"`
Options []SelectMenuOption `json:"options,omitempty"`
Disabled bool `json:"disabled"`

// NOTE: Can only be used in SelectMenu with Channel menu type.
ChannelTypes []ChannelType `json:"channel_types,omitempty"`
}

// Type is a method to get the type of a component.
func (SelectMenu) Type() ComponentType {
func (s SelectMenu) Type() ComponentType {
if s.MenuType != 0 {
return ComponentType(s.MenuType)
}
return SelectMenuComponent
}

// MarshalJSON is a method for marshaling SelectMenu to a JSON object.
func (m SelectMenu) MarshalJSON() ([]byte, error) {
func (s SelectMenu) MarshalJSON() ([]byte, error) {
type selectMenu SelectMenu

return Marshal(struct {
selectMenu
Type ComponentType `json:"type"`
}{
selectMenu: selectMenu(m),
Type: m.Type(),
selectMenu: selectMenu(s),
Type: s.Type(),
})
}

Expand Down
93 changes: 66 additions & 27 deletions examples/components/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,38 +165,52 @@ var (
}
time.Sleep(time.Second) // Doing that so user won't see instant response.
_, err = s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{
Content: "Now you know everything about select component. If you want to know more or ask a question - feel free to.",
Components: []discordgo.MessageComponent{
discordgo.ActionsRow{
Components: []discordgo.MessageComponent{
discordgo.Button{
Emoji: discordgo.ComponentEmoji{
Name: "📜",
Content: "But wait, there is more! You can also auto populate the select menu. Try executing `/selects auto-populated`.",
Flags: discordgo.MessageFlagsEphemeral,
})
if err != nil {
panic(err)
}
},
"channel_select": func(s *discordgo.Session, i *discordgo.InteractionCreate) {
err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Content: "This is it. You've reached your destination. Your choice was <#" + i.MessageComponentData().Values[0] + ">\n" +
"If you want to know more, check out the links below",
Components: []discordgo.MessageComponent{
discordgo.ActionsRow{
Components: []discordgo.MessageComponent{
discordgo.Button{
Emoji: discordgo.ComponentEmoji{
Name: "📜",
},
Label: "Documentation",
Style: discordgo.LinkButton,
URL: "https://discord.com/developers/docs/interactions/message-components#select-menus",
},
Label: "Documentation",
Style: discordgo.LinkButton,
URL: "https://discord.com/developers/docs/interactions/message-components#select-menus",
},
discordgo.Button{
Emoji: discordgo.ComponentEmoji{
Name: "🔧",
discordgo.Button{
Emoji: discordgo.ComponentEmoji{
Name: "🔧",
},
Label: "Discord developers",
Style: discordgo.LinkButton,
URL: "https://discord.gg/discord-developers",
},
Label: "Discord developers",
Style: discordgo.LinkButton,
URL: "https://discord.gg/discord-developers",
},
discordgo.Button{
Emoji: discordgo.ComponentEmoji{
Name: "🦫",
discordgo.Button{
Emoji: discordgo.ComponentEmoji{
Name: "🦫",
},
Label: "Discord Gophers",
Style: discordgo.LinkButton,
URL: "https://discord.gg/7RuRrVHyXF",
},
Label: "Discord Gophers",
Style: discordgo.LinkButton,
URL: "https://discord.gg/7RuRrVHyXF",
},
},
},

Flags: discordgo.MessageFlagsEphemeral,
},
Flags: discordgo.MessageFlagsEphemeral,
})
if err != nil {
panic(err)
Expand Down Expand Up @@ -318,7 +332,7 @@ var (
response = &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Content: "The tastiest things are left for the end. Let's see how the multi-item select menu works: " +
Content: "Now let's see how the multi-item select menu works: " +
"try generating your own stackoverflow search link",
Flags: discordgo.MessageFlagsEphemeral,
Components: []discordgo.MessageComponent{
Expand Down Expand Up @@ -381,7 +395,27 @@ var (
},
},
}

case "auto-populated":
response = &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Content: "The tastiest things are left for the end. Meet auto populated select menus.\n" +
"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.",
Flags: discordgo.MessageFlagsEphemeral,
Components: []discordgo.MessageComponent{
discordgo.ActionsRow{
Components: []discordgo.MessageComponent{
discordgo.SelectMenu{
MenuType: discordgo.ChannelSelectMenu,
CustomID: "channel_select",
Placeholder: "Pick your favorite channel!",
ChannelTypes: []discordgo.ChannelType{discordgo.ChannelTypeGuildText},
},
},
},
},
},
}
}
err := s.InteractionRespond(i.Interaction, response)
if err != nil {
Expand Down Expand Up @@ -430,6 +464,11 @@ func main() {
Name: "single",
Description: "Single-item select menu",
},
{
Type: discordgo.ApplicationCommandOptionSubCommand,
Name: "auto-populated",
Description: "Automatically populated select menu, which lets you pick a member, channel or role",
},
},
Description: "Lo and behold: dropdowns are coming",
})
Expand Down

0 comments on commit b818826

Please sign in to comment.