Interactions: the Buttons (#933)
* Interactions: buttons * Doc fix * Gofmt fix * Fix typo * Remaking interaction data into interface * Godoc fix * Gofmt fix * Godoc fix * InteractionData helper functions and some fixes in slash commands example * Fix components example * Yet another fix of components example * Fix interaction unmarshaling * Gofmt fix * Godoc fix * Gofmt fix * Corrected naming and docs * Rolled back API version * Requested fixes * Added support of components to webhook and regular messages * Fix components unmarshaling * Godoc fix * Requested fixes * Fixed unmarshaling issues * Components example: cleanup * Added components tracking to state * Requested fixes * Renaming fix * Remove more named returns * Minor English fixes Co-authored-by: Carson Hoffman <c@rsonhoffman.com>
This commit is contained in:
parent
e72c457cb4
commit
421e149650
8 changed files with 464 additions and 34 deletions
149
components.go
Normal file
149
components.go
Normal file
|
@ -0,0 +1,149 @@
|
|||
package discordgo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// ComponentType is type of component.
|
||||
type ComponentType uint
|
||||
|
||||
// MessageComponent types.
|
||||
const (
|
||||
ActionsRowComponent ComponentType = 1
|
||||
ButtonComponent ComponentType = 2
|
||||
)
|
||||
|
||||
// MessageComponent is a base interface for all message components.
|
||||
type MessageComponent interface {
|
||||
json.Marshaler
|
||||
Type() ComponentType
|
||||
}
|
||||
|
||||
type unmarshalableMessageComponent struct {
|
||||
MessageComponent
|
||||
}
|
||||
|
||||
// UnmarshalJSON is a helper function to unmarshal MessageComponent object.
|
||||
func (umc *unmarshalableMessageComponent) UnmarshalJSON(src []byte) error {
|
||||
var v struct {
|
||||
Type ComponentType `json:"type"`
|
||||
}
|
||||
err := json.Unmarshal(src, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var data MessageComponent
|
||||
switch v.Type {
|
||||
case ActionsRowComponent:
|
||||
v := ActionsRow{}
|
||||
err = json.Unmarshal(src, &v)
|
||||
data = v
|
||||
case ButtonComponent:
|
||||
v := Button{}
|
||||
err = json.Unmarshal(src, &v)
|
||||
data = v
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
umc.MessageComponent = data
|
||||
return err
|
||||
}
|
||||
|
||||
// ActionsRow is a container for components within one row.
|
||||
type ActionsRow struct {
|
||||
Components []MessageComponent `json:"components"`
|
||||
}
|
||||
|
||||
// MarshalJSON is a method for marshaling ActionsRow to a JSON object.
|
||||
func (r ActionsRow) MarshalJSON() ([]byte, error) {
|
||||
type actionsRow ActionsRow
|
||||
|
||||
return json.Marshal(struct {
|
||||
actionsRow
|
||||
Type ComponentType `json:"type"`
|
||||
}{
|
||||
actionsRow: actionsRow(r),
|
||||
Type: r.Type(),
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON is a helper function to unmarshal Actions Row.
|
||||
func (r *ActionsRow) UnmarshalJSON(data []byte) error {
|
||||
var v struct {
|
||||
RawComponents []unmarshalableMessageComponent `json:"components"`
|
||||
}
|
||||
err := json.Unmarshal(data, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Components = make([]MessageComponent, len(v.RawComponents))
|
||||
for i, v := range v.RawComponents {
|
||||
r.Components[i] = v.MessageComponent
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Type is a method to get the type of a component.
|
||||
func (r ActionsRow) Type() ComponentType {
|
||||
return ActionsRowComponent
|
||||
}
|
||||
|
||||
// ButtonStyle is style of button.
|
||||
type ButtonStyle uint
|
||||
|
||||
// Button styles.
|
||||
const (
|
||||
// PrimaryButton is a button with blurple color.
|
||||
PrimaryButton ButtonStyle = 1
|
||||
// SecondaryButton is a button with grey color.
|
||||
SecondaryButton ButtonStyle = 2
|
||||
// SuccessButton is a button with green color.
|
||||
SuccessButton ButtonStyle = 3
|
||||
// DangerButton is a button with red color.
|
||||
DangerButton ButtonStyle = 4
|
||||
// LinkButton is a special type of button which navigates to a URL. Has grey color.
|
||||
LinkButton ButtonStyle = 5
|
||||
)
|
||||
|
||||
// ButtonEmoji represents button emoji, if it does have one.
|
||||
type ButtonEmoji struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
Animated bool `json:"animated,omitempty"`
|
||||
}
|
||||
|
||||
// Button represents button component.
|
||||
type Button struct {
|
||||
Label string `json:"label"`
|
||||
Style ButtonStyle `json:"style"`
|
||||
Disabled bool `json:"disabled"`
|
||||
Emoji ButtonEmoji `json:"emoji"`
|
||||
|
||||
// NOTE: Only button with LinkButton style can have link. Also, URL is mutually exclusive with CustomID.
|
||||
URL string `json:"url,omitempty"`
|
||||
CustomID string `json:"custom_id,omitempty"`
|
||||
}
|
||||
|
||||
// MarshalJSON is a method for marshaling Button to a JSON object.
|
||||
func (b Button) MarshalJSON() ([]byte, error) {
|
||||
type button Button
|
||||
|
||||
if b.Style == 0 {
|
||||
b.Style = PrimaryButton
|
||||
}
|
||||
|
||||
return json.Marshal(struct {
|
||||
button
|
||||
Type ComponentType `json:"type"`
|
||||
}{
|
||||
button: button(b),
|
||||
Type: b.Type(),
|
||||
})
|
||||
}
|
||||
|
||||
// Type is a method to get the type of a component.
|
||||
func (b Button) Type() ComponentType {
|
||||
return ButtonComponent
|
||||
}
|
20
events.go
20
events.go
|
@ -162,6 +162,11 @@ type MessageCreate struct {
|
|||
*Message
|
||||
}
|
||||
|
||||
// UnmarshalJSON is a helper function to unmarshal MessageCreate object.
|
||||
func (m *MessageCreate) UnmarshalJSON(b []byte) error {
|
||||
return json.Unmarshal(b, &m.Message)
|
||||
}
|
||||
|
||||
// MessageUpdate is the data for a MessageUpdate event.
|
||||
type MessageUpdate struct {
|
||||
*Message
|
||||
|
@ -169,12 +174,22 @@ type MessageUpdate struct {
|
|||
BeforeUpdate *Message `json:"-"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON is a helper function to unmarshal MessageUpdate object.
|
||||
func (m *MessageUpdate) UnmarshalJSON(b []byte) error {
|
||||
return json.Unmarshal(b, &m.Message)
|
||||
}
|
||||
|
||||
// MessageDelete is the data for a MessageDelete event.
|
||||
type MessageDelete struct {
|
||||
*Message
|
||||
BeforeDelete *Message `json:"-"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON is a helper function to unmarshal MessageDelete object.
|
||||
func (m *MessageDelete) UnmarshalJSON(b []byte) error {
|
||||
return json.Unmarshal(b, &m.Message)
|
||||
}
|
||||
|
||||
// MessageReactionAdd is the data for a MessageReactionAdd event.
|
||||
type MessageReactionAdd struct {
|
||||
*MessageReaction
|
||||
|
@ -272,3 +287,8 @@ type WebhooksUpdate struct {
|
|||
type InteractionCreate struct {
|
||||
*Interaction
|
||||
}
|
||||
|
||||
// UnmarshalJSON is a helper function to unmarshal Interaction object.
|
||||
func (i *InteractionCreate) UnmarshalJSON(b []byte) error {
|
||||
return json.Unmarshal(b, &i.Interaction)
|
||||
}
|
||||
|
|
151
examples/components/main.go
Normal file
151
examples/components/main.go
Normal file
|
@ -0,0 +1,151 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
// Bot parameters
|
||||
var (
|
||||
GuildID = flag.String("guild", "", "Test guild ID")
|
||||
BotToken = flag.String("token", "", "Bot access token")
|
||||
AppID = flag.String("app", "", "Application ID")
|
||||
)
|
||||
|
||||
var s *discordgo.Session
|
||||
|
||||
func init() { flag.Parse() }
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
s, err = discordgo.New("Bot " + *BotToken)
|
||||
if err != nil {
|
||||
log.Fatalf("Invalid bot parameters: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
s.AddHandler(func(s *discordgo.Session, r *discordgo.Ready) {
|
||||
log.Println("Bot is up!")
|
||||
})
|
||||
// Buttons are part of interactions, so we register InteractionCreate handler
|
||||
s.AddHandler(func(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
if i.Type == discordgo.InteractionApplicationCommand {
|
||||
if i.ApplicationCommandData().Name == "feedback" {
|
||||
err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: "Are you satisfied with Buttons?",
|
||||
// Buttons and other components are specified in Components field.
|
||||
Components: []discordgo.MessageComponent{
|
||||
// ActionRow is a container of all buttons within the same row.
|
||||
discordgo.ActionsRow{
|
||||
Components: []discordgo.MessageComponent{
|
||||
discordgo.Button{
|
||||
Label: "Yes",
|
||||
Style: discordgo.SuccessButton,
|
||||
Disabled: false,
|
||||
CustomID: "yes_btn",
|
||||
},
|
||||
discordgo.Button{
|
||||
Label: "No",
|
||||
Style: discordgo.DangerButton,
|
||||
Disabled: false,
|
||||
CustomID: "no_btn",
|
||||
},
|
||||
discordgo.Button{
|
||||
Label: "I don't know",
|
||||
Style: discordgo.LinkButton,
|
||||
Disabled: false,
|
||||
// Link buttons don't require CustomID and do not trigger the gateway/HTTP event
|
||||
URL: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||
Emoji: discordgo.ButtonEmoji{
|
||||
Name: "🤷",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// The message may have multiple actions rows.
|
||||
discordgo.ActionsRow{
|
||||
Components: []discordgo.MessageComponent{
|
||||
discordgo.Button{
|
||||
Label: "Discord Developers server",
|
||||
Style: discordgo.LinkButton,
|
||||
Disabled: false,
|
||||
URL: "https://discord.gg/discord-developers",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
// Type for button press will be always InteractionButton (3)
|
||||
if i.Type != discordgo.InteractionMessageComponent {
|
||||
return
|
||||
}
|
||||
|
||||
content := "Thanks for your feedback "
|
||||
|
||||
// CustomID field contains the same id as when was sent. It's used to identify the which button was clicked.
|
||||
switch i.MessageComponentData().CustomID {
|
||||
case "yes_btn":
|
||||
content += "(yes)"
|
||||
case "no_btn":
|
||||
content += "(no)"
|
||||
}
|
||||
|
||||
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
// Buttons also may update the message which to which they are attached.
|
||||
// Or may just acknowledge (InteractionResponseDeferredMessageUpdate) that the event was received and not update the message.
|
||||
// To update it later you need to use interaction response edit endpoint.
|
||||
Type: discordgo.InteractionResponseUpdateMessage,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: content,
|
||||
Components: []discordgo.MessageComponent{
|
||||
discordgo.ActionsRow{
|
||||
Components: []discordgo.MessageComponent{
|
||||
discordgo.Button{
|
||||
Label: "Our sponsor",
|
||||
Style: discordgo.LinkButton,
|
||||
Disabled: false,
|
||||
URL: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||
Emoji: discordgo.ButtonEmoji{
|
||||
Name: "💠",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
_, err := s.ApplicationCommandCreate(*AppID, *GuildID, &discordgo.ApplicationCommand{
|
||||
Name: "feedback",
|
||||
Description: "Give your feedback",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Cannot create slash command: %v", err)
|
||||
}
|
||||
|
||||
err = s.Open()
|
||||
if err != nil {
|
||||
log.Fatalf("Cannot open the session: %v", err)
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
stop := make(chan os.Signal, 1)
|
||||
signal.Notify(stop, os.Interrupt)
|
||||
<-stop
|
||||
log.Println("Graceful shutdown")
|
||||
}
|
|
@ -155,7 +155,7 @@ var (
|
|||
"basic-command": func(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionApplicationCommandResponseData{
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: "Hey there! Congratulations, you just executed your first slash command",
|
||||
},
|
||||
})
|
||||
|
@ -166,9 +166,9 @@ var (
|
|||
// Also, as you can see, here is used utility functions to convert the value
|
||||
// to particular type. Yeah, you can use just switch type,
|
||||
// but this is much simpler
|
||||
i.Data.Options[0].StringValue(),
|
||||
i.Data.Options[1].IntValue(),
|
||||
i.Data.Options[2].BoolValue(),
|
||||
i.ApplicationCommandData().Options[0].StringValue(),
|
||||
i.ApplicationCommandData().Options[1].IntValue(),
|
||||
i.ApplicationCommandData().Options[2].BoolValue(),
|
||||
}
|
||||
msgformat :=
|
||||
` Now you just learned how to use command options. Take a look to the value of which you've just entered:
|
||||
|
@ -176,22 +176,22 @@ var (
|
|||
> integer_option: %d
|
||||
> bool_option: %v
|
||||
`
|
||||
if len(i.Data.Options) >= 4 {
|
||||
margs = append(margs, i.Data.Options[3].ChannelValue(nil).ID)
|
||||
if len(i.ApplicationCommandData().Options) >= 4 {
|
||||
margs = append(margs, i.ApplicationCommandData().Options[3].ChannelValue(nil).ID)
|
||||
msgformat += "> channel-option: <#%s>\n"
|
||||
}
|
||||
if len(i.Data.Options) >= 5 {
|
||||
margs = append(margs, i.Data.Options[4].UserValue(nil).ID)
|
||||
if len(i.ApplicationCommandData().Options) >= 5 {
|
||||
margs = append(margs, i.ApplicationCommandData().Options[4].UserValue(nil).ID)
|
||||
msgformat += "> user-option: <@%s>\n"
|
||||
}
|
||||
if len(i.Data.Options) >= 6 {
|
||||
margs = append(margs, i.Data.Options[5].RoleValue(nil, "").ID)
|
||||
if len(i.ApplicationCommandData().Options) >= 6 {
|
||||
margs = append(margs, i.ApplicationCommandData().Options[5].RoleValue(nil, "").ID)
|
||||
msgformat += "> role-option: <@&%s>\n"
|
||||
}
|
||||
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
// Ignore type for now, we'll discuss them in "responses" part
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionApplicationCommandResponseData{
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: fmt.Sprintf(
|
||||
msgformat,
|
||||
margs...,
|
||||
|
@ -204,15 +204,15 @@ var (
|
|||
|
||||
// As you can see, the name of subcommand (nested, top-level) or subcommand group
|
||||
// is provided through arguments.
|
||||
switch i.Data.Options[0].Name {
|
||||
switch i.ApplicationCommandData().Options[0].Name {
|
||||
case "subcmd":
|
||||
content =
|
||||
"The top-level subcommand is executed. Now try to execute the nested one."
|
||||
default:
|
||||
if i.Data.Options[0].Name != "scmd-grp" {
|
||||
if i.ApplicationCommandData().Options[0].Name != "scmd-grp" {
|
||||
return
|
||||
}
|
||||
switch i.Data.Options[0].Options[0].Name {
|
||||
switch i.ApplicationCommandData().Options[0].Options[0].Name {
|
||||
case "nst-subcmd":
|
||||
content = "Nice, now you know how to execute nested commands too"
|
||||
default:
|
||||
|
@ -223,7 +223,7 @@ var (
|
|||
}
|
||||
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionApplicationCommandResponseData{
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: content,
|
||||
},
|
||||
})
|
||||
|
@ -238,7 +238,7 @@ var (
|
|||
content := ""
|
||||
// As you can see, the response type names used here are pretty self-explanatory,
|
||||
// but for those who want more information see the official documentation
|
||||
switch i.Data.Options[0].IntValue() {
|
||||
switch i.ApplicationCommandData().Options[0].IntValue() {
|
||||
case int64(discordgo.InteractionResponseChannelMessageWithSource):
|
||||
content =
|
||||
"You just responded to an interaction, sent a message and showed the original one. " +
|
||||
|
@ -247,7 +247,7 @@ var (
|
|||
"\nAlso... you can edit your response, wait 5 seconds and this message will be changed"
|
||||
default:
|
||||
err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseType(i.Data.Options[0].IntValue()),
|
||||
Type: discordgo.InteractionResponseType(i.ApplicationCommandData().Options[0].IntValue()),
|
||||
})
|
||||
if err != nil {
|
||||
s.FollowupMessageCreate(s.State.User.ID, i.Interaction, true, &discordgo.WebhookParams{
|
||||
|
@ -258,8 +258,8 @@ var (
|
|||
}
|
||||
|
||||
err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseType(i.Data.Options[0].IntValue()),
|
||||
Data: &discordgo.InteractionApplicationCommandResponseData{
|
||||
Type: discordgo.InteractionResponseType(i.ApplicationCommandData().Options[0].IntValue()),
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: content,
|
||||
},
|
||||
})
|
||||
|
@ -292,7 +292,7 @@ var (
|
|||
|
||||
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionApplicationCommandResponseData{
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
// Note: this isn't documented, but you can use that if you want to.
|
||||
// This flag just allows you to create messages visible only for the caller of the command
|
||||
// (user who triggered the command)
|
||||
|
@ -330,7 +330,7 @@ var (
|
|||
|
||||
func init() {
|
||||
s.AddHandler(func(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
if h, ok := commandHandlers[i.Data.Name]; ok {
|
||||
if h, ok := commandHandlers[i.ApplicationCommandData().Name]; ok {
|
||||
h(s, i)
|
||||
}
|
||||
})
|
||||
|
|
106
interactions.go
106
interactions.go
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"crypto/ed25519"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
@ -65,15 +66,20 @@ type InteractionType uint8
|
|||
const (
|
||||
InteractionPing InteractionType = 1
|
||||
InteractionApplicationCommand InteractionType = 2
|
||||
InteractionMessageComponent InteractionType = 3
|
||||
)
|
||||
|
||||
// Interaction represents an interaction event created via a slash command.
|
||||
// Interaction represents data of an interaction.
|
||||
type Interaction struct {
|
||||
ID string `json:"id"`
|
||||
Type InteractionType `json:"type"`
|
||||
Data ApplicationCommandInteractionData `json:"data"`
|
||||
GuildID string `json:"guild_id"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
ID string `json:"id"`
|
||||
Type InteractionType `json:"type"`
|
||||
Data InteractionData `json:"-"`
|
||||
GuildID string `json:"guild_id"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
|
||||
// The message on which interaction was used.
|
||||
// NOTE: this field is only filled when a button click triggered the interaction. Otherwise it will be nil.
|
||||
Message *Message `json:"message"`
|
||||
|
||||
// The member who invoked this interaction.
|
||||
// NOTE: this field is only filled when the slash command was invoked in a guild;
|
||||
|
@ -90,7 +96,60 @@ type Interaction struct {
|
|||
Version int `json:"version"`
|
||||
}
|
||||
|
||||
// ApplicationCommandInteractionData contains data received in an interaction event.
|
||||
type interaction Interaction
|
||||
|
||||
type rawInteraction struct {
|
||||
interaction
|
||||
Data json.RawMessage `json:"data"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON is a method for unmarshalling JSON object to Interaction.
|
||||
func (i *Interaction) UnmarshalJSON(raw []byte) error {
|
||||
var tmp rawInteraction
|
||||
err := json.Unmarshal(raw, &tmp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*i = Interaction(tmp.interaction)
|
||||
|
||||
switch tmp.Type {
|
||||
case InteractionApplicationCommand:
|
||||
v := ApplicationCommandInteractionData{}
|
||||
err = json.Unmarshal(tmp.Data, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.Data = v
|
||||
case InteractionMessageComponent:
|
||||
v := MessageComponentInteractionData{}
|
||||
err = json.Unmarshal(tmp.Data, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.Data = v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MessageComponentData is helper function to assert the inner InteractionData to MessageComponentInteractionData.
|
||||
// Make sure to check that the Type of the interaction is InteractionMessageComponent before calling.
|
||||
func (i Interaction) MessageComponentData() (data MessageComponentInteractionData) {
|
||||
return i.Data.(MessageComponentInteractionData)
|
||||
}
|
||||
|
||||
// ApplicationCommandData is helper function to assert the inner InteractionData to ApplicationCommandInteractionData.
|
||||
// Make sure to check that the Type of the interaction is InteractionApplicationCommand before calling.
|
||||
func (i Interaction) ApplicationCommandData() (data ApplicationCommandInteractionData) {
|
||||
return i.Data.(ApplicationCommandInteractionData)
|
||||
}
|
||||
|
||||
// InteractionData is a common interface for all types of interaction data.
|
||||
type InteractionData interface {
|
||||
Type() InteractionType
|
||||
}
|
||||
|
||||
// ApplicationCommandInteractionData contains the data of application command interaction.
|
||||
type ApplicationCommandInteractionData struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
@ -108,6 +167,22 @@ type ApplicationCommandInteractionDataResolved struct {
|
|||
Channels map[string]*Channel `json:"channels"`
|
||||
}
|
||||
|
||||
// Type returns the type of interaction data.
|
||||
func (ApplicationCommandInteractionData) Type() InteractionType {
|
||||
return InteractionApplicationCommand
|
||||
}
|
||||
|
||||
// MessageComponentInteractionData contains the data of message component interaction.
|
||||
type MessageComponentInteractionData struct {
|
||||
CustomID string `json:"custom_id"`
|
||||
ComponentType ComponentType `json:"component_type"`
|
||||
}
|
||||
|
||||
// Type returns the type of interaction data.
|
||||
func (MessageComponentInteractionData) Type() InteractionType {
|
||||
return InteractionMessageComponent
|
||||
}
|
||||
|
||||
// ApplicationCommandInteractionDataOption represents an option of a slash command.
|
||||
type ApplicationCommandInteractionDataOption struct {
|
||||
Name string `json:"name"`
|
||||
|
@ -243,18 +318,23 @@ const (
|
|||
InteractionResponseChannelMessageWithSource InteractionResponseType = 4
|
||||
// InteractionResponseDeferredChannelMessageWithSource acknowledges that the event was received, and that a follow-up will come later.
|
||||
InteractionResponseDeferredChannelMessageWithSource InteractionResponseType = 5
|
||||
// InteractionResponseDeferredMessageUpdate acknowledges that the message component interaction event was received, and message will be updated later.
|
||||
InteractionResponseDeferredMessageUpdate InteractionResponseType = 6
|
||||
// InteractionResponseUpdateMessage is for updating the message to which message component was attached.
|
||||
InteractionResponseUpdateMessage InteractionResponseType = 7
|
||||
)
|
||||
|
||||
// InteractionResponse represents a response for an interaction event.
|
||||
type InteractionResponse struct {
|
||||
Type InteractionResponseType `json:"type,omitempty"`
|
||||
Data *InteractionApplicationCommandResponseData `json:"data,omitempty"`
|
||||
Type InteractionResponseType `json:"type,omitempty"`
|
||||
Data *InteractionResponseData `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// InteractionApplicationCommandResponseData is response data for a slash command interaction.
|
||||
type InteractionApplicationCommandResponseData struct {
|
||||
TTS bool `json:"tts,omitempty"`
|
||||
Content string `json:"content,omitempty"`
|
||||
// InteractionResponseData is response data for an interaction.
|
||||
type InteractionResponseData struct {
|
||||
TTS bool `json:"tts"`
|
||||
Content string `json:"content"`
|
||||
Components []MessageComponent `json:"components"`
|
||||
Embeds []*MessageEmbed `json:"embeds,omitempty"`
|
||||
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
|
||||
|
||||
|
|
25
message.go
25
message.go
|
@ -10,6 +10,7 @@
|
|||
package discordgo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
@ -80,6 +81,9 @@ type Message struct {
|
|||
// A list of attachments present in the message.
|
||||
Attachments []*MessageAttachment `json:"attachments"`
|
||||
|
||||
// A list of components attached to the message.
|
||||
Components []MessageComponent `json:"-"`
|
||||
|
||||
// A list of embeds present in the message. Multiple
|
||||
// embeds can currently only be sent by webhooks.
|
||||
Embeds []*MessageEmbed `json:"embeds"`
|
||||
|
@ -125,6 +129,25 @@ type Message struct {
|
|||
Flags MessageFlags `json:"flags"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON is a helper function to unmarshal the Message.
|
||||
func (m *Message) UnmarshalJSON(data []byte) error {
|
||||
type message Message
|
||||
var v struct {
|
||||
message
|
||||
RawComponents []unmarshalableMessageComponent `json:"components"`
|
||||
}
|
||||
err := json.Unmarshal(data, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*m = Message(v.message)
|
||||
m.Components = make([]MessageComponent, len(v.RawComponents))
|
||||
for i, v := range v.RawComponents {
|
||||
m.Components[i] = v.MessageComponent
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// GetCustomEmojis pulls out all the custom (Non-unicode) emojis from a message and returns a Slice of the Emoji struct.
|
||||
func (m *Message) GetCustomEmojis() []*Emoji {
|
||||
var toReturn []*Emoji
|
||||
|
@ -168,6 +191,7 @@ type MessageSend struct {
|
|||
Content string `json:"content,omitempty"`
|
||||
Embed *MessageEmbed `json:"embed,omitempty"`
|
||||
TTS bool `json:"tts"`
|
||||
Components []MessageComponent `json:"components"`
|
||||
Files []*File `json:"-"`
|
||||
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
|
||||
Reference *MessageReference `json:"message_reference,omitempty"`
|
||||
|
@ -180,6 +204,7 @@ type MessageSend struct {
|
|||
// is also where you should get the instance from.
|
||||
type MessageEdit struct {
|
||||
Content *string `json:"content,omitempty"`
|
||||
Components []MessageComponent `json:"components"`
|
||||
Embed *MessageEmbed `json:"embed,omitempty"`
|
||||
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
|
||||
|
||||
|
|
3
state.go
3
state.go
|
@ -655,6 +655,9 @@ func (s *State) MessageAdd(message *Message) error {
|
|||
if message.Author != nil {
|
||||
m.Author = message.Author
|
||||
}
|
||||
if message.Components != nil {
|
||||
m.Components = message.Components
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ type WebhookParams struct {
|
|||
AvatarURL string `json:"avatar_url,omitempty"`
|
||||
TTS bool `json:"tts,omitempty"`
|
||||
Files []*File `json:"-"`
|
||||
Components []MessageComponent `json:"components"`
|
||||
Embeds []*MessageEmbed `json:"embeds,omitempty"`
|
||||
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
|
||||
}
|
||||
|
@ -39,6 +40,7 @@ type WebhookParams struct {
|
|||
// WebhookEdit stores data for editing of a webhook message.
|
||||
type WebhookEdit struct {
|
||||
Content string `json:"content,omitempty"`
|
||||
Components []MessageComponent `json:"components"`
|
||||
Embeds []*MessageEmbed `json:"embeds,omitempty"`
|
||||
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue