feat(components)!: implement label component (#1656)

* feat(components): add Label component

* feat: update modals example

* feat(MessageComponentInteractionData): add resolved property

* chore(MessageComponentInteractionDataResolved)!: rename MessageComponentInteractionDataResolved to ComponentInteractionDataResolved
This commit is contained in:
Simone Tesini
2025-10-26 12:14:32 +01:00
committed by GitHub
parent b2bf06886f
commit 6198d49a2e
3 changed files with 87 additions and 29 deletions

View File

@@ -25,6 +25,7 @@ const (
FileComponentType ComponentType = 13
SeparatorComponent ComponentType = 14
ContainerComponent ComponentType = 17
LabelComponent ComponentType = 18
)
// MessageComponent is a base interface for all message components.
@@ -71,6 +72,8 @@ func (umc *unmarshalableMessageComponent) UnmarshalJSON(src []byte) error {
umc.MessageComponent = &Separator{}
case ContainerComponent:
umc.MessageComponent = &Container{}
case LabelComponent:
umc.MessageComponent = &Label{}
default:
return fmt.Errorf("unknown component type: %d", v.Type)
}
@@ -264,6 +267,9 @@ type SelectMenu struct {
// Unique identifier for the component; auto populated through increment if not provided.
ID int `json:"id,omitempty"`
// List of values that is only populated when receiving an interaction response; do not fill this manually.
Values []string `json:"values,omitempty"`
}
// Type is a method to get the type of a component.
@@ -578,6 +584,54 @@ func (c Container) MarshalJSON() ([]byte, error) {
})
}
// Label is a top-level layout component.
// Labels wrap modal components with text as a label and optional description.
type Label struct {
// Unique identifier for the component; auto populated through increment if not provided.
ID int `json:"id,omitempty"`
Label string `json:"label"`
Description string `json:"description,omitempty"`
Component MessageComponent `json:"component"`
}
// Type is a method to get the type of a component.
func (Label) Type() ComponentType {
return LabelComponent
}
// UnmarshalJSON is a method for unmarshaling Label from JSON
func (l *Label) UnmarshalJSON(data []byte) error {
type label Label
var v struct {
label
RawComponent unmarshalableMessageComponent `json:"component"`
}
err := json.Unmarshal(data, &v)
if err != nil {
return err
}
*l = Label(v.label)
l.Component = v.RawComponent.MessageComponent
return nil
}
// MarshalJSON is a method for marshaling Label to a JSON object.
func (l Label) MarshalJSON() ([]byte, error) {
type label Label
return Marshal(struct {
label
Type ComponentType `json:"type"`
}{
label: label(l),
Type: l.Type(),
})
}
// UnfurledMediaItem represents an unfurled media item.
type UnfurledMediaItem struct {
URL string `json:"url"`

View File

@@ -48,29 +48,32 @@ var (
Data: &discordgo.InteractionResponseData{
CustomID: "modals_survey_" + i.Interaction.Member.User.ID,
Title: "Modals survey",
Flags: discordgo.MessageFlagsIsComponentsV2,
Components: []discordgo.MessageComponent{
discordgo.ActionsRow{
Components: []discordgo.MessageComponent{
discordgo.TextInput{
CustomID: "opinion",
Label: "What is your opinion on them?",
Style: discordgo.TextInputShort,
Placeholder: "Don't be shy, share your opinion with us",
Required: true,
MaxLength: 300,
MinLength: 10,
discordgo.Label{
Label: "How would you rate them?",
Description: "On a scale from terrible to awesome",
Component: discordgo.SelectMenu{
MenuType: discordgo.StringSelectMenu,
CustomID: "rating",
Placeholder: "Your rating...",
Options: []discordgo.SelectMenuOption{
{Label: "Terrible", Value: "terrible"},
{Label: "Bad", Value: "bad"},
{Label: "Neutral", Value: "neutral", Default: true},
{Label: "Good", Value: "good"},
{Label: "Awesome", Value: "awesome"},
},
},
},
discordgo.ActionsRow{
Components: []discordgo.MessageComponent{
discordgo.TextInput{
CustomID: "suggestions",
Label: "What would you suggest to improve them?",
Style: discordgo.TextInputParagraph,
Required: false,
MaxLength: 2000,
},
discordgo.Label{
Label: "What would you suggest to improve them?",
Description: "Please provide as much info as possible!",
Component: discordgo.TextInput{
CustomID: "suggestions",
Style: discordgo.TextInputParagraph,
Required: false,
MaxLength: 2000,
},
},
},
@@ -113,10 +116,10 @@ func main() {
userid := strings.Split(data.CustomID, "_")[2]
_, err = s.ChannelMessageSend(*ResultsChannel, fmt.Sprintf(
"Feedback received. From <@%s>\n\n**Opinion**:\n%s\n\n**Suggestions**:\n%s",
"Feedback received. From <@%s>\n\n**Rating**:\n%s\n\n**Suggestions**:\n%s",
userid,
data.Components[0].(*discordgo.ActionsRow).Components[0].(*discordgo.TextInput).Value,
data.Components[1].(*discordgo.ActionsRow).Components[0].(*discordgo.TextInput).Value,
data.Components[0].(*discordgo.Label).Component.(*discordgo.SelectMenu).Values[0],
data.Components[1].(*discordgo.Label).Component.(*discordgo.TextInput).Value,
))
if err != nil {
panic(err)

View File

@@ -381,16 +381,16 @@ func (ApplicationCommandInteractionData) Type() InteractionType {
// MessageComponentInteractionData contains the data of message component interaction.
type MessageComponentInteractionData struct {
CustomID string `json:"custom_id"`
ComponentType ComponentType `json:"component_type"`
Resolved MessageComponentInteractionDataResolved `json:"resolved"`
CustomID string `json:"custom_id"`
ComponentType ComponentType `json:"component_type"`
Resolved ComponentInteractionDataResolved `json:"resolved"`
// NOTE: Only filled when ComponentType is SelectMenuComponent (3). Otherwise is nil.
Values []string `json:"values"`
}
// MessageComponentInteractionDataResolved contains the resolved data of selected option.
type MessageComponentInteractionDataResolved struct {
// ComponentInteractionDataResolved contains the resolved data of selected option.
type ComponentInteractionDataResolved struct {
Users map[string]*User `json:"users"`
Members map[string]*Member `json:"members"`
Roles map[string]*Role `json:"roles"`
@@ -404,8 +404,9 @@ func (MessageComponentInteractionData) Type() InteractionType {
// ModalSubmitInteractionData contains the data of modal submit interaction.
type ModalSubmitInteractionData struct {
CustomID string `json:"custom_id"`
Components []MessageComponent `json:"-"`
CustomID string `json:"custom_id"`
Components []MessageComponent `json:"-"`
Resolved ComponentInteractionDataResolved `json:"resolved"`
}
// Type returns the type of interaction data.