forked from pothtonswer/discordmuffin
Auto populated select menus (#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.
This commit is contained in:
parent
2998b2c67d
commit
b8188269f9
2 changed files with 102 additions and 37 deletions
|
@ -10,10 +10,14 @@ type ComponentType uint
|
||||||
|
|
||||||
// MessageComponent types.
|
// MessageComponent types.
|
||||||
const (
|
const (
|
||||||
ActionsRowComponent ComponentType = 1
|
ActionsRowComponent ComponentType = 1
|
||||||
ButtonComponent ComponentType = 2
|
ButtonComponent ComponentType = 2
|
||||||
SelectMenuComponent ComponentType = 3
|
SelectMenuComponent ComponentType = 3
|
||||||
TextInputComponent ComponentType = 4
|
TextInputComponent ComponentType = 4
|
||||||
|
UserSelectMenuComponent ComponentType = 5
|
||||||
|
RoleSelectMenuComponent ComponentType = 6
|
||||||
|
MentionableSelectMenuComponent ComponentType = 7
|
||||||
|
ChannelSelectMenuComponent ComponentType = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
// MessageComponent is a base interface for all message components.
|
// MessageComponent is a base interface for all message components.
|
||||||
|
@ -41,7 +45,8 @@ func (umc *unmarshalableMessageComponent) UnmarshalJSON(src []byte) error {
|
||||||
umc.MessageComponent = &ActionsRow{}
|
umc.MessageComponent = &ActionsRow{}
|
||||||
case ButtonComponent:
|
case ButtonComponent:
|
||||||
umc.MessageComponent = &Button{}
|
umc.MessageComponent = &Button{}
|
||||||
case SelectMenuComponent:
|
case SelectMenuComponent, ChannelSelectMenuComponent, UserSelectMenuComponent,
|
||||||
|
RoleSelectMenuComponent, MentionableSelectMenuComponent:
|
||||||
umc.MessageComponent = &SelectMenu{}
|
umc.MessageComponent = &SelectMenu{}
|
||||||
case TextInputComponent:
|
case TextInputComponent:
|
||||||
umc.MessageComponent = &TextInput{}
|
umc.MessageComponent = &TextInput{}
|
||||||
|
@ -169,8 +174,23 @@ type SelectMenuOption struct {
|
||||||
Default bool `json:"default"`
|
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.
|
// SelectMenu represents select menu component.
|
||||||
type SelectMenu struct {
|
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"`
|
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.
|
// 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"`
|
Placeholder string `json:"placeholder"`
|
||||||
|
@ -179,25 +199,31 @@ type SelectMenu struct {
|
||||||
// This value determines the maximal amount of selected items in the menu.
|
// 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.
|
// If MaxValues or MinValues are greater than one then the user can select multiple items in the component.
|
||||||
MaxValues int `json:"max_values,omitempty"`
|
MaxValues int `json:"max_values,omitempty"`
|
||||||
Options []SelectMenuOption `json:"options"`
|
Options []SelectMenuOption `json:"options,omitempty"`
|
||||||
Disabled bool `json:"disabled"`
|
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.
|
// 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
|
return SelectMenuComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON is a method for marshaling SelectMenu to a JSON object.
|
// 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
|
type selectMenu SelectMenu
|
||||||
|
|
||||||
return Marshal(struct {
|
return Marshal(struct {
|
||||||
selectMenu
|
selectMenu
|
||||||
Type ComponentType `json:"type"`
|
Type ComponentType `json:"type"`
|
||||||
}{
|
}{
|
||||||
selectMenu: selectMenu(m),
|
selectMenu: selectMenu(s),
|
||||||
Type: m.Type(),
|
Type: s.Type(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -165,38 +165,52 @@ var (
|
||||||
}
|
}
|
||||||
time.Sleep(time.Second) // Doing that so user won't see instant response.
|
time.Sleep(time.Second) // Doing that so user won't see instant response.
|
||||||
_, err = s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{
|
_, 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.",
|
Content: "But wait, there is more! You can also auto populate the select menu. Try executing `/selects auto-populated`.",
|
||||||
Components: []discordgo.MessageComponent{
|
Flags: discordgo.MessageFlagsEphemeral,
|
||||||
discordgo.ActionsRow{
|
})
|
||||||
Components: []discordgo.MessageComponent{
|
if err != nil {
|
||||||
discordgo.Button{
|
panic(err)
|
||||||
Emoji: discordgo.ComponentEmoji{
|
}
|
||||||
Name: "📜",
|
},
|
||||||
|
"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",
|
discordgo.Button{
|
||||||
Style: discordgo.LinkButton,
|
Emoji: discordgo.ComponentEmoji{
|
||||||
URL: "https://discord.com/developers/docs/interactions/message-components#select-menus",
|
Name: "🔧",
|
||||||
},
|
},
|
||||||
discordgo.Button{
|
Label: "Discord developers",
|
||||||
Emoji: discordgo.ComponentEmoji{
|
Style: discordgo.LinkButton,
|
||||||
Name: "🔧",
|
URL: "https://discord.gg/discord-developers",
|
||||||
},
|
},
|
||||||
Label: "Discord developers",
|
discordgo.Button{
|
||||||
Style: discordgo.LinkButton,
|
Emoji: discordgo.ComponentEmoji{
|
||||||
URL: "https://discord.gg/discord-developers",
|
Name: "🦫",
|
||||||
},
|
},
|
||||||
discordgo.Button{
|
Label: "Discord Gophers",
|
||||||
Emoji: discordgo.ComponentEmoji{
|
Style: discordgo.LinkButton,
|
||||||
Name: "🦫",
|
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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -318,7 +332,7 @@ var (
|
||||||
response = &discordgo.InteractionResponse{
|
response = &discordgo.InteractionResponse{
|
||||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
Data: &discordgo.InteractionResponseData{
|
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",
|
"try generating your own stackoverflow search link",
|
||||||
Flags: discordgo.MessageFlagsEphemeral,
|
Flags: discordgo.MessageFlagsEphemeral,
|
||||||
Components: []discordgo.MessageComponent{
|
Components: []discordgo.MessageComponent{
|
||||||
|
@ -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)
|
err := s.InteractionRespond(i.Interaction, response)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -430,6 +464,11 @@ func main() {
|
||||||
Name: "single",
|
Name: "single",
|
||||||
Description: "Single-item select menu",
|
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",
|
Description: "Lo and behold: dropdowns are coming",
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue