From 27679cb3978cd03515376a2b1ab6616754889db4 Mon Sep 17 00:00:00 2001 From: TopiSenpai Date: Tue, 23 Nov 2021 02:20:12 +0100 Subject: [PATCH 1/5] moved all components into one file & added modals --- core/autocomplete_interaction.go | 8 +- core/modal_submit_interaction.go | 69 +++ core/slash_command_interaction.go | 10 +- discord/action_row.go | 101 ---- ...application_command_autocomplete_result.go | 32 -- discord/button.go | 140 ------ discord/component.go | 447 ++++++++++++++++++ discord/interaction.go | 52 +- discord/interaction_callback.go | 42 ++ discord/modal_submit.go | 126 +++++ discord/select_menu.go | 170 ------- 11 files changed, 740 insertions(+), 457 deletions(-) create mode 100644 core/modal_submit_interaction.go delete mode 100644 discord/action_row.go delete mode 100644 discord/application_command_autocomplete_result.go delete mode 100644 discord/button.go create mode 100644 discord/modal_submit.go delete mode 100644 discord/select_menu.go diff --git a/core/autocomplete_interaction.go b/core/autocomplete_interaction.go index 26271449..b3753eb1 100644 --- a/core/autocomplete_interaction.go +++ b/core/autocomplete_interaction.go @@ -7,6 +7,8 @@ import ( type AutocompleteInteractionFilter func(autocompleteInteraction *AutocompleteInteraction) bool +var _ Interaction = (*AutocompleteInteraction)(nil) + type AutocompleteInteraction struct { discord.AutocompleteInteraction *InteractionFields @@ -20,10 +22,6 @@ type AutocompleteInteractionData struct { Options AutocompleteOptionsMap } -func (i *AutocompleteInteraction) InteractionType() discord.InteractionType { - return discord.InteractionTypeAutocomplete -} - func (i *AutocompleteInteraction) Respond(callbackType discord.InteractionCallbackType, callbackData discord.InteractionCallbackData, opts ...rest.RequestOpt) error { return respond(i.InteractionFields, i.ID, i.Token, callbackType, callbackData, opts...) } @@ -46,7 +44,7 @@ func (i *AutocompleteInteraction) ResultMapFloat(resultMap map[string]float64, o // CommandPath returns the ApplicationCommand path func (i *AutocompleteInteraction) CommandPath() string { - return commandPath(i.Data.CommandName, i.Data.SubCommandName,i.Data.SubCommandGroupName) + return commandPath(i.Data.CommandName, i.Data.SubCommandName, i.Data.SubCommandGroupName) } // Guild returns the Guild from the Caches diff --git a/core/modal_submit_interaction.go b/core/modal_submit_interaction.go new file mode 100644 index 00000000..471799e2 --- /dev/null +++ b/core/modal_submit_interaction.go @@ -0,0 +1,69 @@ +package core + +import ( + "github.com/DisgoOrg/disgo/discord" + "github.com/DisgoOrg/disgo/rest" +) + +type ModalSubmitInteractionFilter func(ModalSubmitInteraction *ModalSubmitInteraction) bool + +var _ Interaction = (*ModalSubmitInteraction)(nil) + +type ModalSubmitInteraction struct { + discord.ModalSubmitInteraction + *InteractionFields +} + + +func (i *ModalSubmitInteraction) Respond(callbackType discord.InteractionCallbackType, callbackData discord.InteractionCallbackData, opts ...rest.RequestOpt) error { + return respond(i.InteractionFields, i.ID, i.Token, callbackType, callbackData, opts...) +} + +func (i *ModalSubmitInteraction) Create(messageCreate discord.MessageCreate, opts ...rest.RequestOpt) error { + return create(i.InteractionFields, i.ID, i.Token, messageCreate, opts...) +} + +func (i *ModalSubmitInteraction) DeferCreate(ephemeral bool, opts ...rest.RequestOpt) error { + return deferCreate(i.InteractionFields, i.ID, i.Token, ephemeral, opts...) +} + +func (i *ModalSubmitInteraction) GetOriginal(opts ...rest.RequestOpt) (*Message, error) { + return getOriginal(i.InteractionFields, i.ApplicationID, i.Token, opts...) +} + +func (i *ModalSubmitInteraction) UpdateOriginal(messageUpdate discord.MessageUpdate, opts ...rest.RequestOpt) (*Message, error) { + return updateOriginal(i.InteractionFields, i.ApplicationID, i.Token, messageUpdate, opts...) +} + +func (i *ModalSubmitInteraction) DeleteOriginal(opts ...rest.RequestOpt) error { + return deleteOriginal(i.InteractionFields, i.ApplicationID, i.Token, opts...) +} + +func (i *ModalSubmitInteraction) GetFollowup(messageID discord.Snowflake, opts ...rest.RequestOpt) (*Message, error) { + return getFollowup(i.InteractionFields, i.ApplicationID, i.Token, messageID, opts...) +} + +func (i *ModalSubmitInteraction) CreateFollowup(messageCreate discord.MessageCreate, opts ...rest.RequestOpt) (*Message, error) { + return createFollowup(i.InteractionFields, i.ApplicationID, i.Token, messageCreate, opts...) +} + +func (i *ModalSubmitInteraction) UpdateFollowup(messageID discord.Snowflake, messageUpdate discord.MessageUpdate, opts ...rest.RequestOpt) (*Message, error) { + return updateFollowup(i.InteractionFields, i.ApplicationID, i.Token, messageID, messageUpdate, opts...) +} + +func (i *ModalSubmitInteraction) DeleteFollowup(messageID discord.Snowflake, opts ...rest.RequestOpt) error { + return deleteFollowup(i.InteractionFields, i.ApplicationID, i.Token, messageID, opts...) +} + +// Guild returns the Guild from the Caches +func (i *ModalSubmitInteraction) Guild() *Guild { + if i.GuildID == nil { + return nil + } + return i.Bot.Caches.Guilds().Get(*i.GuildID) +} + +// Channel returns the Channel from the Caches +func (i *ModalSubmitInteraction) Channel() Channel { + return i.Bot.Caches.Channels().Get(i.ChannelID) +} \ No newline at end of file diff --git a/core/slash_command_interaction.go b/core/slash_command_interaction.go index 9c69bc58..816ad286 100644 --- a/core/slash_command_interaction.go +++ b/core/slash_command_interaction.go @@ -7,6 +7,8 @@ import ( type SlashCommandInteractionFilter func(slashCommandInteraction *SlashCommandInteraction) bool +var _ Interaction = (*SlashCommandInteraction)(nil) + type SlashCommandInteraction struct { discord.SlashCommandInteraction *InteractionFields @@ -21,14 +23,6 @@ type SlashCommandInteractionData struct { Options SlashCommandOptionsMap } -func (i *SlashCommandInteraction) InteractionType() discord.InteractionType { - return discord.InteractionTypeApplicationCommand -} - -func (i *SlashCommandInteraction) ApplicationCommandType() discord.ApplicationCommandType { - return discord.ApplicationCommandTypeSlash -} - func (i *SlashCommandInteraction) Respond(callbackType discord.InteractionCallbackType, callbackData discord.InteractionCallbackData, opts ...rest.RequestOpt) error { return respond(i.InteractionFields, i.ID, i.Token, callbackType, callbackData, opts...) } diff --git a/discord/action_row.go b/discord/action_row.go deleted file mode 100644 index 49e8f326..00000000 --- a/discord/action_row.go +++ /dev/null @@ -1,101 +0,0 @@ -package discord - -import "github.com/DisgoOrg/disgo/json" - -var ( - _ Component = (*ActionRowComponent)(nil) - _ ContainerComponent = (*ActionRowComponent)(nil) -) - -//goland:noinspection GoUnusedExportedFunction -func NewActionRow(components ...InteractiveComponent) ActionRowComponent { - return components -} - -type ActionRowComponent []InteractiveComponent - -func (c ActionRowComponent) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Type ComponentType `json:"type"` - Components []InteractiveComponent `json:"components"` - }{ - Type: c.Type(), - Components: c, - }) -} - -func (c *ActionRowComponent) UnmarshalJSON(data []byte) error { - var actionRow struct { - Components []UnmarshalComponent `json:"components"` - } - - if err := json.Unmarshal(data, &actionRow); err != nil { - return err - } - - if len(actionRow.Components) > 0 { - *c = make([]InteractiveComponent, len(actionRow.Components)) - for i, component := range actionRow.Components { - (*c)[i] = component.Component.(InteractiveComponent) - } - } - - return nil -} - -func (c ActionRowComponent) Type() ComponentType { - return ComponentTypeActionRow -} - -func (c ActionRowComponent) component() {} -func (c ActionRowComponent) containerComponent() {} - -func (c ActionRowComponent) Components() []InteractiveComponent { - return c -} - -// Buttons returns all ButtonComponent(s) in the ActionRowComponent -func (c ActionRowComponent) Buttons() []ButtonComponent { - var buttons []ButtonComponent - for i := range c { - if button, ok := c[i].(ButtonComponent); ok { - buttons = append(buttons, button) - } - } - return buttons -} - -// SelectMenus returns all SelectMenuComponent(s) in the ActionRowComponent -func (c ActionRowComponent) SelectMenus() []SelectMenuComponent { - var selectMenus []SelectMenuComponent - for i := range c { - if selectMenu, ok := c[i].(SelectMenuComponent); ok { - selectMenus = append(selectMenus, selectMenu) - } - } - return selectMenus -} - -// UpdateComponent returns a new ActionRowComponent with the Component which has the customID replaced -func (c ActionRowComponent) UpdateComponent(customID CustomID, component InteractiveComponent) ActionRowComponent { - for i, cc := range c { - if cc.ID() == customID { - c[i] = component - return c - } - } - return c -} - -// AddComponents returns a new ActionRowComponent with the provided Component(s) added -func (c ActionRowComponent) AddComponents(components ...InteractiveComponent) ActionRowComponent { - return append(c, components...) -} - -// RemoveComponent returns a new ActionRowComponent with the provided Component at the index removed -func (c ActionRowComponent) RemoveComponent(index int) ActionRowComponent { - if len(c) > index { - return append(c[:index], c[index+1:]...) - } - return c -} diff --git a/discord/application_command_autocomplete_result.go b/discord/application_command_autocomplete_result.go deleted file mode 100644 index 128aa029..00000000 --- a/discord/application_command_autocomplete_result.go +++ /dev/null @@ -1,32 +0,0 @@ -package discord - -type AutocompleteResult struct { - Choices []AutocompleteChoice `json:"choices"` -} - -func (AutocompleteResult) interactionCallbackData() {} - -type AutocompleteChoice interface { - autoCompleteChoice() -} - -type AutocompleteChoiceString struct { - Name string `json:"name"` - Value string `json:"value"` -} - -func (AutocompleteChoiceString) autoCompleteChoice() {} - -type AutocompleteChoiceInt struct { - Name string `json:"name"` - Value int `json:"value"` -} - -func (AutocompleteChoiceInt) autoCompleteChoice() {} - -type AutocompleteChoiceFloat struct { - Name string `json:"name"` - Value float64 `json:"value"` -} - -func (AutocompleteChoiceFloat) autoCompleteChoice() {} diff --git a/discord/button.go b/discord/button.go deleted file mode 100644 index bab51f16..00000000 --- a/discord/button.go +++ /dev/null @@ -1,140 +0,0 @@ -package discord - -import "github.com/DisgoOrg/disgo/json" - -// ButtonStyle defines how the ButtonComponent looks like (https://discord.com/assets/7bb017ce52cfd6575e21c058feb3883b.png) -type ButtonStyle int - -// Supported ButtonStyle(s) -const ( - ButtonStylePrimary = iota + 1 - ButtonStyleSecondary - ButtonStyleSuccess - ButtonStyleDanger - ButtonStyleLink -) - -// NewButton creates a new ButtonComponent with the provided parameters. Link ButtonComponent(s) need a URL and other ButtonComponent(s) need a customID -//goland:noinspection GoUnusedExportedFunction -func NewButton(style ButtonStyle, label string, customID CustomID, url string) ButtonComponent { - return ButtonComponent{ - Style: style, - CustomID: customID, - URL: url, - Label: label, - } -} - -// NewPrimaryButton creates a new ButtonComponent with ButtonStylePrimary & the provided parameters -//goland:noinspection GoUnusedExportedFunction -func NewPrimaryButton(label string, customID CustomID) ButtonComponent { - return NewButton(ButtonStylePrimary, label, customID, "") -} - -// NewSecondaryButton creates a new ButtonComponent with ButtonStyleSecondary & the provided parameters -//goland:noinspection GoUnusedExportedFunction -func NewSecondaryButton(label string, customID CustomID) ButtonComponent { - return NewButton(ButtonStyleSecondary, label, customID, "") -} - -// NewSuccessButton creates a new ButtonComponent with ButtonStyleSuccess & the provided parameters -//goland:noinspection GoUnusedExportedFunction -func NewSuccessButton(label string, customID CustomID) ButtonComponent { - return NewButton(ButtonStyleSuccess, label, customID, "") -} - -// NewDangerButton creates a new ButtonComponent with ButtonStyleDanger & the provided parameters -//goland:noinspection GoUnusedExportedFunction -func NewDangerButton(label string, customID CustomID) ButtonComponent { - return NewButton(ButtonStyleDanger, label, customID, "") -} - -// NewLinkButton creates a new link ButtonComponent with ButtonStyleLink & the provided parameters -//goland:noinspection GoUnusedExportedFunction -func NewLinkButton(label string, url string) ButtonComponent { - return NewButton(ButtonStyleLink, label, "", url) -} - -var ( - _ Component = (*ButtonComponent)(nil) - _ InteractiveComponent = (*ButtonComponent)(nil) -) - -type ButtonComponent struct { - Style ButtonStyle `json:"style"` - Label string `json:"label,omitempty"` - Emoji *ComponentEmoji `json:"emoji,omitempty"` - CustomID CustomID `json:"custom_id,omitempty"` - URL string `json:"url,omitempty"` - Disabled bool `json:"disabled,omitempty"` -} - -func (c ButtonComponent) MarshalJSON() ([]byte, error) { - type buttonComponent ButtonComponent - return json.Marshal(struct { - Type ComponentType `json:"type"` - buttonComponent - }{ - Type: c.Type(), - buttonComponent: buttonComponent(c), - }) -} - -func (c ButtonComponent) Type() ComponentType { - return ComponentTypeButton -} - -func (c ButtonComponent) ID() CustomID { - return c.CustomID -} - -func (c ButtonComponent) component() {} -func (c ButtonComponent) interactiveComponent() {} - -// WithStyle returns a new ButtonComponent with the provided style -func (c ButtonComponent) WithStyle(style ButtonStyle) ButtonComponent { - c.Style = style - return c -} - -// WithLabel returns a new ButtonComponent with the provided label -func (c ButtonComponent) WithLabel(label string) ButtonComponent { - c.Label = label - return c -} - -// WithEmoji returns a new ButtonComponent with the provided Emoji -func (c ButtonComponent) WithEmoji(emoji ComponentEmoji) ButtonComponent { - c.Emoji = &emoji - return c -} - -// WithCustomID returns a new ButtonComponent with the provided custom id -func (c ButtonComponent) WithCustomID(customID CustomID) ButtonComponent { - c.CustomID = customID - return c -} - -// WithURL returns a new ButtonComponent with the provided URL -func (c ButtonComponent) WithURL(url string) ButtonComponent { - c.URL = url - return c -} - -// AsEnabled returns a new ButtonComponent but enabled -func (c ButtonComponent) AsEnabled() ButtonComponent { - c.Disabled = false - return c -} - -// AsDisabled returns a new ButtonComponent but disabled -func (c ButtonComponent) AsDisabled() ButtonComponent { - c.Disabled = true - return c -} - -// WithDisabled returns a new ButtonComponent but disabled/enabled -func (c ButtonComponent) WithDisabled(disabled bool) ButtonComponent { - c.Disabled = disabled - return c -} diff --git a/discord/component.go b/discord/component.go index a0c15bbf..09ce7544 100644 --- a/discord/component.go +++ b/discord/component.go @@ -15,6 +15,7 @@ const ( ComponentTypeActionRow = iota + 1 ComponentTypeButton ComponentTypeSelectMenu + ComponentTypeInputText ) type CustomID string @@ -91,3 +92,449 @@ type ComponentEmoji struct { Name string `json:"name,omitempty"` Animated bool `json:"animated,omitempty"` } + +var ( + _ Component = (*ActionRowComponent)(nil) + _ ContainerComponent = (*ActionRowComponent)(nil) +) + +//goland:noinspection GoUnusedExportedFunction +func NewActionRow(components ...InteractiveComponent) ActionRowComponent { + return components +} + +type ActionRowComponent []InteractiveComponent + +func (c ActionRowComponent) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type ComponentType `json:"type"` + Components []InteractiveComponent `json:"components"` + }{ + Type: c.Type(), + Components: c, + }) +} + +func (c *ActionRowComponent) UnmarshalJSON(data []byte) error { + var actionRow struct { + Components []UnmarshalComponent `json:"components"` + } + + if err := json.Unmarshal(data, &actionRow); err != nil { + return err + } + + if len(actionRow.Components) > 0 { + *c = make([]InteractiveComponent, len(actionRow.Components)) + for i, component := range actionRow.Components { + (*c)[i] = component.Component.(InteractiveComponent) + } + } + + return nil +} + +func (c ActionRowComponent) Type() ComponentType { + return ComponentTypeActionRow +} + +func (c ActionRowComponent) component() {} +func (c ActionRowComponent) containerComponent() {} + +func (c ActionRowComponent) Components() []InteractiveComponent { + return c +} + +// Buttons returns all ButtonComponent(s) in the ActionRowComponent +func (c ActionRowComponent) Buttons() []ButtonComponent { + var buttons []ButtonComponent + for i := range c { + if button, ok := c[i].(ButtonComponent); ok { + buttons = append(buttons, button) + } + } + return buttons +} + +// SelectMenus returns all SelectMenuComponent(s) in the ActionRowComponent +func (c ActionRowComponent) SelectMenus() []SelectMenuComponent { + var selectMenus []SelectMenuComponent + for i := range c { + if selectMenu, ok := c[i].(SelectMenuComponent); ok { + selectMenus = append(selectMenus, selectMenu) + } + } + return selectMenus +} + +// UpdateComponent returns a new ActionRowComponent with the Component which has the customID replaced +func (c ActionRowComponent) UpdateComponent(customID CustomID, component InteractiveComponent) ActionRowComponent { + for i, cc := range c { + if cc.ID() == customID { + c[i] = component + return c + } + } + return c +} + +// AddComponents returns a new ActionRowComponent with the provided Component(s) added +func (c ActionRowComponent) AddComponents(components ...InteractiveComponent) ActionRowComponent { + return append(c, components...) +} + +// RemoveComponent returns a new ActionRowComponent with the provided Component at the index removed +func (c ActionRowComponent) RemoveComponent(index int) ActionRowComponent { + if len(c) > index { + return append(c[:index], c[index+1:]...) + } + return c +} + +// ButtonStyle defines how the ButtonComponent looks like (https://discord.com/assets/7bb017ce52cfd6575e21c058feb3883b.png) +type ButtonStyle int + +// Supported ButtonStyle(s) +const ( + ButtonStylePrimary = iota + 1 + ButtonStyleSecondary + ButtonStyleSuccess + ButtonStyleDanger + ButtonStyleLink +) + +// NewButton creates a new ButtonComponent with the provided parameters. Link ButtonComponent(s) need a URL and other ButtonComponent(s) need a customID +//goland:noinspection GoUnusedExportedFunction +func NewButton(style ButtonStyle, label string, customID CustomID, url string) ButtonComponent { + return ButtonComponent{ + Style: style, + CustomID: customID, + URL: url, + Label: label, + } +} + +// NewPrimaryButton creates a new ButtonComponent with ButtonStylePrimary & the provided parameters +//goland:noinspection GoUnusedExportedFunction +func NewPrimaryButton(label string, customID CustomID) ButtonComponent { + return NewButton(ButtonStylePrimary, label, customID, "") +} + +// NewSecondaryButton creates a new ButtonComponent with ButtonStyleSecondary & the provided parameters +//goland:noinspection GoUnusedExportedFunction +func NewSecondaryButton(label string, customID CustomID) ButtonComponent { + return NewButton(ButtonStyleSecondary, label, customID, "") +} + +// NewSuccessButton creates a new ButtonComponent with ButtonStyleSuccess & the provided parameters +//goland:noinspection GoUnusedExportedFunction +func NewSuccessButton(label string, customID CustomID) ButtonComponent { + return NewButton(ButtonStyleSuccess, label, customID, "") +} + +// NewDangerButton creates a new ButtonComponent with ButtonStyleDanger & the provided parameters +//goland:noinspection GoUnusedExportedFunction +func NewDangerButton(label string, customID CustomID) ButtonComponent { + return NewButton(ButtonStyleDanger, label, customID, "") +} + +// NewLinkButton creates a new link ButtonComponent with ButtonStyleLink & the provided parameters +//goland:noinspection GoUnusedExportedFunction +func NewLinkButton(label string, url string) ButtonComponent { + return NewButton(ButtonStyleLink, label, "", url) +} + +var ( + _ Component = (*ButtonComponent)(nil) + _ InteractiveComponent = (*ButtonComponent)(nil) +) + +type ButtonComponent struct { + Style ButtonStyle `json:"style"` + Label string `json:"label,omitempty"` + Emoji *ComponentEmoji `json:"emoji,omitempty"` + CustomID CustomID `json:"custom_id,omitempty"` + URL string `json:"url,omitempty"` + Disabled bool `json:"disabled,omitempty"` +} + +func (c ButtonComponent) MarshalJSON() ([]byte, error) { + type buttonComponent ButtonComponent + return json.Marshal(struct { + Type ComponentType `json:"type"` + buttonComponent + }{ + Type: c.Type(), + buttonComponent: buttonComponent(c), + }) +} + +func (c ButtonComponent) Type() ComponentType { + return ComponentTypeButton +} + +func (c ButtonComponent) ID() CustomID { + return c.CustomID +} + +func (c ButtonComponent) component() {} +func (c ButtonComponent) interactiveComponent() {} + +// WithStyle returns a new ButtonComponent with the provided style +func (c ButtonComponent) WithStyle(style ButtonStyle) ButtonComponent { + c.Style = style + return c +} + +// WithLabel returns a new ButtonComponent with the provided label +func (c ButtonComponent) WithLabel(label string) ButtonComponent { + c.Label = label + return c +} + +// WithEmoji returns a new ButtonComponent with the provided Emoji +func (c ButtonComponent) WithEmoji(emoji ComponentEmoji) ButtonComponent { + c.Emoji = &emoji + return c +} + +// WithCustomID returns a new ButtonComponent with the provided custom id +func (c ButtonComponent) WithCustomID(customID CustomID) ButtonComponent { + c.CustomID = customID + return c +} + +// WithURL returns a new ButtonComponent with the provided URL +func (c ButtonComponent) WithURL(url string) ButtonComponent { + c.URL = url + return c +} + +// AsEnabled returns a new ButtonComponent but enabled +func (c ButtonComponent) AsEnabled() ButtonComponent { + c.Disabled = false + return c +} + +// AsDisabled returns a new ButtonComponent but disabled +func (c ButtonComponent) AsDisabled() ButtonComponent { + c.Disabled = true + return c +} + +// WithDisabled returns a new ButtonComponent but disabled/enabled +func (c ButtonComponent) WithDisabled(disabled bool) ButtonComponent { + c.Disabled = disabled + return c +} + +// NewSelectMenu builds a new SelectMenuComponent from the provided values +//goland:noinspection GoUnusedExportedFunction +func NewSelectMenu(customID CustomID, placeholder string, options ...SelectMenuOption) SelectMenuComponent { + return SelectMenuComponent{ + CustomID: customID, + Placeholder: placeholder, + Options: options, + } +} + +var ( + _ Component = (*SelectMenuComponent)(nil) + _ InteractiveComponent = (*SelectMenuComponent)(nil) +) + +type SelectMenuComponent struct { + CustomID CustomID `json:"custom_id"` + Placeholder string `json:"placeholder,omitempty"` + MinValues json.NullInt `json:"min_values,omitempty"` + MaxValues json.NullInt `json:"max_values,omitempty"` + Disabled bool `json:"disabled,omitempty"` + Options []SelectMenuOption `json:"options,omitempty"` +} + +func (c SelectMenuComponent) MarshalJSON() ([]byte, error) { + type selectMenuComponent SelectMenuComponent + return json.Marshal(struct { + Type ComponentType `json:"type"` + selectMenuComponent + }{ + Type: c.Type(), + selectMenuComponent: selectMenuComponent(c), + }) +} + +func (c SelectMenuComponent) Type() ComponentType { + return ComponentTypeSelectMenu +} + +func (c SelectMenuComponent) ID() CustomID { + return c.CustomID +} + +func (c SelectMenuComponent) component() {} +func (c SelectMenuComponent) interactiveComponent() {} + +// WithCustomID returns a new SelectMenuComponent with the provided customID +func (c SelectMenuComponent) WithCustomID(customID CustomID) SelectMenuComponent { + c.CustomID = customID + return c +} + +// WithPlaceholder returns a new SelectMenuComponent with the provided placeholder +func (c SelectMenuComponent) WithPlaceholder(placeholder string) SelectMenuComponent { + c.Placeholder = placeholder + return c +} + +// WithMinValues returns a new SelectMenuComponent with the provided minValue +func (c SelectMenuComponent) WithMinValues(minValue int) SelectMenuComponent { + c.MinValues = *json.NewInt(minValue) + return c +} + +// WithMaxValues returns a new SelectMenuComponent with the provided maxValue +func (c SelectMenuComponent) WithMaxValues(maxValue int) SelectMenuComponent { + c.MaxValues = *json.NewInt(maxValue) + return c +} + +// AsEnabled returns a new SelectMenuComponent but enabled +func (c SelectMenuComponent) AsEnabled() SelectMenuComponent { + c.Disabled = false + return c +} + +// AsDisabled returns a new SelectMenuComponent but disabled +func (c SelectMenuComponent) AsDisabled() SelectMenuComponent { + c.Disabled = true + return c +} + +// WithDisabled returns a new SelectMenuComponent with the provided disabled +func (c SelectMenuComponent) WithDisabled(disabled bool) SelectMenuComponent { + c.Disabled = disabled + return c +} + +// SetOptions returns a new SelectMenuComponent with the provided SelectMenuOption(s) +func (c SelectMenuComponent) SetOptions(options ...SelectMenuOption) SelectMenuComponent { + c.Options = options + return c +} + +// SetOption returns a new SelectMenuComponent with the SelectMenuOption which has the value replaced +func (c SelectMenuComponent) SetOption(value string, option SelectMenuOption) SelectMenuComponent { + for i, o := range c.Options { + if o.Value == value { + c.Options[i] = option + break + } + } + return c +} + +// AddOptions returns a new SelectMenuComponent with the provided SelectMenuOption(s) added +func (c SelectMenuComponent) AddOptions(options ...SelectMenuOption) SelectMenuComponent { + c.Options = append(c.Options, options...) + return c +} + +// RemoveOption returns a new SelectMenuComponent with the provided SelectMenuOption at the index removed +func (c SelectMenuComponent) RemoveOption(index int) SelectMenuComponent { + if len(c.Options) > index { + c.Options = append(c.Options[:index], c.Options[index+1:]...) + } + return c +} + +// NewSelectMenuOption builds a new SelectMenuOption +//goland:noinspection GoUnusedExportedFunction +func NewSelectMenuOption(label string, value string) SelectMenuOption { + return SelectMenuOption{ + Label: label, + Value: value, + } +} + +// SelectMenuOption represents an option in a SelectMenuComponent +type SelectMenuOption struct { + Label string `json:"label"` + Value string `json:"value"` + Description string `json:"description,omitempty"` + Emoji *ComponentEmoji `json:"emoji,omitempty"` + Default bool `json:"default,omitempty"` +} + +// WithLabel returns a new SelectMenuOption with the provided label +func (o SelectMenuOption) WithLabel(label string) SelectMenuOption { + o.Label = label + return o +} + +// WithValue returns a new SelectMenuOption with the provided value +func (o SelectMenuOption) WithValue(value string) SelectMenuOption { + o.Value = value + return o +} + +// WithDescription returns a new SelectMenuOption with the provided description +func (o SelectMenuOption) WithDescription(description string) SelectMenuOption { + o.Description = description + return o +} + +// WithEmoji returns a new SelectMenuOption with the provided Emoji +func (o SelectMenuOption) WithEmoji(emoji ComponentEmoji) SelectMenuOption { + o.Emoji = &emoji + return o +} + +// WithDefault returns a new SelectMenuOption as default/non-default +func (o SelectMenuOption) WithDefault(defaultOption bool) SelectMenuOption { + o.Default = defaultOption + return o +} + +var ( + _ Component = (*InputText)(nil) + _ InteractiveComponent = (*InputText)(nil) +) + +type InputText struct { + TextStyle TextStyle `json:"text_style"` + CustomID CustomID `json:"custom_id"` + Label string `json:"label"` + Placeholder string `json:"placeholder,omitempty"` + MinLength json.NullInt `json:"min_length,omitempty"` + MaxLength json.NullInt `json:"max_length,omitempty"` +} + +func (t InputText) MarshalJSON() ([]byte, error) { + type inputText InputText + return json.Marshal(struct { + Type ComponentType `json:"type"` + inputText + }{ + Type: t.Type(), + inputText: inputText(t), + }) +} + +func (t InputText) Type() ComponentType { + return ComponentTypeInputText +} + +func (t InputText) ID() CustomID { + return t.CustomID +} + +func (t InputText) component() {} +func (t InputText) interactiveComponent() {} + +type TextStyle int + +//goland:noinspection GoUnusedConst +const ( + TextStyleShort = iota + 1 + TextStyleParagraph +) diff --git a/discord/interaction.go b/discord/interaction.go index 91913257..6f37aae8 100644 --- a/discord/interaction.go +++ b/discord/interaction.go @@ -15,6 +15,7 @@ const ( InteractionTypeApplicationCommand InteractionTypeComponent InteractionTypeAutocomplete + InteractionTypeModalSubmit ) // Interaction is used for easier unmarshalling of different Interaction(s) @@ -381,6 +382,7 @@ func (d *AutocompleteInteractionData) UnmarshalJSON(data []byte) error { return err } + *d = AutocompleteInteractionData(iData.autocompleteInteractionData) if len(iData.Options) > 0 { d.Options = make([]AutocompleteOption, len(iData.Options)) for i, option := range iData.Options { @@ -388,7 +390,55 @@ func (d *AutocompleteInteractionData) UnmarshalJSON(data []byte) error { } } - *d = AutocompleteInteractionData(iData.autocompleteInteractionData) + return nil +} + +var ( + _ Interaction = (*ModalSubmitInteraction)(nil) +) + +type ModalSubmitInteraction struct { + ID Snowflake `json:"id"` + ApplicationID Snowflake `json:"application_id"` + Token string `json:"token"` + Version int `json:"version"` + GuildID *Snowflake `json:"guild_id,omitempty"` + ChannelID Snowflake `json:"channel_id"` + Member *Member `json:"member,omitempty"` + User *User `json:"user,omitempty"` + Data ModalSubmitInteractionData `json:"data"` +} + +func (ModalSubmitInteraction) interaction() {} + +func (ModalSubmitInteraction) InteractionType() InteractionType { + return InteractionTypeModalSubmit +} + +type ModalSubmitInteractionData struct { + CustomID CustomID `json:"custom_id"` + Components []ModalSubmitContainerComponent `json:"components"` +} + +func (d *ModalSubmitInteractionData) Unmarshal(data []byte) error { + type modalSubmitInteractionData ModalSubmitInteractionData + var iData struct { + modalSubmitInteractionData + Components []UnmarshalModalSubmitComponent `json:"components"` + } + + if err := json.Unmarshal(data, &iData); err != nil { + return err + } + + *d = ModalSubmitInteractionData(iData.modalSubmitInteractionData) + + if len(iData.Components) > 0 { + d.Components = make([]ModalSubmitContainerComponent, len(iData.Components)) + for i := range iData.Components { + d.Components[i] = iData.Components[i].ModalSubmitComponent.(ModalSubmitContainerComponent) + } + } return nil } diff --git a/discord/interaction_callback.go b/discord/interaction_callback.go index 028495a6..99867d3e 100644 --- a/discord/interaction_callback.go +++ b/discord/interaction_callback.go @@ -13,6 +13,7 @@ const ( InteractionCallbackTypeDeferredUpdateMessage InteractionCallbackTypeUpdateMessage InteractionCallbackTypeAutocompleteResult + InteractionCallbackTypeModal ) // InteractionResponse is how you answer interactions. If an answer is not sent within 3 seconds of receiving it, the interaction is failed, and you will be unable to respond to it. @@ -36,3 +37,44 @@ type InteractionCallbackData interface { type InteractionResponseCreator interface { ToResponseBody(response InteractionResponse) (interface{}, error) } + +type AutocompleteResult struct { + Choices []AutocompleteChoice `json:"choices"` +} + +func (AutocompleteResult) interactionCallbackData() {} + +type AutocompleteChoice interface { + autoCompleteChoice() +} + +type AutocompleteChoiceString struct { + Name string `json:"name"` + Value string `json:"value"` +} + +func (AutocompleteChoiceString) autoCompleteChoice() {} + +type AutocompleteChoiceInt struct { + Name string `json:"name"` + Value int `json:"value"` +} + +func (AutocompleteChoiceInt) autoCompleteChoice() {} + +type AutocompleteChoiceFloat struct { + Name string `json:"name"` + Value float64 `json:"value"` +} + +func (AutocompleteChoiceFloat) autoCompleteChoice() {} + +var _ InteractionCallbackData = (*Modal)(nil) + +type Modal struct { + CustomID CustomID `json:"custom_id"` + Title string `json:"title"` + Components []ContainerComponent `json:"components"` +} + +func (m Modal) interactionCallbackData() {} diff --git a/discord/modal_submit.go b/discord/modal_submit.go new file mode 100644 index 00000000..e5689450 --- /dev/null +++ b/discord/modal_submit.go @@ -0,0 +1,126 @@ +package discord + +import ( + "fmt" + + "github.com/DisgoOrg/disgo/json" +) + +type ModalSubmitComponent interface { + Type() ComponentType + modalSubmitComponent() +} + +type ModalSubmitContainerComponent interface { + ModalSubmitComponent + Components() []ModalSubmitInteractiveComponent + modalSubmitContainerComponent() +} + +type ModalSubmitInteractiveComponent interface { + ModalSubmitComponent + ID() CustomID + Value() string + modalSubmitInteractiveComponent() +} + +type UnmarshalModalSubmitComponent struct { + ModalSubmitComponent +} + +func (u *UnmarshalModalSubmitComponent) UnmarshalJSON(data []byte) error { + var cType struct { + Type ComponentType `json:"type"` + } + + if err := json.Unmarshal(data, &cType); err != nil { + return err + } + + var ( + modalSubmitComponent ModalSubmitComponent + err error + ) + + switch cType.Type { + case ComponentTypeActionRow: + v := ModalSubmitActionRowComponent{} + err = json.Unmarshal(data, &v) + modalSubmitComponent = v + + case ComponentTypeInputText: + v := ModalSubmitInputTextComponent{} + err = json.Unmarshal(data, &v) + modalSubmitComponent = v + + default: + err = fmt.Errorf("unkown component with type %d received", cType.Type) + } + if err != nil { + return err + } + + u.ModalSubmitComponent = modalSubmitComponent + return nil +} + +var ( + _ ModalSubmitComponent = (*ModalSubmitActionRowComponent)(nil) + _ ModalSubmitContainerComponent = (*ModalSubmitActionRowComponent)(nil) +) + +type ModalSubmitActionRowComponent []ModalSubmitInteractiveComponent + +func (c *ModalSubmitActionRowComponent) UnmarshalJSON(data []byte) error { + var actionRow struct { + Components []UnmarshalModalSubmitComponent `json:"components"` + } + + if err := json.Unmarshal(data, &actionRow); err != nil { + return err + } + + if len(actionRow.Components) > 0 { + *c = make([]ModalSubmitInteractiveComponent, len(actionRow.Components)) + for i := range actionRow.Components { + (*c)[i] = actionRow.Components[i].ModalSubmitComponent.(ModalSubmitInteractiveComponent) + } + } + + return nil +} + +func (ModalSubmitActionRowComponent) Type() ComponentType { + return ComponentTypeInputText +} + +func (c ModalSubmitActionRowComponent) Components() []ModalSubmitInteractiveComponent { + return c +} + +func (ModalSubmitActionRowComponent) modalSubmitComponent() {} +func (ModalSubmitActionRowComponent) modalSubmitContainerComponent() {} + +var ( + _ ModalSubmitComponent = (*ModalSubmitInputTextComponent)(nil) + _ ModalSubmitInteractiveComponent = (*ModalSubmitInputTextComponent)(nil) +) + +type ModalSubmitInputTextComponent struct { + CustomID CustomID `json:"custom_id"` + TextValue string `json:"value"` +} + +func (ModalSubmitInputTextComponent) Type() ComponentType { + return ComponentTypeInputText +} + +func (c ModalSubmitInputTextComponent) ID() CustomID { + return c.CustomID +} +func (c ModalSubmitInputTextComponent) Value() string { + return c.TextValue +} + +func (ModalSubmitInputTextComponent) modalSubmitComponent() {} +func (ModalSubmitInputTextComponent) modalSubmitInteractiveComponent() {} diff --git a/discord/select_menu.go b/discord/select_menu.go deleted file mode 100644 index 189fc180..00000000 --- a/discord/select_menu.go +++ /dev/null @@ -1,170 +0,0 @@ -package discord - -import "github.com/DisgoOrg/disgo/json" - -// NewSelectMenu builds a new SelectMenuComponent from the provided values -//goland:noinspection GoUnusedExportedFunction -func NewSelectMenu(customID CustomID, placeholder string, options ...SelectMenuOption) SelectMenuComponent { - return SelectMenuComponent{ - CustomID: customID, - Placeholder: placeholder, - Options: options, - } -} - -var ( - _ Component = (*SelectMenuComponent)(nil) - _ InteractiveComponent = (*SelectMenuComponent)(nil) -) - -type SelectMenuComponent struct { - CustomID CustomID `json:"custom_id"` - Placeholder string `json:"placeholder,omitempty"` - MinValues json.NullInt `json:"min_values,omitempty"` - MaxValues json.NullInt `json:"max_values,omitempty"` - Disabled bool `json:"disabled,omitempty"` - Options []SelectMenuOption `json:"options,omitempty"` -} - -func (c SelectMenuComponent) MarshalJSON() ([]byte, error) { - type selectMenuComponent SelectMenuComponent - return json.Marshal(struct { - Type ComponentType `json:"type"` - selectMenuComponent - }{ - Type: c.Type(), - selectMenuComponent: selectMenuComponent(c), - }) -} - -func (c SelectMenuComponent) Type() ComponentType { - return ComponentTypeSelectMenu -} - -func (c SelectMenuComponent) ID() CustomID { - return c.CustomID -} - -func (c SelectMenuComponent) component() {} -func (c SelectMenuComponent) interactiveComponent() {} - -// WithCustomID returns a new SelectMenuComponent with the provided customID -func (c SelectMenuComponent) WithCustomID(customID CustomID) SelectMenuComponent { - c.CustomID = customID - return c -} - -// WithPlaceholder returns a new SelectMenuComponent with the provided placeholder -func (c SelectMenuComponent) WithPlaceholder(placeholder string) SelectMenuComponent { - c.Placeholder = placeholder - return c -} - -// WithMinValues returns a new SelectMenuComponent with the provided minValue -func (c SelectMenuComponent) WithMinValues(minValue int) SelectMenuComponent { - c.MinValues = *json.NewInt(minValue) - return c -} - -// WithMaxValues returns a new SelectMenuComponent with the provided maxValue -func (c SelectMenuComponent) WithMaxValues(maxValue int) SelectMenuComponent { - c.MaxValues = *json.NewInt(maxValue) - return c -} - -// AsEnabled returns a new SelectMenuComponent but enabled -func (c SelectMenuComponent) AsEnabled() SelectMenuComponent { - c.Disabled = false - return c -} - -// AsDisabled returns a new SelectMenuComponent but disabled -func (c SelectMenuComponent) AsDisabled() SelectMenuComponent { - c.Disabled = true - return c -} - -// WithDisabled returns a new SelectMenuComponent with the provided disabled -func (c SelectMenuComponent) WithDisabled(disabled bool) SelectMenuComponent { - c.Disabled = disabled - return c -} - -// SetOptions returns a new SelectMenuComponent with the provided SelectMenuOption(s) -func (c SelectMenuComponent) SetOptions(options ...SelectMenuOption) SelectMenuComponent { - c.Options = options - return c -} - -// SetOption returns a new SelectMenuComponent with the SelectMenuOption which has the value replaced -func (c SelectMenuComponent) SetOption(value string, option SelectMenuOption) SelectMenuComponent { - for i, o := range c.Options { - if o.Value == value { - c.Options[i] = option - break - } - } - return c -} - -// AddOptions returns a new SelectMenuComponent with the provided SelectMenuOption(s) added -func (c SelectMenuComponent) AddOptions(options ...SelectMenuOption) SelectMenuComponent { - c.Options = append(c.Options, options...) - return c -} - -// RemoveOption returns a new SelectMenuComponent with the provided SelectMenuOption at the index removed -func (c SelectMenuComponent) RemoveOption(index int) SelectMenuComponent { - if len(c.Options) > index { - c.Options = append(c.Options[:index], c.Options[index+1:]...) - } - return c -} - -// NewSelectMenuOption builds a new SelectMenuOption -//goland:noinspection GoUnusedExportedFunction -func NewSelectMenuOption(label string, value string) SelectMenuOption { - return SelectMenuOption{ - Label: label, - Value: value, - } -} - -// SelectMenuOption represents an option in a SelectMenuComponent -type SelectMenuOption struct { - Label string `json:"label"` - Value string `json:"value"` - Description string `json:"description,omitempty"` - Emoji *ComponentEmoji `json:"emoji,omitempty"` - Default bool `json:"default,omitempty"` -} - -// WithLabel returns a new SelectMenuOption with the provided label -func (o SelectMenuOption) WithLabel(label string) SelectMenuOption { - o.Label = label - return o -} - -// WithValue returns a new SelectMenuOption with the provided value -func (o SelectMenuOption) WithValue(value string) SelectMenuOption { - o.Value = value - return o -} - -// WithDescription returns a new SelectMenuOption with the provided description -func (o SelectMenuOption) WithDescription(description string) SelectMenuOption { - o.Description = description - return o -} - -// WithEmoji returns a new SelectMenuOption with the provided Emoji -func (o SelectMenuOption) WithEmoji(emoji ComponentEmoji) SelectMenuOption { - o.Emoji = &emoji - return o -} - -// WithDefault returns a new SelectMenuOption as default/non-default -func (o SelectMenuOption) WithDefault(defaultOption bool) SelectMenuOption { - o.Default = defaultOption - return o -} From 78d236b8b4b70410ddf449e78c4f74a6b25a072c Mon Sep 17 00:00:00 2001 From: TopiSenpai Date: Tue, 23 Nov 2021 02:21:03 +0100 Subject: [PATCH 2/5] add modal event --- events/events_interactions.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/events/events_interactions.go b/events/events_interactions.go index 26e4d8b9..1f4a0c91 100644 --- a/events/events_interactions.go +++ b/events/events_interactions.go @@ -49,3 +49,8 @@ type AutocompleteEvent struct { *GenericEvent *core.AutocompleteInteraction } + +type ModalSubmitEvent struct { + *GenericEvent + *core.ModalSubmitInteraction +} \ No newline at end of file From 321a4637380b0e417c6ed9e1671d74af6ba901e6 Mon Sep 17 00:00:00 2001 From: TopiSenpai Date: Mon, 24 Jan 2022 18:29:04 +0100 Subject: [PATCH 3/5] update modals stuff to interaction rework --- .../application_commands/gateway/example.go | 2 +- .../application_commands/http/example.go | 2 +- _examples/components/example.go | 2 +- _examples/test/listeners.go | 34 ++--- core/application_command_interaction.go | 14 +- core/component_interaction.go | 16 +-- core/entity_builder.go | 26 ++-- core/interaction.go | 24 +++- core/modal_submit_interaction.go | 60 +-------- discord/errors.go | 1 + discord/interaction.go | 28 ++-- discord/interaction_callback.go | 12 +- discord/modal_components.go | 126 ++++++++++++++++++ discord/modal_submit.go | 126 ------------------ 14 files changed, 222 insertions(+), 251 deletions(-) create mode 100644 discord/modal_components.go delete mode 100644 discord/modal_submit.go diff --git a/_examples/application_commands/gateway/example.go b/_examples/application_commands/gateway/example.go index 95328f8e..4ef13460 100644 --- a/_examples/application_commands/gateway/example.go +++ b/_examples/application_commands/gateway/example.go @@ -73,7 +73,7 @@ func main() { func commandListener(event *events.ApplicationCommandInteractionEvent) { data := event.SlashCommandInteractionData() if data.CommandName == "say" { - err := event.Create(discord.NewMessageCreateBuilder(). + err := event.CreateMessage(discord.NewMessageCreateBuilder(). SetContent(*data.Options.String("message")). Build(), ) diff --git a/_examples/application_commands/http/example.go b/_examples/application_commands/http/example.go index 59217036..b928a5fa 100644 --- a/_examples/application_commands/http/example.go +++ b/_examples/application_commands/http/example.go @@ -76,7 +76,7 @@ func main() { func commandListener(event *events.ApplicationCommandInteractionEvent) { data := event.SlashCommandInteractionData() if data.CommandName == "say" { - err := event.Create(discord.NewMessageCreateBuilder(). + err := event.CreateMessage(discord.NewMessageCreateBuilder(). SetContent(*data.Options.String("message")). Build(), ) diff --git a/_examples/components/example.go b/_examples/components/example.go index 84087597..e0350bea 100644 --- a/_examples/components/example.go +++ b/_examples/components/example.go @@ -40,7 +40,7 @@ func main() { }, OnComponentInteraction: func(event *events.ComponentInteractionEvent) { if event.ButtonInteractionData().CustomID == "danger" { - _ = event.Create(discord.NewMessageCreateBuilder().SetEphemeral(true).SetContent("Ey that was danger").Build()) + _ = event.CreateMessage(discord.NewMessageCreateBuilder().SetEphemeral(true).SetContent("Ey that was danger").Build()) } }, }), diff --git a/_examples/test/listeners.go b/_examples/test/listeners.go index 1c90a8e1..180cc4c9 100644 --- a/_examples/test/listeners.go +++ b/_examples/test/listeners.go @@ -25,19 +25,19 @@ func componentListener(event *events.ComponentInteractionEvent) { case *core.ButtonInteractionData: switch data.CustomID { case "test1": - _ = event.Create(discord.NewMessageCreateBuilder(). + _ = event.CreateMessage(discord.NewMessageCreateBuilder(). SetContent(data.CustomID.String()). Build(), ) case "test2": - _ = event.DeferCreate(false) + _ = event.DeferCreateMessage(false) case "test3": - _ = event.DeferUpdate() + _ = event.DeferUpdateMessage() case "test4": - _ = event.Update(discord.NewMessageUpdateBuilder(). + _ = event.UpdateMessage(discord.NewMessageUpdateBuilder(). SetContent(data.CustomID.String()). Build(), ) @@ -46,7 +46,7 @@ func componentListener(event *events.ComponentInteractionEvent) { case *core.SelectMenuInteractionData: switch data.CustomID { case "test3": - if err := event.DeferUpdate(); err != nil { + if err := event.DeferUpdateMessage(); err != nil { log.Errorf("error sending interaction response: %s", err) } _, _ = event.CreateFollowupMessage(discord.NewMessageCreateBuilder(). @@ -62,7 +62,7 @@ func applicationCommandListener(event *events.ApplicationCommandInteractionEvent data := event.SlashCommandInteractionData() switch data.CommandName { case "locale": - err := event.Create(discord.NewMessageCreateBuilder(). + err := event.CreateMessage(discord.NewMessageCreateBuilder(). SetContentf("Guild Locale: %s\nLocale: %s", event.GuildLocale, event.Locale). Build(), ) @@ -79,7 +79,7 @@ func applicationCommandListener(event *events.ApplicationCommandInteractionEvent AddField("Time", "...", true). AddField("Code", "```go\n"+code+"\n```", false). AddField("Output", "```\n...\n```", false) - _ = event.Create(discord.NewMessageCreateBuilder().SetEmbeds(embed.Build()).Build()) + _ = event.CreateMessage(discord.NewMessageCreateBuilder().SetEmbeds(embed.Build()).Build()) start := time.Now() output, err := gval.Evaluate(code, map[string]interface{}{ @@ -91,7 +91,7 @@ func applicationCommandListener(event *events.ApplicationCommandInteractionEvent embed.SetField(1, "Time", strconv.Itoa(int(elapsed.Milliseconds()))+"ms", true) if err != nil { - _, err = event.UpdateResponse(discord.NewMessageUpdateBuilder(). + _, err = event.UpdateOriginalMessage(discord.NewMessageUpdateBuilder(). SetEmbeds(embed. SetColor(red). SetField(0, "Status", "Failed", true). @@ -105,7 +105,7 @@ func applicationCommandListener(event *events.ApplicationCommandInteractionEvent } return } - _, err = event.UpdateResponse(discord.NewMessageUpdateBuilder(). + _, err = event.UpdateOriginalMessage(discord.NewMessageUpdateBuilder(). SetEmbeds(embed. SetColor(green). SetField(0, "Status", "Success", true). @@ -120,7 +120,7 @@ func applicationCommandListener(event *events.ApplicationCommandInteractionEvent }() case "say": - _ = event.Create(discord.NewMessageCreateBuilder(). + _ = event.CreateMessage(discord.NewMessageCreateBuilder(). SetContent(*data.Options.String("message")). SetEphemeral(*data.Options.Bool("ephemeral")). ClearAllowedMentions(). @@ -129,13 +129,13 @@ func applicationCommandListener(event *events.ApplicationCommandInteractionEvent case "test": go func() { - _ = event.DeferCreate(true) + _ = event.DeferCreateMessage(true) members, err := event.Guild().RequestMembersWithQuery("", 0) if err != nil { - _, _ = event.UpdateResponse(discord.NewMessageUpdateBuilder().SetContentf("failed to load members. error: %s", err).Build()) + _, _ = event.UpdateOriginalMessage(discord.NewMessageUpdateBuilder().SetContentf("failed to load members. error: %s", err).Build()) return } - _, _ = event.UpdateResponse(discord.NewMessageUpdateBuilder(). + _, _ = event.UpdateOriginalMessage(discord.NewMessageUpdateBuilder(). SetContentf("loaded %d members", len(members)). Build(), ) @@ -146,11 +146,11 @@ func applicationCommandListener(event *events.ApplicationCommandInteractionEvent role := data.Options.Role("role") if err := event.Bot().RestServices.GuildService().AddMemberRole(*event.GuildID, user.ID, role.ID); err == nil { - _ = event.Create(discord.NewMessageCreateBuilder().AddEmbeds( + _ = event.CreateMessage(discord.NewMessageCreateBuilder().AddEmbeds( discord.NewEmbedBuilder().SetColor(green).SetDescriptionf("Added %s to %s", role, user).Build(), ).Build()) } else { - _ = event.Create(discord.NewMessageCreateBuilder().AddEmbeds( + _ = event.CreateMessage(discord.NewMessageCreateBuilder().AddEmbeds( discord.NewEmbedBuilder().SetColor(red).SetDescriptionf("Failed to add %s to %s", role, user).Build(), ).Build()) } @@ -160,11 +160,11 @@ func applicationCommandListener(event *events.ApplicationCommandInteractionEvent role := data.Options.Role("role") if err := event.Bot().RestServices.GuildService().RemoveMemberRole(*event.GuildID, user.ID, role.ID); err == nil { - _ = event.Create(discord.NewMessageCreateBuilder().AddEmbeds( + _ = event.CreateMessage(discord.NewMessageCreateBuilder().AddEmbeds( discord.NewEmbedBuilder().SetColor(65280).SetDescriptionf("Removed %s from %s", role, user).Build(), ).Build()) } else { - _ = event.Create(discord.NewMessageCreateBuilder().AddEmbeds( + _ = event.CreateMessage(discord.NewMessageCreateBuilder().AddEmbeds( discord.NewEmbedBuilder().SetColor(16711680).SetDescriptionf("Failed to remove %s from %s", role, user).Build(), ).Build()) } diff --git a/core/application_command_interaction.go b/core/application_command_interaction.go index 800db57a..90c16f97 100644 --- a/core/application_command_interaction.go +++ b/core/application_command_interaction.go @@ -9,7 +9,7 @@ type ApplicationCommandInteractionFilter func(interaction *ApplicationCommandInt // ApplicationCommandInteraction represents a generic ApplicationCommandInteraction received from discord type ApplicationCommandInteraction struct { - *ReplyInteraction + *ModalReplyInteraction Data ApplicationCommandInteractionData } @@ -18,16 +18,16 @@ func (i ApplicationCommandInteraction) Type() discord.InteractionType { return discord.InteractionTypeApplicationCommand } -func (i ApplicationCommandInteraction) SlashCommandInteractionData() *SlashCommandInteractionData { - return i.Data.(*SlashCommandInteractionData) +func (i ApplicationCommandInteraction) SlashCommandInteractionData() SlashCommandInteractionData { + return i.Data.(SlashCommandInteractionData) } -func (i ApplicationCommandInteraction) UserCommandInteractionData() *UserCommandInteractionData { - return i.Data.(*UserCommandInteractionData) +func (i ApplicationCommandInteraction) UserCommandInteractionData() UserCommandInteractionData { + return i.Data.(UserCommandInteractionData) } -func (i ApplicationCommandInteraction) MessageCommandInteractionData() *MessageCommandInteractionData { - return i.Data.(*MessageCommandInteractionData) +func (i ApplicationCommandInteraction) MessageCommandInteractionData() MessageCommandInteractionData { + return i.Data.(MessageCommandInteractionData) } type ApplicationCommandInteractionData interface { diff --git a/core/component_interaction.go b/core/component_interaction.go index b0c4fa49..e9d634d0 100644 --- a/core/component_interaction.go +++ b/core/component_interaction.go @@ -9,7 +9,7 @@ type ComponentInteractionFilter func(interaction *ComponentInteraction) bool // ComponentInteraction represents a generic ComponentInteraction received from discord type ComponentInteraction struct { - *ReplyInteraction + *ModalReplyInteraction Data ComponentInteractionData Message *Message } @@ -19,15 +19,15 @@ func (i ComponentInteraction) Type() discord.InteractionType { return discord.InteractionTypeComponent } -func (i ComponentInteraction) ButtonInteractionData() *ButtonInteractionData { - return i.Data.(*ButtonInteractionData) +func (i ComponentInteraction) ButtonInteractionData() ButtonInteractionData { + return i.Data.(ButtonInteractionData) } -func (i ComponentInteraction) SelectMenuInteractionData() *SelectMenuInteractionData { - return i.Data.(*SelectMenuInteractionData) +func (i ComponentInteraction) SelectMenuInteractionData() SelectMenuInteractionData { + return i.Data.(SelectMenuInteractionData) } -func (i ComponentInteraction) Update(messageUpdate discord.MessageUpdate, opts ...rest.RequestOpt) error { +func (i ComponentInteraction) UpdateMessage(messageUpdate discord.MessageUpdate, opts ...rest.RequestOpt) error { return i.Respond(discord.InteractionCallbackTypeUpdateMessage, messageUpdate, opts...) } @@ -44,10 +44,10 @@ func (i ComponentInteraction) UpdateComponent(customID discord.CustomID, compone } } - return i.Update(discord.NewMessageUpdateBuilder().SetContainerComponents(containerComponents...).Build(), opts...) + return i.UpdateMessage(discord.NewMessageUpdateBuilder().SetContainerComponents(containerComponents...).Build(), opts...) } -func (i ComponentInteraction) DeferUpdate(opts ...rest.RequestOpt) error { +func (i ComponentInteraction) DeferUpdateMessage(opts ...rest.RequestOpt) error { return i.Respond(discord.InteractionCallbackTypeDeferredUpdateMessage, nil, opts...) } diff --git a/core/entity_builder.go b/core/entity_builder.go index 7237b29d..dcb9471a 100644 --- a/core/entity_builder.go +++ b/core/entity_builder.go @@ -101,7 +101,7 @@ func (b *entityBuilderImpl) CreateInteraction(interaction discord.Interaction, c var interactionData ApplicationCommandInteractionData switch d := i.Data.(type) { case discord.SlashCommandInteractionData: - data := &SlashCommandInteractionData{ + data := SlashCommandInteractionData{ SlashCommandInteractionData: d, Resolved: &SlashCommandResolved{ Users: map[snowflake.Snowflake]*User{}, @@ -203,7 +203,7 @@ func (b *entityBuilderImpl) CreateInteraction(interaction discord.Interaction, c interactionData = data case discord.UserCommandInteractionData: - data := &UserCommandInteractionData{ + data := UserCommandInteractionData{ UserCommandInteractionData: d, Resolved: &UserCommandResolved{ Users: map[snowflake.Snowflake]*User{}, @@ -222,7 +222,7 @@ func (b *entityBuilderImpl) CreateInteraction(interaction discord.Interaction, c interactionData = data case discord.MessageCommandInteractionData: - data := &MessageCommandInteractionData{ + data := MessageCommandInteractionData{ MessageCommandInteractionData: d, Resolved: &MessageCommandResolved{ Messages: map[snowflake.Snowflake]*Message{}, @@ -234,24 +234,24 @@ func (b *entityBuilderImpl) CreateInteraction(interaction discord.Interaction, c interactionData = data } return &ApplicationCommandInteraction{ - ReplyInteraction: &ReplyInteraction{BaseInteraction: b.baseInteraction(i.BaseInteraction, c, updateCache)}, - Data: interactionData, + ModalReplyInteraction: &ModalReplyInteraction{ReplyInteraction: &ReplyInteraction{BaseInteraction: b.baseInteraction(i.BaseInteraction, c, updateCache)}}, + Data: interactionData, } case discord.ComponentInteraction: componentInteraction := &ComponentInteraction{ - ReplyInteraction: &ReplyInteraction{BaseInteraction: b.baseInteraction(i.BaseInteraction, c, updateCache)}, - Message: b.CreateMessage(i.Message, updateCache), + ModalReplyInteraction: &ModalReplyInteraction{ReplyInteraction: &ReplyInteraction{BaseInteraction: b.baseInteraction(i.BaseInteraction, c, updateCache)}}, + Message: b.CreateMessage(i.Message, updateCache), } switch d := i.Data.(type) { case discord.ButtonInteractionData: - componentInteraction.Data = &ButtonInteractionData{ + componentInteraction.Data = ButtonInteractionData{ ButtonInteractionData: d, interaction: componentInteraction, } case discord.SelectMenuInteractionData: - componentInteraction.Data = &SelectMenuInteractionData{ + componentInteraction.Data = SelectMenuInteractionData{ SelectMenuInteractionData: d, interaction: componentInteraction, } @@ -290,6 +290,14 @@ func (b *entityBuilderImpl) CreateInteraction(interaction discord.Interaction, c return autocompleteInteraction + case discord.ModalSubmitInteraction: + modalSubmitInteraction := &ModalSubmitInteraction{ + ReplyInteraction: &ReplyInteraction{BaseInteraction: b.baseInteraction(i.BaseInteraction, c, updateCache)}, + Data: i.Data, + } + + return modalSubmitInteraction + default: b.Bot().Logger.Error("unknown interaction type %d received", interaction.Type()) return nil diff --git a/core/interaction.go b/core/interaction.go index 389c281c..d3ff220a 100644 --- a/core/interaction.go +++ b/core/interaction.go @@ -1,6 +1,8 @@ package core import ( + "time" + "github.com/DisgoOrg/disgo/discord" "github.com/DisgoOrg/disgo/rest" "github.com/DisgoOrg/snowflake" @@ -37,6 +39,10 @@ func (i *BaseInteraction) Respond(callbackType discord.InteractionCallbackType, } i.Acknowledged = true + if time.Now().After(i.ID.Time().Add(3 * time.Second)) { + return discord.ErrInteractionExpired + } + response := discord.InteractionResponse{ Type: callbackType, Data: callbackData, @@ -70,7 +76,7 @@ type ReplyInteraction struct { *BaseInteraction } -func (i ReplyInteraction) GetResponse(opts ...rest.RequestOpt) (*Message, error) { +func (i ReplyInteraction) GetOriginalMessage(opts ...rest.RequestOpt) (*Message, error) { message, err := i.Bot.RestServices.InteractionService().GetInteractionResponse(i.ApplicationID, i.Token, opts...) if err != nil { return nil, err @@ -78,7 +84,7 @@ func (i ReplyInteraction) GetResponse(opts ...rest.RequestOpt) (*Message, error) return i.Bot.EntityBuilder.CreateMessage(*message, CacheStrategyNoWs), nil } -func (i ReplyInteraction) UpdateResponse(messageUpdate discord.MessageUpdate, opts ...rest.RequestOpt) (*Message, error) { +func (i ReplyInteraction) UpdateOriginalMessage(messageUpdate discord.MessageUpdate, opts ...rest.RequestOpt) (*Message, error) { message, err := i.Bot.RestServices.InteractionService().UpdateInteractionResponse(i.ApplicationID, i.Token, messageUpdate, opts...) if err != nil { return nil, err @@ -86,15 +92,15 @@ func (i ReplyInteraction) UpdateResponse(messageUpdate discord.MessageUpdate, op return i.Bot.EntityBuilder.CreateMessage(*message, CacheStrategyNoWs), nil } -func (i ReplyInteraction) DeleteResponse(opts ...rest.RequestOpt) error { +func (i ReplyInteraction) DeleteOriginalMessage(opts ...rest.RequestOpt) error { return i.Bot.RestServices.InteractionService().DeleteInteractionResponse(i.ApplicationID, i.Token, opts...) } -func (i ReplyInteraction) Create(messageCreate discord.MessageCreate, opts ...rest.RequestOpt) error { +func (i ReplyInteraction) CreateMessage(messageCreate discord.MessageCreate, opts ...rest.RequestOpt) error { return i.Respond(discord.InteractionCallbackTypeChannelMessageWithSource, messageCreate, opts...) } -func (i ReplyInteraction) DeferCreate(ephemeral bool, opts ...rest.RequestOpt) error { +func (i ReplyInteraction) DeferCreateMessage(ephemeral bool, opts ...rest.RequestOpt) error { var data discord.InteractionCallbackData if ephemeral { data = discord.MessageCreate{Flags: discord.MessageFlagEphemeral} @@ -129,3 +135,11 @@ func (i ReplyInteraction) UpdateFollowupMessage(messageID snowflake.Snowflake, m func (i ReplyInteraction) DeleteFollowupMessage(messageID snowflake.Snowflake, opts ...rest.RequestOpt) error { return i.Bot.RestServices.InteractionService().DeleteFollowupMessage(i.ApplicationID, i.Token, messageID, opts...) } + +type ModalReplyInteraction struct { + *ReplyInteraction +} + +func (i ModalReplyInteraction) CreateModal(modalCreate discord.ModalCreate, opts ...rest.RequestOpt) error { + return i.Respond(discord.InteractionCallbackTypeModal, modalCreate, opts...) +} diff --git a/core/modal_submit_interaction.go b/core/modal_submit_interaction.go index 471799e2..2102badd 100644 --- a/core/modal_submit_interaction.go +++ b/core/modal_submit_interaction.go @@ -2,7 +2,6 @@ package core import ( "github.com/DisgoOrg/disgo/discord" - "github.com/DisgoOrg/disgo/rest" ) type ModalSubmitInteractionFilter func(ModalSubmitInteraction *ModalSubmitInteraction) bool @@ -10,60 +9,11 @@ type ModalSubmitInteractionFilter func(ModalSubmitInteraction *ModalSubmitIntera var _ Interaction = (*ModalSubmitInteraction)(nil) type ModalSubmitInteraction struct { - discord.ModalSubmitInteraction - *InteractionFields + *ReplyInteraction + Data discord.ModalSubmitInteractionData } - -func (i *ModalSubmitInteraction) Respond(callbackType discord.InteractionCallbackType, callbackData discord.InteractionCallbackData, opts ...rest.RequestOpt) error { - return respond(i.InteractionFields, i.ID, i.Token, callbackType, callbackData, opts...) -} - -func (i *ModalSubmitInteraction) Create(messageCreate discord.MessageCreate, opts ...rest.RequestOpt) error { - return create(i.InteractionFields, i.ID, i.Token, messageCreate, opts...) -} - -func (i *ModalSubmitInteraction) DeferCreate(ephemeral bool, opts ...rest.RequestOpt) error { - return deferCreate(i.InteractionFields, i.ID, i.Token, ephemeral, opts...) -} - -func (i *ModalSubmitInteraction) GetOriginal(opts ...rest.RequestOpt) (*Message, error) { - return getOriginal(i.InteractionFields, i.ApplicationID, i.Token, opts...) -} - -func (i *ModalSubmitInteraction) UpdateOriginal(messageUpdate discord.MessageUpdate, opts ...rest.RequestOpt) (*Message, error) { - return updateOriginal(i.InteractionFields, i.ApplicationID, i.Token, messageUpdate, opts...) -} - -func (i *ModalSubmitInteraction) DeleteOriginal(opts ...rest.RequestOpt) error { - return deleteOriginal(i.InteractionFields, i.ApplicationID, i.Token, opts...) +func (i ModalSubmitInteraction) interaction() {} +func (i ModalSubmitInteraction) Type() discord.InteractionType { + return discord.InteractionTypeModalSubmit } - -func (i *ModalSubmitInteraction) GetFollowup(messageID discord.Snowflake, opts ...rest.RequestOpt) (*Message, error) { - return getFollowup(i.InteractionFields, i.ApplicationID, i.Token, messageID, opts...) -} - -func (i *ModalSubmitInteraction) CreateFollowup(messageCreate discord.MessageCreate, opts ...rest.RequestOpt) (*Message, error) { - return createFollowup(i.InteractionFields, i.ApplicationID, i.Token, messageCreate, opts...) -} - -func (i *ModalSubmitInteraction) UpdateFollowup(messageID discord.Snowflake, messageUpdate discord.MessageUpdate, opts ...rest.RequestOpt) (*Message, error) { - return updateFollowup(i.InteractionFields, i.ApplicationID, i.Token, messageID, messageUpdate, opts...) -} - -func (i *ModalSubmitInteraction) DeleteFollowup(messageID discord.Snowflake, opts ...rest.RequestOpt) error { - return deleteFollowup(i.InteractionFields, i.ApplicationID, i.Token, messageID, opts...) -} - -// Guild returns the Guild from the Caches -func (i *ModalSubmitInteraction) Guild() *Guild { - if i.GuildID == nil { - return nil - } - return i.Bot.Caches.Guilds().Get(*i.GuildID) -} - -// Channel returns the Channel from the Caches -func (i *ModalSubmitInteraction) Channel() Channel { - return i.Bot.Caches.Channels().Get(i.ChannelID) -} \ No newline at end of file diff --git a/discord/errors.go b/discord/errors.go index f2889d18..418a4c0a 100644 --- a/discord/errors.go +++ b/discord/errors.go @@ -23,6 +23,7 @@ var ( ErrSelfDM = errors.New("can't open a dm channel to yourself") ErrInteractionAlreadyReplied = errors.New("you already replied to this interaction") + ErrInteractionExpired = errors.New("this interaction has expired") ErrChannelNotTypeNews = errors.New("channel type is not 'NEWS'") diff --git a/discord/interaction.go b/discord/interaction.go index e581ca50..0176acb4 100644 --- a/discord/interaction.go +++ b/discord/interaction.go @@ -77,6 +77,11 @@ func (i *UnmarshalInteraction) UnmarshalJSON(data []byte) error { err = json.Unmarshal(data, &v) interaction = v + case InteractionTypeModalSubmit: + v := ModalSubmitInteraction{} + err = json.Unmarshal(data, &v) + interaction = v + default: return fmt.Errorf("unkown interaction with type %d received", iType.Type) } @@ -392,33 +397,26 @@ var ( ) type ModalSubmitInteraction struct { - ID Snowflake `json:"id"` - ApplicationID Snowflake `json:"application_id"` - Token string `json:"token"` - Version int `json:"version"` - GuildID *Snowflake `json:"guild_id,omitempty"` - ChannelID Snowflake `json:"channel_id"` - Member *Member `json:"member,omitempty"` - User *User `json:"user,omitempty"` - Data ModalSubmitInteractionData `json:"data"` + BaseInteraction + Data ModalSubmitInteractionData `json:"data"` } func (ModalSubmitInteraction) interaction() {} -func (ModalSubmitInteraction) InteractionType() InteractionType { +func (ModalSubmitInteraction) Type() InteractionType { return InteractionTypeModalSubmit } type ModalSubmitInteractionData struct { - CustomID CustomID `json:"custom_id"` - Components []ModalSubmitContainerComponent `json:"components"` + CustomID CustomID `json:"custom_id"` + Components []ModalContainerComponent `json:"components"` } func (d *ModalSubmitInteractionData) Unmarshal(data []byte) error { type modalSubmitInteractionData ModalSubmitInteractionData var iData struct { + Components []UnmarshalModalComponent `json:"components"` modalSubmitInteractionData - Components []UnmarshalModalSubmitComponent `json:"components"` } if err := json.Unmarshal(data, &iData); err != nil { @@ -428,9 +426,9 @@ func (d *ModalSubmitInteractionData) Unmarshal(data []byte) error { *d = ModalSubmitInteractionData(iData.modalSubmitInteractionData) if len(iData.Components) > 0 { - d.Components = make([]ModalSubmitContainerComponent, len(iData.Components)) + d.Components = make([]ModalContainerComponent, len(iData.Components)) for i := range iData.Components { - d.Components[i] = iData.Components[i].ModalSubmitComponent.(ModalSubmitContainerComponent) + d.Components[i] = iData.Components[i].ModalComponent.(ModalContainerComponent) } } diff --git a/discord/interaction_callback.go b/discord/interaction_callback.go index 99867d3e..5a1a9594 100644 --- a/discord/interaction_callback.go +++ b/discord/interaction_callback.go @@ -69,12 +69,12 @@ type AutocompleteChoiceFloat struct { func (AutocompleteChoiceFloat) autoCompleteChoice() {} -var _ InteractionCallbackData = (*Modal)(nil) +var _ InteractionCallbackData = (*ModalCreate)(nil) -type Modal struct { - CustomID CustomID `json:"custom_id"` - Title string `json:"title"` - Components []ContainerComponent `json:"components"` +type ModalCreate struct { + CustomID CustomID `json:"custom_id"` + Title string `json:"title"` + Components []ModalContainerComponent `json:"components"` } -func (m Modal) interactionCallbackData() {} +func (m ModalCreate) interactionCallbackData() {} diff --git a/discord/modal_components.go b/discord/modal_components.go new file mode 100644 index 00000000..f1e25bff --- /dev/null +++ b/discord/modal_components.go @@ -0,0 +1,126 @@ +package discord + +import ( + "fmt" + + "github.com/DisgoOrg/disgo/json" +) + +type ModalComponent interface { + Type() ComponentType + modalComponent() +} + +type ModalContainerComponent interface { + ModalComponent + Components() []ModalInteractiveComponent + modalContainerComponent() +} + +type ModalInteractiveComponent interface { + ModalComponent + ID() CustomID + Value() string + modalInteractiveComponent() +} + +type UnmarshalModalComponent struct { + ModalComponent +} + +func (u *UnmarshalModalComponent) UnmarshalJSON(data []byte) error { + var cType struct { + Type ComponentType `json:"type"` + } + + if err := json.Unmarshal(data, &cType); err != nil { + return err + } + + var ( + modalSubmitComponent ModalComponent + err error + ) + + switch cType.Type { + case ComponentTypeActionRow: + v := ModalActionRowComponent{} + err = json.Unmarshal(data, &v) + modalSubmitComponent = v + + case ComponentTypeInputText: + v := ModalInputTextComponent{} + err = json.Unmarshal(data, &v) + modalSubmitComponent = v + + default: + err = fmt.Errorf("unkown component with type %d received", cType.Type) + } + if err != nil { + return err + } + + u.ModalComponent = modalSubmitComponent + return nil +} + +var ( + _ ModalComponent = (*ModalActionRowComponent)(nil) + _ ModalContainerComponent = (*ModalActionRowComponent)(nil) +) + +type ModalActionRowComponent []ModalInteractiveComponent + +func (c *ModalActionRowComponent) UnmarshalJSON(data []byte) error { + var actionRow struct { + Components []UnmarshalModalComponent `json:"components"` + } + + if err := json.Unmarshal(data, &actionRow); err != nil { + return err + } + + if len(actionRow.Components) > 0 { + *c = make([]ModalInteractiveComponent, len(actionRow.Components)) + for i := range actionRow.Components { + (*c)[i] = actionRow.Components[i].ModalComponent.(ModalInteractiveComponent) + } + } + + return nil +} + +func (ModalActionRowComponent) Type() ComponentType { + return ComponentTypeInputText +} + +func (c ModalActionRowComponent) Components() []ModalInteractiveComponent { + return c +} + +func (ModalActionRowComponent) modalComponent() {} +func (ModalActionRowComponent) modalContainerComponent() {} + +var ( + _ ModalComponent = (*ModalInputTextComponent)(nil) + _ ModalInteractiveComponent = (*ModalInputTextComponent)(nil) +) + +type ModalInputTextComponent struct { + CustomID CustomID `json:"custom_id"` + TextValue string `json:"value"` +} + +func (ModalInputTextComponent) Type() ComponentType { + return ComponentTypeInputText +} + +func (c ModalInputTextComponent) ID() CustomID { + return c.CustomID +} +func (c ModalInputTextComponent) Value() string { + return c.TextValue +} + +func (ModalInputTextComponent) modalComponent() {} +func (ModalInputTextComponent) modalInteractiveComponent() {} diff --git a/discord/modal_submit.go b/discord/modal_submit.go deleted file mode 100644 index e5689450..00000000 --- a/discord/modal_submit.go +++ /dev/null @@ -1,126 +0,0 @@ -package discord - -import ( - "fmt" - - "github.com/DisgoOrg/disgo/json" -) - -type ModalSubmitComponent interface { - Type() ComponentType - modalSubmitComponent() -} - -type ModalSubmitContainerComponent interface { - ModalSubmitComponent - Components() []ModalSubmitInteractiveComponent - modalSubmitContainerComponent() -} - -type ModalSubmitInteractiveComponent interface { - ModalSubmitComponent - ID() CustomID - Value() string - modalSubmitInteractiveComponent() -} - -type UnmarshalModalSubmitComponent struct { - ModalSubmitComponent -} - -func (u *UnmarshalModalSubmitComponent) UnmarshalJSON(data []byte) error { - var cType struct { - Type ComponentType `json:"type"` - } - - if err := json.Unmarshal(data, &cType); err != nil { - return err - } - - var ( - modalSubmitComponent ModalSubmitComponent - err error - ) - - switch cType.Type { - case ComponentTypeActionRow: - v := ModalSubmitActionRowComponent{} - err = json.Unmarshal(data, &v) - modalSubmitComponent = v - - case ComponentTypeInputText: - v := ModalSubmitInputTextComponent{} - err = json.Unmarshal(data, &v) - modalSubmitComponent = v - - default: - err = fmt.Errorf("unkown component with type %d received", cType.Type) - } - if err != nil { - return err - } - - u.ModalSubmitComponent = modalSubmitComponent - return nil -} - -var ( - _ ModalSubmitComponent = (*ModalSubmitActionRowComponent)(nil) - _ ModalSubmitContainerComponent = (*ModalSubmitActionRowComponent)(nil) -) - -type ModalSubmitActionRowComponent []ModalSubmitInteractiveComponent - -func (c *ModalSubmitActionRowComponent) UnmarshalJSON(data []byte) error { - var actionRow struct { - Components []UnmarshalModalSubmitComponent `json:"components"` - } - - if err := json.Unmarshal(data, &actionRow); err != nil { - return err - } - - if len(actionRow.Components) > 0 { - *c = make([]ModalSubmitInteractiveComponent, len(actionRow.Components)) - for i := range actionRow.Components { - (*c)[i] = actionRow.Components[i].ModalSubmitComponent.(ModalSubmitInteractiveComponent) - } - } - - return nil -} - -func (ModalSubmitActionRowComponent) Type() ComponentType { - return ComponentTypeInputText -} - -func (c ModalSubmitActionRowComponent) Components() []ModalSubmitInteractiveComponent { - return c -} - -func (ModalSubmitActionRowComponent) modalSubmitComponent() {} -func (ModalSubmitActionRowComponent) modalSubmitContainerComponent() {} - -var ( - _ ModalSubmitComponent = (*ModalSubmitInputTextComponent)(nil) - _ ModalSubmitInteractiveComponent = (*ModalSubmitInputTextComponent)(nil) -) - -type ModalSubmitInputTextComponent struct { - CustomID CustomID `json:"custom_id"` - TextValue string `json:"value"` -} - -func (ModalSubmitInputTextComponent) Type() ComponentType { - return ComponentTypeInputText -} - -func (c ModalSubmitInputTextComponent) ID() CustomID { - return c.CustomID -} -func (c ModalSubmitInputTextComponent) Value() string { - return c.TextValue -} - -func (ModalSubmitInputTextComponent) modalSubmitComponent() {} -func (ModalSubmitInputTextComponent) modalSubmitInteractiveComponent() {} From 0b9a2f61af7f0c4ba80b97c21860d3ae7aecf958 Mon Sep 17 00:00:00 2001 From: TopiSenpai Date: Wed, 9 Feb 2022 03:21:28 +0100 Subject: [PATCH 4/5] fix implemention of modals --- .gitignore | 3 +- _examples/test/commands.go | 102 +----------- _examples/test/examplebot.go | 14 +- _examples/test/go.mod | 1 - _examples/test/go.sum | 4 - _examples/test/listeners.go | 155 +++++++----------- core/application_command_interaction.go | 9 +- core/component_interaction.go | 12 +- core/entity_builder.go | 15 +- core/events/events_interactions.go | 4 +- core/events/listener_adapter.go | 5 + .../gateway_handler_interaction_create.go | 6 + core/interaction.go | 60 +++---- core/modal_submit_interaction.go | 11 +- discord/component.go | 51 +++--- discord/interaction.go | 12 +- discord/interaction_callback.go | 6 +- discord/modal_components.go | 126 -------------- 18 files changed, 183 insertions(+), 413 deletions(-) delete mode 100644 discord/modal_components.go diff --git a/.gitignore b/.gitignore index 723ef36f..de03f380 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.idea \ No newline at end of file +.idea +.env diff --git a/_examples/test/commands.go b/_examples/test/commands.go index 1beaaa0d..dee8b9e7 100644 --- a/_examples/test/commands.go +++ b/_examples/test/commands.go @@ -12,18 +12,6 @@ var commands = []discord.ApplicationCommandCreate{ Description: "return the guild & your locale", DefaultPermission: true, }, - discord.SlashCommandCreate{ - Name: "eval", - Description: "runs some go code", - DefaultPermission: true, - Options: []discord.ApplicationCommandOption{ - discord.ApplicationCommandOptionString{ - Name: "code", - Description: "the code to eval", - Required: true, - }, - }, - }, discord.SlashCommandCreate{ Name: "test", Description: "test", @@ -46,98 +34,10 @@ var commands = []discord.ApplicationCommandCreate{ }, }, }, - discord.SlashCommandCreate{ - Name: "addrole", - Description: "This command adds a role to a member", - DefaultPermission: true, - Options: []discord.ApplicationCommandOption{ - discord.ApplicationCommandOptionUser{ - Name: "member", - Description: "The member to add a role to", - Required: true, - }, - discord.ApplicationCommandOptionRole{ - Name: "role", - Description: "The role to add to a member", - Required: true, - }, - }, - }, - discord.SlashCommandCreate{ - Name: "removerole", - Description: "This command removes a role from a member", - DefaultPermission: true, - Options: []discord.ApplicationCommandOption{ - discord.ApplicationCommandOptionUser{ - Name: "member", - Description: "The member to removes a role from", - Required: true, - }, - discord.ApplicationCommandOptionRole{ - Name: "role", - Description: "The role to removes from a member", - Required: true, - }, - }, - }, - discord.SlashCommandCreate{ - Name: "root", - Description: "root command", - DefaultPermission: true, - Options: []discord.ApplicationCommandOption{ - discord.ApplicationCommandOptionSubCommandGroup{ - Name: "group", - Description: "group command", - Options: []discord.ApplicationCommandOptionSubCommand{ - { - Name: "sub", - Description: "sub command", - Options: []discord.ApplicationCommandOption{ - discord.ApplicationCommandOptionString{ - Name: "test", - Description: "test", - Required: true, - }, - }, - }, - }, - }, - }, - }, } func registerCommands(bot *core.Bot) { - cmds, err := bot.SetGuildCommands(guildID, commands) - if err != nil { + if _, err := bot.SetGuildCommands(guildID, commands); err != nil { log.Fatalf("error while registering guild commands: %s", err) } - - var cmdsPermissions []discord.ApplicationCommandPermissionsSet - for _, cmd := range cmds { - var perms discord.ApplicationCommandPermission - if c, ok := cmd.(core.SlashCommand); ok { - if c.Name == "eval" { - perms = discord.ApplicationCommandPermissionRole{ - RoleID: adminRoleID, - Permission: true, - } - } else { - perms = discord.ApplicationCommandPermissionRole{ - RoleID: testRoleID, - Permission: true, - } - cmdsPermissions = append(cmdsPermissions, discord.ApplicationCommandPermissionsSet{ - ID: c.ID(), - Permissions: []discord.ApplicationCommandPermission{perms}, - }) - } - cmdsPermissions = append(cmdsPermissions, discord.ApplicationCommandPermissionsSet{ - ID: c.ID(), - Permissions: []discord.ApplicationCommandPermission{perms}, - }) - } - } - if _, err = bot.SetGuildCommandsPermissions(guildID, cmdsPermissions); err != nil { - log.Fatalf("error while setting command permissions: %s", err) - } } diff --git a/_examples/test/examplebot.go b/_examples/test/examplebot.go index a87df771..a01c39d0 100644 --- a/_examples/test/examplebot.go +++ b/_examples/test/examplebot.go @@ -16,17 +16,9 @@ import ( "github.com/DisgoOrg/snowflake" ) -const ( - red = 16711680 - orange = 16562691 - green = 65280 -) - var ( - token = os.Getenv("disgo_token") - guildID = snowflake.GetSnowflakeEnv("disgo_test_guild_id") - adminRoleID = snowflake.GetSnowflakeEnv("disgo_admin_role_id") - testRoleID = snowflake.GetSnowflakeEnv("disgo_test_role_id") + token = os.Getenv("disgo_token") + guildID = snowflake.GetSnowflakeEnv("disgo_test_guild_id") //go:embed gopher.png gopher []byte @@ -41,7 +33,7 @@ func main() { disgo, err := bot.New(token, //bot.WithRawEventsEnabled(), bot.WithGatewayOpts( - gateway.WithGatewayIntents(discord.GatewayIntentsAll), + gateway.WithGatewayIntents(discord.GatewayIntentsNonPrivileged), gateway.WithPresence(core.NewListeningPresence("your bullshit", discord.OnlineStatusOnline, false)), ), bot.WithCacheOpts( diff --git a/_examples/test/go.mod b/_examples/test/go.mod index 6859bd5d..9e4b89a5 100644 --- a/_examples/test/go.mod +++ b/_examples/test/go.mod @@ -8,7 +8,6 @@ require ( github.com/DisgoOrg/disgo v0.5.6 github.com/DisgoOrg/log v1.1.2 github.com/DisgoOrg/snowflake v1.0.3 - github.com/PaesslerAG/gval v1.1.1 ) require ( diff --git a/_examples/test/go.sum b/_examples/test/go.sum index 0e99b83b..b5932a7f 100644 --- a/_examples/test/go.sum +++ b/_examples/test/go.sum @@ -2,10 +2,6 @@ github.com/DisgoOrg/log v1.1.2 h1:tGJS4jaH1PyjPRHybHp8WpYJ/4fR3fYWT4Mv1PoDGBM= github.com/DisgoOrg/log v1.1.2/go.mod h1:tSMofXaNhQNvzLRoL4tAiCG9yGY1ES5DLvduh7e9GRU= github.com/DisgoOrg/snowflake v1.0.3 h1:KfzswrZOr8ISu/J5P3C5+QC9yKxW7nNJWeCJpkldxbI= github.com/DisgoOrg/snowflake v1.0.3/go.mod h1:jIQVlVmElm2OGt6v52ITf/71ODaU09chUCflxt8+3yM= -github.com/PaesslerAG/gval v1.1.1 h1:4d7pprU9876+m3rc08X33UjGip8oV1kkm8Gh5GBuTss= -github.com/PaesslerAG/gval v1.1.1/go.mod h1:Fa8gfkCmUsELXgayr8sfL/sw+VzCVoa03dcOcR/if2w= -github.com/PaesslerAG/jsonpath v0.1.0 h1:gADYeifvlqK3R3i2cR5B4DGgxLXIPb3TRTH1mGi0jPI= -github.com/PaesslerAG/jsonpath v0.1.0/go.mod h1:4BzmtoM/PI8fPO4aQGIusjGxGir2BzcV0grWtFzq1Y8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/_examples/test/listeners.go b/_examples/test/listeners.go index 180cc4c9..f1f2b2dc 100644 --- a/_examples/test/listeners.go +++ b/_examples/test/listeners.go @@ -2,28 +2,68 @@ package main import ( "bytes" - "fmt" - "strconv" + "strings" "time" - "github.com/DisgoOrg/disgo/core/events" - "github.com/DisgoOrg/disgo/core" + "github.com/DisgoOrg/disgo/core/events" "github.com/DisgoOrg/disgo/discord" "github.com/DisgoOrg/log" - "github.com/PaesslerAG/gval" ) var listener = &events.ListenerAdapter{ OnGuildMessageCreate: messageListener, OnApplicationCommandInteraction: applicationCommandListener, OnComponentInteraction: componentListener, + OnModalSubmit: modalListener, +} + +func modalListener(event *events.ModalSubmitInteractionEvent) { + switch event.Data.CustomID { + case "test1": + value := event.Data.Components[0].Components()[0].(discord.TextInputComponent).Value + _ = event.CreateMessage(discord.MessageCreate{Content: value}) + + case "test2": + value := event.Data.Components[0].Components()[0].(discord.TextInputComponent).Value + _ = event.DeferCreateMessage(false) + go func() { + time.Sleep(time.Second * 5) + _, _ = event.UpdateOriginalMessage(discord.MessageUpdate{Content: &value}) + }() + + case "test3": + value := event.Data.Components[0].Components()[0].(discord.TextInputComponent).Value + _ = event.UpdateMessage(discord.MessageUpdate{Content: &value}) + + case "test4": + _ = event.DeferUpdateMessage() + } } func componentListener(event *events.ComponentInteractionEvent) { switch data := event.Data.(type) { - case *core.ButtonInteractionData: - switch data.CustomID { + case core.ButtonInteractionData: + ids := strings.Split(data.CustomID.String(), ":") + switch ids[0] { + case "modal": + _ = event.CreateModal(discord.ModalCreate{ + CustomID: discord.CustomID("test" + ids[1]), + Title: "Test" + ids[1] + " Modal", + Components: []discord.ContainerComponent{ + discord.ActionRowComponent{ + discord.TextInputComponent{ + CustomID: "test_input", + Style: discord.TextInputStyleShort, + Label: "qwq", + Required: true, + Placeholder: "test placeholder", + Value: "uwu", + }, + }, + }, + }) + case "test1": _ = event.CreateMessage(discord.NewMessageCreateBuilder(). SetContent(data.CustomID.String()). @@ -43,7 +83,7 @@ func componentListener(event *events.ComponentInteractionEvent) { ) } - case *core.SelectMenuInteractionData: + case core.SelectMenuInteractionData: switch data.CustomID { case "test3": if err := event.DeferUpdateMessage(); err != nil { @@ -70,55 +110,6 @@ func applicationCommandListener(event *events.ApplicationCommandInteractionEvent event.Bot().Logger.Error("error on sending response: ", err) } - case "eval": - go func() { - code := *data.Options.String("code") - embed := discord.NewEmbedBuilder(). - SetColor(orange). - AddField("Status", "...", true). - AddField("Time", "...", true). - AddField("Code", "```go\n"+code+"\n```", false). - AddField("Output", "```\n...\n```", false) - _ = event.CreateMessage(discord.NewMessageCreateBuilder().SetEmbeds(embed.Build()).Build()) - - start := time.Now() - output, err := gval.Evaluate(code, map[string]interface{}{ - "bot": event.Bot(), - "event": event, - }) - - elapsed := time.Since(start) - embed.SetField(1, "Time", strconv.Itoa(int(elapsed.Milliseconds()))+"ms", true) - - if err != nil { - _, err = event.UpdateOriginalMessage(discord.NewMessageUpdateBuilder(). - SetEmbeds(embed. - SetColor(red). - SetField(0, "Status", "Failed", true). - SetField(3, "Output", "```"+err.Error()+"```", false). - Build(), - ). - Build(), - ) - if err != nil { - log.Errorf("error sending interaction response: %s", err) - } - return - } - _, err = event.UpdateOriginalMessage(discord.NewMessageUpdateBuilder(). - SetEmbeds(embed. - SetColor(green). - SetField(0, "Status", "Success", true). - SetField(3, "Output", "```"+fmt.Sprintf("%+v", output)+"```", false). - Build(), - ). - Build(), - ) - if err != nil { - log.Errorf("error sending interaction response: %s", err) - } - }() - case "say": _ = event.CreateMessage(discord.NewMessageCreateBuilder(). SetContent(*data.Options.String("message")). @@ -128,46 +119,16 @@ func applicationCommandListener(event *events.ApplicationCommandInteractionEvent ) case "test": - go func() { - _ = event.DeferCreateMessage(true) - members, err := event.Guild().RequestMembersWithQuery("", 0) - if err != nil { - _, _ = event.UpdateOriginalMessage(discord.NewMessageUpdateBuilder().SetContentf("failed to load members. error: %s", err).Build()) - return - } - _, _ = event.UpdateOriginalMessage(discord.NewMessageUpdateBuilder(). - SetContentf("loaded %d members", len(members)). - Build(), - ) - }() - - case "addrole": - user := data.Options.User("member") - role := data.Options.Role("role") - - if err := event.Bot().RestServices.GuildService().AddMemberRole(*event.GuildID, user.ID, role.ID); err == nil { - _ = event.CreateMessage(discord.NewMessageCreateBuilder().AddEmbeds( - discord.NewEmbedBuilder().SetColor(green).SetDescriptionf("Added %s to %s", role, user).Build(), - ).Build()) - } else { - _ = event.CreateMessage(discord.NewMessageCreateBuilder().AddEmbeds( - discord.NewEmbedBuilder().SetColor(red).SetDescriptionf("Failed to add %s to %s", role, user).Build(), - ).Build()) - } - - case "removerole": - user := data.Options.User("member") - role := data.Options.Role("role") - - if err := event.Bot().RestServices.GuildService().RemoveMemberRole(*event.GuildID, user.ID, role.ID); err == nil { - _ = event.CreateMessage(discord.NewMessageCreateBuilder().AddEmbeds( - discord.NewEmbedBuilder().SetColor(65280).SetDescriptionf("Removed %s from %s", role, user).Build(), - ).Build()) - } else { - _ = event.CreateMessage(discord.NewMessageCreateBuilder().AddEmbeds( - discord.NewEmbedBuilder().SetColor(16711680).SetDescriptionf("Failed to remove %s from %s", role, user).Build(), - ).Build()) - } + _ = event.CreateMessage(discord.NewMessageCreateBuilder(). + SetContent("test"). + AddActionRow( + discord.NewPrimaryButton("test1", "modal:1"), + discord.NewPrimaryButton("test2", "modal:2"), + discord.NewPrimaryButton("test3", "modal:3"), + discord.NewPrimaryButton("test4", "modal:4"), + ). + Build(), + ) } } diff --git a/core/application_command_interaction.go b/core/application_command_interaction.go index 90c16f97..36966d71 100644 --- a/core/application_command_interaction.go +++ b/core/application_command_interaction.go @@ -2,14 +2,17 @@ package core import ( "github.com/DisgoOrg/disgo/discord" + "github.com/DisgoOrg/disgo/rest" "github.com/DisgoOrg/snowflake" ) type ApplicationCommandInteractionFilter func(interaction *ApplicationCommandInteraction) bool +var _ Interaction = (*ApplicationCommandInteraction)(nil) + // ApplicationCommandInteraction represents a generic ApplicationCommandInteraction received from discord type ApplicationCommandInteraction struct { - *ModalReplyInteraction + CreateInteraction Data ApplicationCommandInteractionData } @@ -30,6 +33,10 @@ func (i ApplicationCommandInteraction) MessageCommandInteractionData() MessageCo return i.Data.(MessageCommandInteractionData) } +func (i ApplicationCommandInteraction) CreateModal(modalCreate discord.ModalCreate, opts ...rest.RequestOpt) error { + return i.Respond(discord.InteractionCallbackTypeModal, modalCreate, opts...) +} + type ApplicationCommandInteractionData interface { discord.ApplicationCommandInteractionData } diff --git a/core/component_interaction.go b/core/component_interaction.go index e9d634d0..fe7f53fb 100644 --- a/core/component_interaction.go +++ b/core/component_interaction.go @@ -7,9 +7,11 @@ import ( type ComponentInteractionFilter func(interaction *ComponentInteraction) bool +var _ Interaction = (*ComponentInteraction)(nil) + // ComponentInteraction represents a generic ComponentInteraction received from discord type ComponentInteraction struct { - *ModalReplyInteraction + CreateInteraction Data ComponentInteractionData Message *Message } @@ -31,6 +33,10 @@ func (i ComponentInteraction) UpdateMessage(messageUpdate discord.MessageUpdate, return i.Respond(discord.InteractionCallbackTypeUpdateMessage, messageUpdate, opts...) } +func (i ComponentInteraction) DeferUpdateMessage(opts ...rest.RequestOpt) error { + return i.Respond(discord.InteractionCallbackTypeDeferredUpdateMessage, nil, opts...) +} + func (i ComponentInteraction) UpdateComponent(customID discord.CustomID, component discord.InteractiveComponent, opts ...rest.RequestOpt) error { containerComponents := make([]discord.ContainerComponent, len(i.Message.Components)) for ii := range i.Message.Components { @@ -47,8 +53,8 @@ func (i ComponentInteraction) UpdateComponent(customID discord.CustomID, compone return i.UpdateMessage(discord.NewMessageUpdateBuilder().SetContainerComponents(containerComponents...).Build(), opts...) } -func (i ComponentInteraction) DeferUpdateMessage(opts ...rest.RequestOpt) error { - return i.Respond(discord.InteractionCallbackTypeDeferredUpdateMessage, nil, opts...) +func (i ComponentInteraction) CreateModal(modalCreate discord.ModalCreate, opts ...rest.RequestOpt) error { + return i.Respond(discord.InteractionCallbackTypeModal, modalCreate, opts...) } type ComponentInteractionData interface { diff --git a/core/entity_builder.go b/core/entity_builder.go index dcb9471a..d208b350 100644 --- a/core/entity_builder.go +++ b/core/entity_builder.go @@ -233,15 +233,17 @@ func (b *entityBuilderImpl) CreateInteraction(interaction discord.Interaction, c } interactionData = data } + baseInteraction := b.baseInteraction(i.BaseInteraction, c, updateCache) return &ApplicationCommandInteraction{ - ModalReplyInteraction: &ModalReplyInteraction{ReplyInteraction: &ReplyInteraction{BaseInteraction: b.baseInteraction(i.BaseInteraction, c, updateCache)}}, - Data: interactionData, + CreateInteraction: CreateInteraction{BaseInteraction: baseInteraction}, + Data: interactionData, } case discord.ComponentInteraction: + baseInteraction := b.baseInteraction(i.BaseInteraction, c, updateCache) componentInteraction := &ComponentInteraction{ - ModalReplyInteraction: &ModalReplyInteraction{ReplyInteraction: &ReplyInteraction{BaseInteraction: b.baseInteraction(i.BaseInteraction, c, updateCache)}}, - Message: b.CreateMessage(i.Message, updateCache), + CreateInteraction: CreateInteraction{BaseInteraction: baseInteraction}, + Message: b.CreateMessage(i.Message, updateCache), } switch d := i.Data.(type) { case discord.ButtonInteractionData: @@ -291,9 +293,10 @@ func (b *entityBuilderImpl) CreateInteraction(interaction discord.Interaction, c return autocompleteInteraction case discord.ModalSubmitInteraction: + baseInteraction := b.baseInteraction(i.BaseInteraction, c, updateCache) modalSubmitInteraction := &ModalSubmitInteraction{ - ReplyInteraction: &ReplyInteraction{BaseInteraction: b.baseInteraction(i.BaseInteraction, c, updateCache)}, - Data: i.Data, + CreateInteraction: CreateInteraction{BaseInteraction: baseInteraction}, + Data: i.Data, } return modalSubmitInteraction diff --git a/core/events/events_interactions.go b/core/events/events_interactions.go index 1ee51bf9..d802e9cd 100644 --- a/core/events/events_interactions.go +++ b/core/events/events_interactions.go @@ -22,7 +22,7 @@ type AutocompleteInteractionEvent struct { *core.AutocompleteInteraction } -type ModalSubmitEvent struct { +type ModalSubmitInteractionEvent struct { *GenericEvent *core.ModalSubmitInteraction -} \ No newline at end of file +} diff --git a/core/events/listener_adapter.go b/core/events/listener_adapter.go index 0a74e38c..16f25484 100644 --- a/core/events/listener_adapter.go +++ b/core/events/listener_adapter.go @@ -120,6 +120,7 @@ type ListenerAdapter struct { OnApplicationCommandInteraction func(event *ApplicationCommandInteractionEvent) OnComponentInteraction func(event *ComponentInteractionEvent) OnAutocompleteInteraction func(event *AutocompleteInteractionEvent) + OnModalSubmit func(event *ModalSubmitInteractionEvent) // Message Events OnMessageCreate func(event *MessageCreateEvent) @@ -498,6 +499,10 @@ func (l ListenerAdapter) OnEvent(event core.Event) { if listener := l.OnAutocompleteInteraction; listener != nil { listener(e) } + case *ModalSubmitInteractionEvent: + if listener := l.OnModalSubmit; listener != nil { + listener(e) + } // Message Events case *MessageCreateEvent: diff --git a/core/handlers/gateway_handler_interaction_create.go b/core/handlers/gateway_handler_interaction_create.go index d117f632..4678b350 100644 --- a/core/handlers/gateway_handler_interaction_create.go +++ b/core/handlers/gateway_handler_interaction_create.go @@ -53,6 +53,12 @@ func HandleInteraction(bot *core.Bot, sequenceNumber int, c chan<- discord.Inter AutocompleteInteraction: i, }) + case *core.ModalSubmitInteraction: + bot.EventManager.Dispatch(&events.ModalSubmitInteractionEvent{ + GenericEvent: genericEvent, + ModalSubmitInteraction: i, + }) + default: bot.Logger.Errorf("unknown interaction with type %d received", interaction.Type()) } diff --git a/core/interaction.go b/core/interaction.go index d3ff220a..c52e4728 100644 --- a/core/interaction.go +++ b/core/interaction.go @@ -72,11 +72,35 @@ func (i *BaseInteraction) Channel() MessageChannel { return nil } -type ReplyInteraction struct { +type UpdateInteraction struct { *BaseInteraction } -func (i ReplyInteraction) GetOriginalMessage(opts ...rest.RequestOpt) (*Message, error) { +func (i UpdateInteraction) UpdateMessage(messageUpdate discord.MessageUpdate, opts ...rest.RequestOpt) error { + return i.Respond(discord.InteractionCallbackTypeUpdateMessage, messageUpdate, opts...) +} + +func (i UpdateInteraction) DeferUpdateMessage(opts ...rest.RequestOpt) error { + return i.Respond(discord.InteractionCallbackTypeDeferredUpdateMessage, nil, opts...) +} + +type CreateInteraction struct { + *BaseInteraction +} + +func (i CreateInteraction) CreateMessage(messageCreate discord.MessageCreate, opts ...rest.RequestOpt) error { + return i.Respond(discord.InteractionCallbackTypeChannelMessageWithSource, messageCreate, opts...) +} + +func (i CreateInteraction) DeferCreateMessage(ephemeral bool, opts ...rest.RequestOpt) error { + var data discord.InteractionCallbackData + if ephemeral { + data = discord.MessageCreate{Flags: discord.MessageFlagEphemeral} + } + return i.Respond(discord.InteractionCallbackTypeDeferredChannelMessageWithSource, data, opts...) +} + +func (i CreateInteraction) GetOriginalMessage(opts ...rest.RequestOpt) (*Message, error) { message, err := i.Bot.RestServices.InteractionService().GetInteractionResponse(i.ApplicationID, i.Token, opts...) if err != nil { return nil, err @@ -84,7 +108,7 @@ func (i ReplyInteraction) GetOriginalMessage(opts ...rest.RequestOpt) (*Message, return i.Bot.EntityBuilder.CreateMessage(*message, CacheStrategyNoWs), nil } -func (i ReplyInteraction) UpdateOriginalMessage(messageUpdate discord.MessageUpdate, opts ...rest.RequestOpt) (*Message, error) { +func (i CreateInteraction) UpdateOriginalMessage(messageUpdate discord.MessageUpdate, opts ...rest.RequestOpt) (*Message, error) { message, err := i.Bot.RestServices.InteractionService().UpdateInteractionResponse(i.ApplicationID, i.Token, messageUpdate, opts...) if err != nil { return nil, err @@ -92,23 +116,11 @@ func (i ReplyInteraction) UpdateOriginalMessage(messageUpdate discord.MessageUpd return i.Bot.EntityBuilder.CreateMessage(*message, CacheStrategyNoWs), nil } -func (i ReplyInteraction) DeleteOriginalMessage(opts ...rest.RequestOpt) error { +func (i CreateInteraction) DeleteOriginalMessage(opts ...rest.RequestOpt) error { return i.Bot.RestServices.InteractionService().DeleteInteractionResponse(i.ApplicationID, i.Token, opts...) } -func (i ReplyInteraction) CreateMessage(messageCreate discord.MessageCreate, opts ...rest.RequestOpt) error { - return i.Respond(discord.InteractionCallbackTypeChannelMessageWithSource, messageCreate, opts...) -} - -func (i ReplyInteraction) DeferCreateMessage(ephemeral bool, opts ...rest.RequestOpt) error { - var data discord.InteractionCallbackData - if ephemeral { - data = discord.MessageCreate{Flags: discord.MessageFlagEphemeral} - } - return i.Respond(discord.InteractionCallbackTypeDeferredChannelMessageWithSource, data, opts...) -} - -func (i ReplyInteraction) GetFollowupMessage(messageID snowflake.Snowflake, opts ...rest.RequestOpt) (*Message, error) { +func (i CreateInteraction) GetFollowupMessage(messageID snowflake.Snowflake, opts ...rest.RequestOpt) (*Message, error) { message, err := i.Bot.RestServices.InteractionService().GetFollowupMessage(i.ApplicationID, i.Token, messageID, opts...) if err != nil { return nil, err @@ -116,7 +128,7 @@ func (i ReplyInteraction) GetFollowupMessage(messageID snowflake.Snowflake, opts return i.Bot.EntityBuilder.CreateMessage(*message, CacheStrategyNoWs), nil } -func (i ReplyInteraction) CreateFollowupMessage(messageCreate discord.MessageCreate, opts ...rest.RequestOpt) (*Message, error) { +func (i CreateInteraction) CreateFollowupMessage(messageCreate discord.MessageCreate, opts ...rest.RequestOpt) (*Message, error) { message, err := i.Bot.RestServices.InteractionService().CreateFollowupMessage(i.ApplicationID, i.Token, messageCreate, opts...) if err != nil { return nil, err @@ -124,7 +136,7 @@ func (i ReplyInteraction) CreateFollowupMessage(messageCreate discord.MessageCre return i.Bot.EntityBuilder.CreateMessage(*message, CacheStrategyNoWs), nil } -func (i ReplyInteraction) UpdateFollowupMessage(messageID snowflake.Snowflake, messageUpdate discord.MessageUpdate, opts ...rest.RequestOpt) (*Message, error) { +func (i CreateInteraction) UpdateFollowupMessage(messageID snowflake.Snowflake, messageUpdate discord.MessageUpdate, opts ...rest.RequestOpt) (*Message, error) { message, err := i.Bot.RestServices.InteractionService().UpdateFollowupMessage(i.ApplicationID, i.Token, messageID, messageUpdate, opts...) if err != nil { return nil, err @@ -132,14 +144,6 @@ func (i ReplyInteraction) UpdateFollowupMessage(messageID snowflake.Snowflake, m return i.Bot.EntityBuilder.CreateMessage(*message, CacheStrategyNoWs), nil } -func (i ReplyInteraction) DeleteFollowupMessage(messageID snowflake.Snowflake, opts ...rest.RequestOpt) error { +func (i CreateInteraction) DeleteFollowupMessage(messageID snowflake.Snowflake, opts ...rest.RequestOpt) error { return i.Bot.RestServices.InteractionService().DeleteFollowupMessage(i.ApplicationID, i.Token, messageID, opts...) } - -type ModalReplyInteraction struct { - *ReplyInteraction -} - -func (i ModalReplyInteraction) CreateModal(modalCreate discord.ModalCreate, opts ...rest.RequestOpt) error { - return i.Respond(discord.InteractionCallbackTypeModal, modalCreate, opts...) -} diff --git a/core/modal_submit_interaction.go b/core/modal_submit_interaction.go index 2102badd..a815c162 100644 --- a/core/modal_submit_interaction.go +++ b/core/modal_submit_interaction.go @@ -2,6 +2,7 @@ package core import ( "github.com/DisgoOrg/disgo/discord" + "github.com/DisgoOrg/disgo/rest" ) type ModalSubmitInteractionFilter func(ModalSubmitInteraction *ModalSubmitInteraction) bool @@ -9,7 +10,7 @@ type ModalSubmitInteractionFilter func(ModalSubmitInteraction *ModalSubmitIntera var _ Interaction = (*ModalSubmitInteraction)(nil) type ModalSubmitInteraction struct { - *ReplyInteraction + CreateInteraction Data discord.ModalSubmitInteractionData } @@ -17,3 +18,11 @@ func (i ModalSubmitInteraction) interaction() {} func (i ModalSubmitInteraction) Type() discord.InteractionType { return discord.InteractionTypeModalSubmit } + +func (i ModalSubmitInteraction) UpdateMessage(messageUpdate discord.MessageUpdate, opts ...rest.RequestOpt) error { + return i.Respond(discord.InteractionCallbackTypeUpdateMessage, messageUpdate, opts...) +} + +func (i ModalSubmitInteraction) DeferUpdateMessage(opts ...rest.RequestOpt) error { + return i.Respond(discord.InteractionCallbackTypeDeferredUpdateMessage, nil, opts...) +} diff --git a/discord/component.go b/discord/component.go index aaa54aa7..1db38fb8 100644 --- a/discord/component.go +++ b/discord/component.go @@ -16,7 +16,7 @@ const ( ComponentTypeActionRow = iota + 1 ComponentTypeButton ComponentTypeSelectMenu - ComponentTypeInputText + ComponentTypeTextInput ) type CustomID string @@ -77,6 +77,11 @@ func (u *UnmarshalComponent) UnmarshalJSON(data []byte) error { err = json.Unmarshal(data, &v) component = v + case ComponentTypeTextInput: + v := TextInputComponent{} + err = json.Unmarshal(data, &v) + component = v + default: err = fmt.Errorf("unkown component with type %d received", cType.Type) } @@ -497,45 +502,47 @@ func (o SelectMenuOption) WithDefault(defaultOption bool) SelectMenuOption { } var ( - _ Component = (*InputText)(nil) - _ InteractiveComponent = (*InputText)(nil) + _ Component = (*TextInputComponent)(nil) + _ InteractiveComponent = (*TextInputComponent)(nil) ) -type InputText struct { - TextStyle TextStyle `json:"text_style"` - CustomID CustomID `json:"custom_id"` - Label string `json:"label"` - Placeholder string `json:"placeholder,omitempty"` - MinLength json.NullInt `json:"min_length,omitempty"` - MaxLength json.NullInt `json:"max_length,omitempty"` +type TextInputComponent struct { + CustomID CustomID `json:"custom_id"` + Style TextInputStyle `json:"style"` + Label string `json:"label,omitempty"` + MinLength int `json:"min_length,omitempty"` + MaxLength int `json:"max_length,omitempty"` + Required bool `json:"required,omitempty"` + Placeholder string `json:"placeholder,omitempty"` + Value string `json:"value,omitempty"` } -func (t InputText) MarshalJSON() ([]byte, error) { - type inputText InputText +func (t TextInputComponent) MarshalJSON() ([]byte, error) { + type textInput TextInputComponent return json.Marshal(struct { Type ComponentType `json:"type"` - inputText + textInput }{ Type: t.Type(), - inputText: inputText(t), + textInput: textInput(t), }) } -func (t InputText) Type() ComponentType { - return ComponentTypeInputText +func (t TextInputComponent) Type() ComponentType { + return ComponentTypeTextInput } -func (t InputText) ID() CustomID { +func (t TextInputComponent) ID() CustomID { return t.CustomID } -func (t InputText) component() {} -func (t InputText) interactiveComponent() {} +func (t TextInputComponent) component() {} +func (t TextInputComponent) interactiveComponent() {} -type TextStyle int +type TextInputStyle int //goland:noinspection GoUnusedConst const ( - TextStyleShort = iota + 1 - TextStyleParagraph + TextInputStyleShort = iota + 1 + TextInputStyleParagraph ) diff --git a/discord/interaction.go b/discord/interaction.go index b625ec01..1d18d48a 100644 --- a/discord/interaction.go +++ b/discord/interaction.go @@ -435,14 +435,14 @@ func (ModalSubmitInteraction) Type() InteractionType { } type ModalSubmitInteractionData struct { - CustomID CustomID `json:"custom_id"` - Components []ModalContainerComponent `json:"components"` + CustomID CustomID `json:"custom_id"` + Components []ContainerComponent `json:"components"` } -func (d *ModalSubmitInteractionData) Unmarshal(data []byte) error { +func (d *ModalSubmitInteractionData) UnmarshalJSON(data []byte) error { type modalSubmitInteractionData ModalSubmitInteractionData var iData struct { - Components []UnmarshalModalComponent `json:"components"` + Components []UnmarshalComponent `json:"components"` modalSubmitInteractionData } @@ -453,9 +453,9 @@ func (d *ModalSubmitInteractionData) Unmarshal(data []byte) error { *d = ModalSubmitInteractionData(iData.modalSubmitInteractionData) if len(iData.Components) > 0 { - d.Components = make([]ModalContainerComponent, len(iData.Components)) + d.Components = make([]ContainerComponent, len(iData.Components)) for i := range iData.Components { - d.Components[i] = iData.Components[i].ModalComponent.(ModalContainerComponent) + d.Components[i] = iData.Components[i].Component.(ContainerComponent) } } diff --git a/discord/interaction_callback.go b/discord/interaction_callback.go index 5a1a9594..22cb0745 100644 --- a/discord/interaction_callback.go +++ b/discord/interaction_callback.go @@ -72,9 +72,9 @@ func (AutocompleteChoiceFloat) autoCompleteChoice() {} var _ InteractionCallbackData = (*ModalCreate)(nil) type ModalCreate struct { - CustomID CustomID `json:"custom_id"` - Title string `json:"title"` - Components []ModalContainerComponent `json:"components"` + CustomID CustomID `json:"custom_id"` + Title string `json:"title"` + Components []ContainerComponent `json:"components"` } func (m ModalCreate) interactionCallbackData() {} diff --git a/discord/modal_components.go b/discord/modal_components.go deleted file mode 100644 index f1e25bff..00000000 --- a/discord/modal_components.go +++ /dev/null @@ -1,126 +0,0 @@ -package discord - -import ( - "fmt" - - "github.com/DisgoOrg/disgo/json" -) - -type ModalComponent interface { - Type() ComponentType - modalComponent() -} - -type ModalContainerComponent interface { - ModalComponent - Components() []ModalInteractiveComponent - modalContainerComponent() -} - -type ModalInteractiveComponent interface { - ModalComponent - ID() CustomID - Value() string - modalInteractiveComponent() -} - -type UnmarshalModalComponent struct { - ModalComponent -} - -func (u *UnmarshalModalComponent) UnmarshalJSON(data []byte) error { - var cType struct { - Type ComponentType `json:"type"` - } - - if err := json.Unmarshal(data, &cType); err != nil { - return err - } - - var ( - modalSubmitComponent ModalComponent - err error - ) - - switch cType.Type { - case ComponentTypeActionRow: - v := ModalActionRowComponent{} - err = json.Unmarshal(data, &v) - modalSubmitComponent = v - - case ComponentTypeInputText: - v := ModalInputTextComponent{} - err = json.Unmarshal(data, &v) - modalSubmitComponent = v - - default: - err = fmt.Errorf("unkown component with type %d received", cType.Type) - } - if err != nil { - return err - } - - u.ModalComponent = modalSubmitComponent - return nil -} - -var ( - _ ModalComponent = (*ModalActionRowComponent)(nil) - _ ModalContainerComponent = (*ModalActionRowComponent)(nil) -) - -type ModalActionRowComponent []ModalInteractiveComponent - -func (c *ModalActionRowComponent) UnmarshalJSON(data []byte) error { - var actionRow struct { - Components []UnmarshalModalComponent `json:"components"` - } - - if err := json.Unmarshal(data, &actionRow); err != nil { - return err - } - - if len(actionRow.Components) > 0 { - *c = make([]ModalInteractiveComponent, len(actionRow.Components)) - for i := range actionRow.Components { - (*c)[i] = actionRow.Components[i].ModalComponent.(ModalInteractiveComponent) - } - } - - return nil -} - -func (ModalActionRowComponent) Type() ComponentType { - return ComponentTypeInputText -} - -func (c ModalActionRowComponent) Components() []ModalInteractiveComponent { - return c -} - -func (ModalActionRowComponent) modalComponent() {} -func (ModalActionRowComponent) modalContainerComponent() {} - -var ( - _ ModalComponent = (*ModalInputTextComponent)(nil) - _ ModalInteractiveComponent = (*ModalInputTextComponent)(nil) -) - -type ModalInputTextComponent struct { - CustomID CustomID `json:"custom_id"` - TextValue string `json:"value"` -} - -func (ModalInputTextComponent) Type() ComponentType { - return ComponentTypeInputText -} - -func (c ModalInputTextComponent) ID() CustomID { - return c.CustomID -} -func (c ModalInputTextComponent) Value() string { - return c.TextValue -} - -func (ModalInputTextComponent) modalComponent() {} -func (ModalInputTextComponent) modalInteractiveComponent() {} From 7778f66b3c32262ff003a1220ed5d725475d5f00 Mon Sep 17 00:00:00 2001 From: TopiSenpai Date: Fri, 11 Feb 2022 15:17:31 +0100 Subject: [PATCH 5/5] add ModalCreateBuilder --- discord/interaction_callback.go | 10 ----- discord/modal_create.go | 73 +++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 discord/modal_create.go diff --git a/discord/interaction_callback.go b/discord/interaction_callback.go index 22cb0745..6223d456 100644 --- a/discord/interaction_callback.go +++ b/discord/interaction_callback.go @@ -68,13 +68,3 @@ type AutocompleteChoiceFloat struct { } func (AutocompleteChoiceFloat) autoCompleteChoice() {} - -var _ InteractionCallbackData = (*ModalCreate)(nil) - -type ModalCreate struct { - CustomID CustomID `json:"custom_id"` - Title string `json:"title"` - Components []ContainerComponent `json:"components"` -} - -func (m ModalCreate) interactionCallbackData() {} diff --git a/discord/modal_create.go b/discord/modal_create.go new file mode 100644 index 00000000..9e224a4a --- /dev/null +++ b/discord/modal_create.go @@ -0,0 +1,73 @@ +package discord + +var _ InteractionCallbackData = (*ModalCreate)(nil) + +type ModalCreate struct { + CustomID CustomID `json:"custom_id"` + Title string `json:"title"` + Components []ContainerComponent `json:"components"` +} + +func (ModalCreate) interactionCallbackData() {} + +// NewModalCreateBuilder creates a new ModalCreateBuilder to be built later +//goland:noinspection GoUnusedExportedFunction +func NewModalCreateBuilder() *ModalCreateBuilder { + return &ModalCreateBuilder{} +} + +type ModalCreateBuilder struct { + ModalCreate +} + +// SetCustomID sets the CustomID of the ModalCreate +func (b *ModalCreateBuilder) SetCustomID(customID CustomID) *ModalCreateBuilder { + b.CustomID = customID + return b +} + +// SetTitle sets the title of the ModalCreate +func (b *ModalCreateBuilder) SetTitle(title string) *ModalCreateBuilder { + b.Title = title + return b +} + +// SetContainerComponents sets the discord.ContainerComponent(s) of the ModalCreate +func (b *ModalCreateBuilder) SetContainerComponents(containerComponents ...ContainerComponent) *ModalCreateBuilder { + b.Components = containerComponents + return b +} + +// SetContainerComponent sets the provided discord.InteractiveComponent at the index of discord.InteractiveComponent(s) +func (b *ModalCreateBuilder) SetContainerComponent(i int, container ContainerComponent) *ModalCreateBuilder { + if len(b.Components) > i { + b.Components[i] = container + } + return b +} + +// AddActionRow adds a new discord.ActionRowComponent with the provided discord.InteractiveComponent(s) to the ModalCreate +func (b *ModalCreateBuilder) AddActionRow(components ...InteractiveComponent) *ModalCreateBuilder { + b.Components = append(b.Components, ActionRowComponent(components)) + return b +} + +// AddContainerComponents adds the discord.ContainerComponent(s) to the ModalCreate +func (b *ModalCreateBuilder) AddContainerComponents(containers ...ContainerComponent) *ModalCreateBuilder { + b.Components = append(b.Components, containers...) + return b +} + +// RemoveContainerComponent removes a discord.ActionRowComponent from the ModalCreate +func (b *ModalCreateBuilder) RemoveContainerComponent(i int) *ModalCreateBuilder { + if len(b.Components) > i { + b.Components = append(b.Components[:i], b.Components[i+1:]...) + } + return b +} + +// ClearContainerComponents removes all the discord.ContainerComponent(s) of the ModalCreate +func (b *ModalCreateBuilder) ClearContainerComponents() *ModalCreateBuilder { + b.Components = []ContainerComponent{} + return b +}