Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions _examples/application_commands/http/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ require (
github.com/disgoorg/omit v1.0.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad // indirect
golang.org/x/crypto v0.37.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/crypto v0.39.0 // indirect
golang.org/x/sys v0.33.0 // indirect
)
2 changes: 2 additions & 0 deletions _examples/application_commands/http/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
100 changes: 64 additions & 36 deletions _examples/componentsv2/example.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/disgoorg/disgo"
"github.com/disgoorg/disgo/bot"
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/disgo/handler"
)

Expand All @@ -36,6 +35,15 @@ var (
Required: false,
},
},
IntegrationTypes: []discord.ApplicationIntegrationType{
discord.ApplicationIntegrationTypeGuildInstall,
discord.ApplicationIntegrationTypeUserInstall,
},
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypeBotDM,
discord.InteractionContextTypePrivateChannel,
},
},
}
)
Expand All @@ -45,9 +53,14 @@ func main() {
slog.Info("disgo version", slog.String("version", disgo.Version))
slog.SetLogLoggerLevel(slog.LevelDebug)

r := handler.New()
r.SlashCommand("/test", onTest)
r.SlashCommand("/modal", onModal)
r.Modal("/modal", onModalSubmit)

client, err := disgo.New(token,
bot.WithDefaultGateway(),
bot.WithEventListenerFunc(onCommand),
bot.WithEventListeners(r),
)
if err != nil {
slog.Error("error while building bot", slog.Any("err", err))
Expand All @@ -70,39 +83,54 @@ func main() {
<-s
}

func onCommand(e *events.ApplicationCommandInteractionCreate) {
switch data := e.Data.(type) {
case discord.SlashCommandInteractionData:
flags := discord.MessageFlagIsComponentsV2
if ephemeral, ok := data.OptBool("ephemeral"); !ok || ephemeral {
flags = flags.Add(discord.MessageFlagEphemeral)
}
if err := e.CreateMessage(discord.MessageCreate{
Flags: flags,
Components: []discord.LayoutComponent{
discord.NewContainer(
discord.NewSection(
discord.NewTextDisplay("**Name: [Seeing Red](https://open.spotify.com/track/65qBr6ToDUjTD1RiE1H4Gl)**"),
discord.NewTextDisplay("**Artist: [Architects](https://open.spotify.com/artist/3ZztVuWxHzNpl0THurTFCv)**"),
discord.NewTextDisplay("**Album: [The Sky, The Earth & All Between](https://open.spotify.com/album/2W82VyyIFAXigJEiLm5TT1)**"),
).WithAccessory(discord.NewThumbnail("attachment://thumbnail.png")),
discord.NewTextDisplay("`0:08`/`3:40`"),
discord.NewTextDisplay("[🔘▬▬▬▬▬▬▬▬▬]"),
discord.NewSmallSeparator(),
discord.NewActionRow(
discord.NewPrimaryButton("", "/player/previous").WithEmoji(discord.ComponentEmoji{Name: "⏮"}),
discord.NewPrimaryButton("", "/player/pause_play").WithEmoji(discord.ComponentEmoji{Name: "⏯"}),
discord.NewPrimaryButton("", "/player/next").WithEmoji(discord.ComponentEmoji{Name: "⏭"}),
discord.NewDangerButton("", "/player/stop").WithEmoji(discord.ComponentEmoji{Name: "⏹"}),
discord.NewPrimaryButton("", "/player/like").WithEmoji(discord.ComponentEmoji{Name: "❤️"}),
),
).WithAccentColor(0x5c5fea),
},
Files: []*discord.File{
discord.NewFile("thumbnail.png", "", bytes.NewReader(thumbnail)),
},
}); err != nil {
slog.Error("error while sending message", slog.Any("err", err))
}
func onTest(data discord.SlashCommandInteractionData, e *handler.CommandEvent) error {
flags := discord.MessageFlagIsComponentsV2
if ephemeral, ok := data.OptBool("ephemeral"); !ok || ephemeral {
flags = flags.Add(discord.MessageFlagEphemeral)
}

return e.CreateMessage(discord.MessageCreate{
Flags: flags,
Components: []discord.LayoutComponent{
discord.NewContainer(
discord.NewSection(
discord.NewTextDisplay("**Name: [Seeing Red](https://open.spotify.com/track/65qBr6ToDUjTD1RiE1H4Gl)**"),
discord.NewTextDisplay("**Artist: [Architects](https://open.spotify.com/artist/3ZztVuWxHzNpl0THurTFCv)**"),
discord.NewTextDisplay("**Album: [The Sky, The Earth & All Between](https://open.spotify.com/album/2W82VyyIFAXigJEiLm5TT1)**"),
).WithAccessory(discord.NewThumbnail("attachment://thumbnail.png")),
discord.NewTextDisplay("`0:08`/`3:40`"),
discord.NewTextDisplay("[🔘▬▬▬▬▬▬▬▬▬]"),
discord.NewSmallSeparator(),
discord.NewActionRow(
discord.NewPrimaryButton("", "/player/previous").WithEmoji(discord.ComponentEmoji{Name: "⏮"}),
discord.NewPrimaryButton("", "/player/pause_play").WithEmoji(discord.ComponentEmoji{Name: "⏯"}),
discord.NewPrimaryButton("", "/player/next").WithEmoji(discord.ComponentEmoji{Name: "⏭"}),
discord.NewDangerButton("", "/player/stop").WithEmoji(discord.ComponentEmoji{Name: "⏹"}),
discord.NewPrimaryButton("", "/player/like").WithEmoji(discord.ComponentEmoji{Name: "❤️"}),
),
).WithAccentColor(0x5c5fea),
},
Files: []*discord.File{
discord.NewFile("thumbnail.png", "", bytes.NewReader(thumbnail)),
},
})
}

func onModal(_ discord.SlashCommandInteractionData, e *handler.CommandEvent) error {
return e.Modal(discord.ModalCreate{
CustomID: "/modal",
Title: "Test Modal",
Components: []discord.LayoutComponent{
discord.NewTextDisplay("This is a modal"),
discord.NewLabel("Test Input", discord.NewShortTextInput("test_input")),
discord.NewLabel("Test Select", discord.NewUserSelectMenu("test_user", "Select a user")),
},
})
}

func onModalSubmit(e *handler.ModalEvent) error {
return e.CreateMessage(discord.MessageCreate{
Content: "You submitted the modal!",
Flags: discord.MessageFlagEphemeral,
})
}
1 change: 0 additions & 1 deletion _examples/test/listeners.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ func componentListener(event *events.ComponentInteractionCreate) {
discord.TextInputComponent{
CustomID: "test_input",
Style: discord.TextInputStyleShort,
Label: "qwq",
Required: true,
Placeholder: "test placeholder",
Value: "uwu",
Expand Down
23 changes: 13 additions & 10 deletions discord/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ type ContainerSubComponent interface {
// LabelSubComponent is an interface for all components that can be present in a [LabelComponent].
// [StringSelectMenuComponent]
// [TextInputComponent]
// [UserSelectMenuComponent]
// [RoleSelectMenuComponent]
// [MentionableSelectMenuComponent]
// [ChannelSelectMenuComponent]
// [UnknownComponent]
type LabelSubComponent interface {
Component
// labelSubComponent is a marker to simulate unions.
Expand Down Expand Up @@ -608,16 +613,14 @@ var (
//
// [Discord Docs]: https://discord.com/developers/docs/interactions/message-components#text-inputs
type TextInputComponent struct {
ID int `json:"id,omitempty"`
CustomID string `json:"custom_id"`
Style TextInputStyle `json:"style"`
// Deprecated: Label is deprecated and will be removed in a future version. Use [LabelComponent] instead.
Label string `json:"label,omitempty"`
MinLength *int `json:"min_length,omitempty"`
MaxLength int `json:"max_length,omitempty"`
Required bool `json:"required"`
Placeholder string `json:"placeholder,omitempty"`
Value string `json:"value,omitempty"`
ID int `json:"id,omitempty"`
CustomID string `json:"custom_id"`
Style TextInputStyle `json:"style"`
MinLength *int `json:"min_length,omitempty"`
MaxLength int `json:"max_length,omitempty"`
Required bool `json:"required"`
Placeholder string `json:"placeholder,omitempty"`
Value string `json:"value,omitempty"`
}

func (c TextInputComponent) MarshalJSON() ([]byte, error) {
Expand Down
46 changes: 43 additions & 3 deletions discord/select_menu.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ var (
_ Component = (*UserSelectMenuComponent)(nil)
_ InteractiveComponent = (*UserSelectMenuComponent)(nil)
_ SelectMenuComponent = (*UserSelectMenuComponent)(nil)
_ LabelSubComponent = (*UserSelectMenuComponent)(nil)
)

// NewUserSelectMenu builds a new SelectMenuComponent from the provided values
Expand All @@ -235,6 +236,8 @@ type UserSelectMenuComponent struct {
DefaultValues []SelectMenuDefaultValue `json:"default_values,omitempty"`
MinValues *int `json:"min_values,omitempty"`
MaxValues int `json:"max_values,omitempty"`
// Required Indicates if the select menu is required to submit the Modal.
Required bool `json:"required"`
// Disabled whether the select menu is disabled (only supported in messages)
Disabled bool `json:"disabled"`
}
Expand Down Expand Up @@ -265,6 +268,7 @@ func (c UserSelectMenuComponent) GetCustomID() string {
func (UserSelectMenuComponent) component() {}
func (UserSelectMenuComponent) interactiveComponent() {}
func (UserSelectMenuComponent) selectMenuComponent() {}
func (UserSelectMenuComponent) labelSubComponent() {}

// WithCustomID returns a new UserSelectMenuComponent with the provided customID
func (c UserSelectMenuComponent) WithCustomID(customID string) UserSelectMenuComponent {
Expand Down Expand Up @@ -338,10 +342,17 @@ func (c UserSelectMenuComponent) WithID(id int) UserSelectMenuComponent {
return c
}

// WithRequired returns a new UserSelectMenuComponent with the provided required value
func (c UserSelectMenuComponent) WithRequired(required bool) UserSelectMenuComponent {
c.Required = required
return c
}

var (
_ Component = (*UserSelectMenuComponent)(nil)
_ InteractiveComponent = (*UserSelectMenuComponent)(nil)
_ SelectMenuComponent = (*UserSelectMenuComponent)(nil)
_ Component = (*RoleSelectMenuComponent)(nil)
_ InteractiveComponent = (*RoleSelectMenuComponent)(nil)
_ SelectMenuComponent = (*RoleSelectMenuComponent)(nil)
_ LabelSubComponent = (*RoleSelectMenuComponent)(nil)
)

// NewRoleSelectMenu builds a new SelectMenuComponent from the provided values
Expand All @@ -359,6 +370,8 @@ type RoleSelectMenuComponent struct {
DefaultValues []SelectMenuDefaultValue `json:"default_values,omitempty"`
MinValues *int `json:"min_values,omitempty"`
MaxValues int `json:"max_values,omitempty"`
// Required Indicates if the select menu is required to submit the Modal.
Required bool `json:"required"`
// Disabled whether the select menu is disabled (only supported in messages)
Disabled bool `json:"disabled"`
}
Expand Down Expand Up @@ -389,6 +402,7 @@ func (c RoleSelectMenuComponent) GetCustomID() string {
func (RoleSelectMenuComponent) component() {}
func (RoleSelectMenuComponent) interactiveComponent() {}
func (RoleSelectMenuComponent) selectMenuComponent() {}
func (RoleSelectMenuComponent) labelSubComponent() {}

// WithCustomID returns a new RoleSelectMenuComponent with the provided customID
func (c RoleSelectMenuComponent) WithCustomID(customID string) RoleSelectMenuComponent {
Expand Down Expand Up @@ -462,10 +476,17 @@ func (c RoleSelectMenuComponent) WithID(id int) RoleSelectMenuComponent {
return c
}

// WithRequired returns a new RoleSelectMenuComponent with the provided required value
func (c RoleSelectMenuComponent) WithRequired(required bool) RoleSelectMenuComponent {
c.Required = required
return c
}

var (
_ Component = (*MentionableSelectMenuComponent)(nil)
_ InteractiveComponent = (*MentionableSelectMenuComponent)(nil)
_ SelectMenuComponent = (*MentionableSelectMenuComponent)(nil)
_ LabelSubComponent = (*MentionableSelectMenuComponent)(nil)
)

// NewMentionableSelectMenu builds a new SelectMenuComponent from the provided values
Expand All @@ -483,6 +504,8 @@ type MentionableSelectMenuComponent struct {
DefaultValues []SelectMenuDefaultValue `json:"default_values,omitempty"`
MinValues *int `json:"min_values,omitempty"`
MaxValues int `json:"max_values,omitempty"`
// Required Indicates if the select menu is required to submit the Modal.
Required bool `json:"required"`
// Disabled whether the select menu is disabled (only supported in messages)
Disabled bool `json:"disabled"`
}
Expand Down Expand Up @@ -513,6 +536,7 @@ func (c MentionableSelectMenuComponent) GetCustomID() string {
func (MentionableSelectMenuComponent) component() {}
func (MentionableSelectMenuComponent) interactiveComponent() {}
func (MentionableSelectMenuComponent) selectMenuComponent() {}
func (MentionableSelectMenuComponent) labelSubComponent() {}

// WithCustomID returns a new MentionableSelectMenuComponent with the provided customID
func (c MentionableSelectMenuComponent) WithCustomID(customID string) MentionableSelectMenuComponent {
Expand Down Expand Up @@ -583,10 +607,17 @@ func (c MentionableSelectMenuComponent) WithID(id int) MentionableSelectMenuComp
return c
}

// WithRequired returns a new MentionableSelectMenuComponent with the provided required value
func (c MentionableSelectMenuComponent) WithRequired(required bool) MentionableSelectMenuComponent {
c.Required = required
return c
}

var (
_ Component = (*ChannelSelectMenuComponent)(nil)
_ InteractiveComponent = (*ChannelSelectMenuComponent)(nil)
_ SelectMenuComponent = (*ChannelSelectMenuComponent)(nil)
_ LabelSubComponent = (*ChannelSelectMenuComponent)(nil)
)

// NewChannelSelectMenu builds a new SelectMenuComponent from the provided values
Expand All @@ -605,6 +636,8 @@ type ChannelSelectMenuComponent struct {
MinValues *int `json:"min_values,omitempty"`
MaxValues int `json:"max_values,omitempty"`
ChannelTypes []ChannelType `json:"channel_types,omitempty"`
// Required Indicates if the select menu is required to submit the Modal.
Required bool `json:"required"`
// Disabled whether the select menu is disabled (only supported in messages)
Disabled bool `json:"disabled"`
}
Expand Down Expand Up @@ -635,6 +668,7 @@ func (c ChannelSelectMenuComponent) GetCustomID() string {
func (ChannelSelectMenuComponent) component() {}
func (ChannelSelectMenuComponent) interactiveComponent() {}
func (ChannelSelectMenuComponent) selectMenuComponent() {}
func (ChannelSelectMenuComponent) labelSubComponent() {}

// WithCustomID returns a new ChannelSelectMenuComponent with the provided customID
func (c ChannelSelectMenuComponent) WithCustomID(customID string) ChannelSelectMenuComponent {
Expand Down Expand Up @@ -714,6 +748,12 @@ func (c ChannelSelectMenuComponent) WithID(id int) ChannelSelectMenuComponent {
return c
}

// WithRequired returns a new ChannelSelectMenuComponent with the provided required value
func (c ChannelSelectMenuComponent) WithRequired(required bool) ChannelSelectMenuComponent {
c.Required = required
return c
}

type SelectMenuDefaultValue struct {
Type SelectMenuDefaultValueType `json:"type"`
ID snowflake.ID `json:"id"`
Expand Down
Loading