package discordgo import ( "encoding/json" "fmt" ) // ComponentType is type of component. type ComponentType uint // MessageComponent types. const ( 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. type MessageComponent interface { json.Marshaler Type() ComponentType } type unmarshalableMessageComponent struct { MessageComponent } // UnmarshalJSON is a helper function to unmarshal MessageComponent object. func (umc *unmarshalableMessageComponent) UnmarshalJSON(src []byte) error { var v struct { Type ComponentType `json:"type"` } err := json.Unmarshal(src, &v) if err != nil { return err } switch v.Type { case ActionsRowComponent: umc.MessageComponent = &ActionsRow{} case ButtonComponent: umc.MessageComponent = &Button{} case SelectMenuComponent, ChannelSelectMenuComponent, UserSelectMenuComponent, RoleSelectMenuComponent, MentionableSelectMenuComponent: umc.MessageComponent = &SelectMenu{} case TextInputComponent: umc.MessageComponent = &TextInput{} default: return fmt.Errorf("unknown component type: %d", v.Type) } return json.Unmarshal(src, umc.MessageComponent) } // MessageComponentFromJSON is a helper function for unmarshaling message components func MessageComponentFromJSON(b []byte) (MessageComponent, error) { var u unmarshalableMessageComponent err := u.UnmarshalJSON(b) if err != nil { return nil, fmt.Errorf("failed to unmarshal into MessageComponent: %w", err) } return u.MessageComponent, nil } // ActionsRow is a container for components within one row. type ActionsRow struct { Components []MessageComponent `json:"components"` } // MarshalJSON is a method for marshaling ActionsRow to a JSON object. func (r ActionsRow) MarshalJSON() ([]byte, error) { type actionsRow ActionsRow return Marshal(struct { actionsRow Type ComponentType `json:"type"` }{ actionsRow: actionsRow(r), Type: r.Type(), }) } // UnmarshalJSON is a helper function to unmarshal Actions Row. func (r *ActionsRow) UnmarshalJSON(data []byte) error { var v struct { RawComponents []unmarshalableMessageComponent `json:"components"` } err := json.Unmarshal(data, &v) if err != nil { return err } r.Components = make([]MessageComponent, len(v.RawComponents)) for i, v := range v.RawComponents { r.Components[i] = v.MessageComponent } return err } // Type is a method to get the type of a component. func (r ActionsRow) Type() ComponentType { return ActionsRowComponent } // ButtonStyle is style of button. type ButtonStyle uint // Button styles. const ( // PrimaryButton is a button with blurple color. PrimaryButton ButtonStyle = 1 // SecondaryButton is a button with grey color. SecondaryButton ButtonStyle = 2 // SuccessButton is a button with green color. SuccessButton ButtonStyle = 3 // DangerButton is a button with red color. DangerButton ButtonStyle = 4 // LinkButton is a special type of button which navigates to a URL. Has grey color. LinkButton ButtonStyle = 5 ) // ComponentEmoji represents button emoji, if it does have one. type ComponentEmoji struct { Name string `json:"name,omitempty"` ID string `json:"id,omitempty"` Animated bool `json:"animated,omitempty"` } // Button represents button component. type Button struct { Label string `json:"label"` Style ButtonStyle `json:"style"` Disabled bool `json:"disabled"` Emoji *ComponentEmoji `json:"emoji,omitempty"` // NOTE: Only button with LinkButton style can have link. Also, URL is mutually exclusive with CustomID. URL string `json:"url,omitempty"` CustomID string `json:"custom_id,omitempty"` } // MarshalJSON is a method for marshaling Button to a JSON object. func (b Button) MarshalJSON() ([]byte, error) { type button Button if b.Style == 0 { b.Style = PrimaryButton } return Marshal(struct { button Type ComponentType `json:"type"` }{ button: button(b), Type: b.Type(), }) } // Type is a method to get the type of a component. func (Button) Type() ComponentType { return ButtonComponent } // SelectMenuOption represents an option for a select menu. type SelectMenuOption struct { Label string `json:"label,omitempty"` Value string `json:"value"` Description string `json:"description"` Emoji *ComponentEmoji `json:"emoji,omitempty"` // Determines whenever option is selected by default or not. Default bool `json:"default"` } // SelectMenuDefaultValueType represents the type of an entity selected by default in auto-populated select menus. type SelectMenuDefaultValueType string // SelectMenuDefaultValue types. const ( SelectMenuDefaultValueUser SelectMenuDefaultValueType = "user" SelectMenuDefaultValueRole SelectMenuDefaultValueType = "role" SelectMenuDefaultValueChannel SelectMenuDefaultValueType = "channel" ) // SelectMenuDefaultValue represents an entity selected by default in auto-populated select menus. type SelectMenuDefaultValue struct { // ID of the entity. ID string `json:"id"` // Type of the entity. Type SelectMenuDefaultValueType `json:"type"` } // 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"` // This value determines the minimal amount of selected items in the menu. MinValues *int `json:"min_values,omitempty"` // 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"` // List of default values for auto-populated select menus. // NOTE: Number of entries should be in the range defined by MinValues and MaxValues. DefaultValues []SelectMenuDefaultValue `json:"default_values,omitempty"` 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 (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 (s SelectMenu) MarshalJSON() ([]byte, error) { type selectMenu SelectMenu return Marshal(struct { selectMenu Type ComponentType `json:"type"` }{ selectMenu: selectMenu(s), Type: s.Type(), }) } // TextInput represents text input component. type TextInput struct { CustomID string `json:"custom_id"` Label string `json:"label"` Style TextInputStyle `json:"style"` Placeholder string `json:"placeholder,omitempty"` Value string `json:"value,omitempty"` Required bool `json:"required"` MinLength int `json:"min_length,omitempty"` MaxLength int `json:"max_length,omitempty"` } // Type is a method to get the type of a component. func (TextInput) Type() ComponentType { return TextInputComponent } // MarshalJSON is a method for marshaling TextInput to a JSON object. func (m TextInput) MarshalJSON() ([]byte, error) { type inputText TextInput return Marshal(struct { inputText Type ComponentType `json:"type"` }{ inputText: inputText(m), Type: m.Type(), }) } // TextInputStyle is style of text in TextInput component. type TextInputStyle uint // Text styles const ( TextInputShort TextInputStyle = 1 TextInputParagraph TextInputStyle = 2 )