From d7f34491fafeb93020e9788f0485fbf1873faa45 Mon Sep 17 00:00:00 2001 From: FedorLap2006 Date: Sat, 12 Dec 2020 02:04:36 +0300 Subject: [PATCH 01/28] UnknownBan error code addition --- structs.go | 1 + 1 file changed, 1 insertion(+) diff --git a/structs.go b/structs.go index 91b500235..13eb99342 100644 --- a/structs.go +++ b/structs.go @@ -1253,6 +1253,7 @@ const ( ErrCodeUnknownUser = 10013 ErrCodeUnknownEmoji = 10014 ErrCodeUnknownWebhook = 10015 + ErrCodeUnknownBan = 10026 ErrCodeBotsCannotUseEndpoint = 20001 ErrCodeOnlyBotsCanUseEndpoint = 20002 From 356455eba0c7111653af56856d051c0cac507051 Mon Sep 17 00:00:00 2001 From: FedorLap2006 Date: Sat, 12 Dec 2020 03:05:33 +0300 Subject: [PATCH 02/28] GuildBan method implementation --- restapi.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/restapi.go b/restapi.go index b08871161..fcdd9d780 100644 --- a/restapi.go +++ b/restapi.go @@ -693,6 +693,18 @@ func (s *Session) GuildBanCreate(guildID, userID string, days int) (err error) { return s.GuildBanCreateWithReason(guildID, userID, "", days) } +// GuildBan finds ban by given guild and user id and returns GuildBan structure +func (s *Session) GuildBan(guildID, userID string) (st *GuildBan, err error) { + body, err := s.RequestWithBucketID("GET", EndpointGuildBan(guildID, userID), nil, EndpointGuildBan(guildID, userID)) + if err != nil { + return + } + + err = unmarshal(body, &st) + + return +} + // GuildBanCreateWithReason bans the given user from the given guild also providing a reaso. // guildID : The ID of a Guild. // userID : The ID of a User From d703a29ecae47a702087be219d85f4fcb500721d Mon Sep 17 00:00:00 2001 From: FedorLap2006 Date: Sat, 12 Dec 2020 21:33:26 +0300 Subject: [PATCH 03/28] Gofmt fix Gofmt fix --- restapi.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/restapi.go b/restapi.go index fcdd9d780..fdbcc35bc 100644 --- a/restapi.go +++ b/restapi.go @@ -693,8 +693,9 @@ func (s *Session) GuildBanCreate(guildID, userID string, days int) (err error) { return s.GuildBanCreateWithReason(guildID, userID, "", days) } -// GuildBan finds ban by given guild and user id and returns GuildBan structure +// GuildBan finds ban by given guild and user id and returns GuildBan structure func (s *Session) GuildBan(guildID, userID string) (st *GuildBan, err error) { + body, err := s.RequestWithBucketID("GET", EndpointGuildBan(guildID, userID), nil, EndpointGuildBan(guildID, userID)) if err != nil { return From 4aeca90cf87c09c18779c3896c24fd388c33005c Mon Sep 17 00:00:00 2001 From: FedorLap2006 Date: Thu, 24 Dec 2020 03:36:07 +0300 Subject: [PATCH 04/28] Interactions: application commands basic API and gateway integration --- endpoints.go | 29 ++++++-- eventhandlers.go | 24 ++++++ events.go | 5 ++ examples/slash-commands/main.go | 74 +++++++++++++++++++ interactions.go | 125 ++++++++++++++++++++++++++++++++ oauth2.go | 14 ++-- restapi.go | 62 ++++++++++++++++ 7 files changed, 320 insertions(+), 13 deletions(-) create mode 100644 examples/slash-commands/main.go create mode 100644 interactions.go diff --git a/endpoints.go b/endpoints.go index 88663fe8f..7789d85a5 100644 --- a/endpoints.go +++ b/endpoints.go @@ -14,7 +14,7 @@ package discordgo import "strconv" // APIVersion is the Discord API version used for the REST and Websocket API. -var APIVersion = "6" +var APIVersion = "8" // Known Discord API Endpoints. var ( @@ -131,6 +131,21 @@ var ( return EndpointMessageReactions(cID, mID, eID) + "/" + uID } + EndpointApplicationGlobalCommands = func(aID string) string { + return EndpointApplication(aID) + "/" + "commands" + } + + EndpointApplicationGlobalCommand = func(aID, cID string) string { + return EndpointApplicationGlobalCommands(aID) + "/" + cID + } + + EndpointApplicationGuildCommands = func(aID, gID string) string { + return EndpointApplication(aID) + "/" + "guilds" + "/" + gID + "/" + "commands" + } + EndpointApplicationGuildCommand = func(aID, gID, cID string) string { + return EndpointApplicationGuildCommands(aID, gID) + "/" + cID + } + EndpointRelationships = func() string { return EndpointUsers + "@me" + "/relationships" } EndpointRelationship = func(uID string) string { return EndpointRelationships() + "/" + uID } EndpointRelationshipsMutual = func(uID string) string { return EndpointUsers + uID + "/relationships" } @@ -144,9 +159,11 @@ var ( EndpointEmoji = func(eID string) string { return EndpointCDN + "emojis/" + eID + ".png" } EndpointEmojiAnimated = func(eID string) string { return EndpointCDN + "emojis/" + eID + ".gif" } - EndpointOauth2 = EndpointAPI + "oauth2/" - EndpointApplications = EndpointOauth2 + "applications" - EndpointApplication = func(aID string) string { return EndpointApplications + "/" + aID } - EndpointApplicationsBot = func(aID string) string { return EndpointApplications + "/" + aID + "/bot" } - EndpointApplicationAssets = func(aID string) string { return EndpointApplications + "/" + aID + "/assets" } + EndpointOauth2 = EndpointAPI + "oauth2/" + EndpointOauth2Applications = EndpointOauth2 + "applications" + EndpointApplications = EndpointAPI + "applications" + EndpointApplication = func(aID string) string { return EndpointApplications + "/" + aID } + EndpointOauth2Application = func(aID string) string { return EndpointOauth2Applications + "/" + aID } + EndpointOauth2ApplicationsBot = func(aID string) string { return EndpointOauth2Applications + "/" + aID + "/bot" } + EndpointOauth2ApplicationAssets = func(aID string) string { return EndpointOauth2Applications + "/" + aID + "/assets" } ) diff --git a/eventhandlers.go b/eventhandlers.go index d2b9a98b7..4f5c4d92e 100644 --- a/eventhandlers.go +++ b/eventhandlers.go @@ -28,6 +28,7 @@ const ( guildRoleDeleteEventType = "GUILD_ROLE_DELETE" guildRoleUpdateEventType = "GUILD_ROLE_UPDATE" guildUpdateEventType = "GUILD_UPDATE" + interactionCreateEventType = "INTERACTION_CREATE" messageAckEventType = "MESSAGE_ACK" messageCreateEventType = "MESSAGE_CREATE" messageDeleteEventType = "MESSAGE_DELETE" @@ -458,6 +459,26 @@ func (eh guildUpdateEventHandler) Handle(s *Session, i interface{}) { } } +// interactionCreateEventHandler is an event handler for InteractionCreate events. +type interactionCreateEventHandler func(*Session, *InteractionCreate) + +// Type returns the event type for InteractionCreate events. +func (eh interactionCreateEventHandler) Type() string { + return interactionCreateEventType +} + +// New returns a new instance of InteractionCreate. +func (eh interactionCreateEventHandler) New() interface{} { + return &InteractionCreate{} +} + +// Handle is the handler for InteractionCreate events. +func (eh interactionCreateEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*InteractionCreate); ok { + eh(s, t) + } +} + // messageAckEventHandler is an event handler for MessageAck events. type messageAckEventHandler func(*Session, *MessageAck) @@ -959,6 +980,8 @@ func handlerForInterface(handler interface{}) EventHandler { return guildRoleUpdateEventHandler(v) case func(*Session, *GuildUpdate): return guildUpdateEventHandler(v) + case func(*Session, *InteractionCreate): + return interactionCreateEventHandler(v) case func(*Session, *MessageAck): return messageAckEventHandler(v) case func(*Session, *MessageCreate): @@ -1029,6 +1052,7 @@ func init() { registerInterfaceProvider(guildRoleDeleteEventHandler(nil)) registerInterfaceProvider(guildRoleUpdateEventHandler(nil)) registerInterfaceProvider(guildUpdateEventHandler(nil)) + registerInterfaceProvider(interactionCreateEventHandler(nil)) registerInterfaceProvider(messageAckEventHandler(nil)) registerInterfaceProvider(messageCreateEventHandler(nil)) registerInterfaceProvider(messageDeleteEventHandler(nil)) diff --git a/events.go b/events.go index dd0e3d846..4fa24a97f 100644 --- a/events.go +++ b/events.go @@ -268,3 +268,8 @@ type WebhooksUpdate struct { GuildID string `json:"guild_id"` ChannelID string `json:"channel_id"` } + +// InteractionCreate is the data for a InteractionCreate event +type InteractionCreate struct { + *Interaction +} diff --git a/examples/slash-commands/main.go b/examples/slash-commands/main.go new file mode 100644 index 000000000..d8a1c7e38 --- /dev/null +++ b/examples/slash-commands/main.go @@ -0,0 +1,74 @@ +package main + +import ( + dgo "github.com/bwmarrin/discordgo" + dotenv "github.com/joho/godotenv" + "log" + "os" + "os/signal" + "syscall" +) + +func loadEnv() { + err := dotenv.Load() + if err != nil { + log.Fatal(err) + } +} + +func main() { + loadEnv() + client, err := dgo.New("Bot " + os.Getenv("BOT_TOKEN")) + if err != nil { + log.Fatal(err) + } + + client.AddHandler(func(*dgo.Session, *dgo.Ready) { + log.Println("Bot is up!") + }) + client.AddHandler(func(_ *dgo.Session, i *dgo.InteractionCreate) { + log.Println("Received an interaction: ", i.Interaction) + }) + + if err := client.Open(); err != nil { + log.Fatal(err) + } + + cmd, err := client.ApplicationCommandCreate(&dgo.ApplicationCommand{ + Name: "pingme", + Description: "Command for pinging a bot", + Options: []*dgo.ApplicationCommandOption{ + { + Type: dgo.ApplicationCommandOptionBoolean, + Name: "showcmd", + Description: "Show the command in the chat or hide it", + Default: false, + Required: false, + }, + }, + }, "") + + if err != nil { + panic(err) + } + + log.Println("Created command ID: ", cmd.ID) + log.Println("Old command: ", cmd) + cmd, err = client.ApplicationCommandEdit(cmd.ID, "", &dgo.ApplicationCommand{ + Name: "pingme_twice", + Description: "Twice", + }) + + if err != nil { + panic(err) + } + + log.Println("New command: ", cmd) + + ch := make(chan os.Signal, 1) + signal.Notify(ch, syscall.SIGINT, os.Interrupt) + <-ch + if err := client.Close(); err != nil { + log.Fatal(err) + } +} diff --git a/interactions.go b/interactions.go new file mode 100644 index 000000000..3abff58a1 --- /dev/null +++ b/interactions.go @@ -0,0 +1,125 @@ +package discordgo + +import ( + "time" +) + +const InteractionDeadline = time.Second * 3 + +// ApplicationCommand is representing application's slash command. +type ApplicationCommand struct { + ID string `json:"id"` + ApplicationID string `json:"application_id"` + Name string `json:"name"` + Description string `json:"description"` + Options []*ApplicationCommandOption `json:"options"` +} + +// ApplicationCommandOptionType is type of an slash-command's option. +type ApplicationCommandOptionType uint8 + +const ( + _ = ApplicationCommandOptionType(iota) + // ApplicationCommandOptionSubCommand is a type of option for sub-commands. + ApplicationCommandOptionSubCommand + // ApplicationCommandOptionSubCommand is a type of option for sub-commands groups. + ApplicationCommandOptionSubCommandGroup + // ApplicationCommandOptionSubCommand is a type of option for string options. + ApplicationCommandOptionString + // ApplicationCommandOptionSubCommand is a type of option for integer options. + ApplicationCommandOptionInteger + // ApplicationCommandOptionSubCommand is a type of option for boolean options. + ApplicationCommandOptionBoolean + // ApplicationCommandOptionSubCommand is a type of option when user id/mention is needed. + ApplicationCommandOptionUser + // ApplicationCommandOptionSubCommand is a type of option when channel id/mention is needed. + ApplicationCommandOptionChannel + // ApplicationCommandOptionSubCommand is a type of option when role id/mention is needed. + ApplicationCommandOptionRole +) + +// ApplicationCommandOption is representing an option/subcommand/subcommands group. +type ApplicationCommandOption struct { + Type ApplicationCommandOptionType `json:"type"` + Name string `json:"name"` + Description string `json:"description"` + Default bool `json:"default"` + Required bool `json:"required"` + Choices []*ApplicationCommandOptionChoice `json:"choices"` + Options []*ApplicationCommandOption `json:"options"` +} + +// ApplicationCommandOption is representing slash-command's option choice. +type ApplicationCommandOptionChoice struct { + Name string `json:"name"` + Value interface{} `json:"value"` +} + +// InteractionType is representing interaction type. +type InteractionType uint8 + +const ( + _ = InteractionType(iota) + // InteractionPing is type of interaction for ping. + InteractionPing + // InteractionApplicationCommand is type of interaction for application commands. + InteractionApplicationCommand +) + +// Interaction is representing interaction with application. +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"` + Member *Member `json:"member"` + Token string `json:"token"` + Version int `json:"version"` +} + +// ApplicationCommandInteractionData is representing interaction data for application command +type ApplicationCommandInteractionData struct { + ID string + Name string + Options []*ApplicationCommandInteractionDataOption +} + +// ApplicationCommandInteractionDataOption is representing an option of application's command +type ApplicationCommandInteractionDataOption struct { + Name string `json:"name"` + // Contains the value specified by InteractionType + Value interface{} `json:"value"` + Options []*ApplicationCommandInteractionDataOption `json:"options"` +} + +// InteractionResponseType is type of interaction response +type InteractionResponseType uint8 + +const ( + _ = InteractionResponseType(iota) + // InteractionResponsePong is an interaction response type when you need to just ACK a "Ping". + InteractionResponsePong + // InteractionResponsePong is an interaction response type when you need to ACK a command without sending a message, eating the user's input. + InteractionResponseAcknowledge + // InteractionResponsePong is an interaction response type when you need to respond with a message, eating the user's input. + InteractionResponseChannelMessage + // InteractionResponsePong is an interaction response type when you need to respond with a message, showing the user's input. + InteractionResponseChannelMessageWithSource + // InteractionResponsePong is an interaction response type when you need to ACK a command without sending a message, showing the user's input. + InteractionResponseACKWithSource +) + +// InteractionResponse is representing response for interaction with application +type InteractionResponse struct { + Type InteractionResponseType `json:"type"` + Data *InteractionApplicationCommandCallbackData `json:"data"` +} + +// InteractionApplicationCommandCallbackData is callback data for application command interaction +type InteractionApplicationCommandCallbackData struct { + TTS bool `json:"tts"` + Content string `json:"content"` + Embeds []*MessageEmbed `json:"embeds"` + AllowedMentions *MessageAllowedMentions `json:"allowed_mentions"` +} diff --git a/oauth2.go b/oauth2.go index 289eca949..dc1c67280 100644 --- a/oauth2.go +++ b/oauth2.go @@ -61,7 +61,7 @@ type Application struct { // appID : The ID of an Application func (s *Session) Application(appID string) (st *Application, err error) { - body, err := s.RequestWithBucketID("GET", EndpointApplication(appID), nil, EndpointApplication("")) + body, err := s.RequestWithBucketID("GET", EndpointOauth2Application(appID), nil, EndpointOauth2Application("")) if err != nil { return } @@ -73,7 +73,7 @@ func (s *Session) Application(appID string) (st *Application, err error) { // Applications returns all applications for the authenticated user func (s *Session) Applications() (st []*Application, err error) { - body, err := s.RequestWithBucketID("GET", EndpointApplications, nil, EndpointApplications) + body, err := s.RequestWithBucketID("GET", EndpointOauth2Applications, nil, EndpointOauth2Applications) if err != nil { return } @@ -93,7 +93,7 @@ func (s *Session) ApplicationCreate(ap *Application) (st *Application, err error RedirectURIs *[]string `json:"redirect_uris,omitempty"` }{ap.Name, ap.Description, ap.RedirectURIs} - body, err := s.RequestWithBucketID("POST", EndpointApplications, data, EndpointApplications) + body, err := s.RequestWithBucketID("POST", EndpointOauth2Applications, data, EndpointOauth2Applications) if err != nil { return } @@ -112,7 +112,7 @@ func (s *Session) ApplicationUpdate(appID string, ap *Application) (st *Applicat RedirectURIs *[]string `json:"redirect_uris,omitempty"` }{ap.Name, ap.Description, ap.RedirectURIs} - body, err := s.RequestWithBucketID("PUT", EndpointApplication(appID), data, EndpointApplication("")) + body, err := s.RequestWithBucketID("PUT", EndpointOauth2Application(appID), data, EndpointOauth2Application("")) if err != nil { return } @@ -125,7 +125,7 @@ func (s *Session) ApplicationUpdate(appID string, ap *Application) (st *Applicat // appID : The ID of an Application func (s *Session) ApplicationDelete(appID string) (err error) { - _, err = s.RequestWithBucketID("DELETE", EndpointApplication(appID), nil, EndpointApplication("")) + _, err = s.RequestWithBucketID("DELETE", EndpointOauth2Application(appID), nil, EndpointOauth2Application("")) if err != nil { return } @@ -143,7 +143,7 @@ type Asset struct { // ApplicationAssets returns an application's assets func (s *Session) ApplicationAssets(appID string) (ass []*Asset, err error) { - body, err := s.RequestWithBucketID("GET", EndpointApplicationAssets(appID), nil, EndpointApplicationAssets("")) + body, err := s.RequestWithBucketID("GET", EndpointOauth2ApplicationAssets(appID), nil, EndpointOauth2ApplicationAssets("")) if err != nil { return } @@ -163,7 +163,7 @@ func (s *Session) ApplicationAssets(appID string) (ass []*Asset, err error) { // NOTE: func name may change, if I can think up something better. func (s *Session) ApplicationBotCreate(appID string) (st *User, err error) { - body, err := s.RequestWithBucketID("POST", EndpointApplicationsBot(appID), nil, EndpointApplicationsBot("")) + body, err := s.RequestWithBucketID("POST", EndpointOauth2ApplicationsBot(appID), nil, EndpointOauth2ApplicationsBot("")) if err != nil { return } diff --git a/restapi.go b/restapi.go index fdbcc35bc..16eebdcf5 100644 --- a/restapi.go +++ b/restapi.go @@ -2328,3 +2328,65 @@ func (s *Session) RelationshipsMutualGet(userID string) (mf []*User, err error) err = unmarshal(body, &mf) return } + +// ApplicationCommandCreate creates an global application command. If guild is specified - creates guild specific application command. +func (s *Session) ApplicationCommandCreate(cmd *ApplicationCommand, guildID string) (rcmd *ApplicationCommand, err error) { + endpoint := EndpointApplicationGlobalCommands(s.State.User.ID) + if guildID != "" { + endpoint = EndpointApplicationGuildCommands(s.State.User.ID, guildID) + } + body, err := s.RequestWithBucketID("POST", endpoint, *cmd, endpoint) + if err != nil { + return + } + + err = unmarshal(body, &rcmd) + return +} + +// ApplicationCommandDelete deletes global application command by id. If guild is specified - deletes guild specific application command. +func (s *Session) ApplicationCommandDelete(cmdID, guildID string) (err error) { + endpoint := EndpointApplicationGlobalCommand(s.State.User.ID, cmdID) + if guildID != "" { + endpoint = EndpointApplicationGuildCommand(s.State.User.ID, guildID, cmdID) + } + _, err = s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) + return +} + +// ApplicationCommandEdit edits global application command by id and replaces with contents of "cmd". If guild is specified - edits guild specific application command. +func (s *Session) ApplicationCommandEdit(cmdID, guildID string, cmd *ApplicationCommand) (oldcmd *ApplicationCommand, err error) { + endpoint := EndpointApplicationGlobalCommand(s.State.User.ID, cmdID) + if guildID != "" { + endpoint = EndpointApplicationGuildCommand(s.State.User.ID, guildID, cmdID) + } + + body, err := s.RequestWithBucketID("PATCH", endpoint, *cmd, endpoint) + + err = unmarshal(body, &oldcmd) + return +} + +// ApplicationCommand retrieves a command by given id and returns it. If guild is specified - retrieves guild specific application command. +func (s *Session) ApplicationCommand(cmdID, guildID string) (cmd *ApplicationCommand, err error) { + endpoint := EndpointApplicationGlobalCommand(s.State.User.ID, cmdID) + if guildID != "" { + endpoint = EndpointApplicationGuildCommand(s.State.User.ID, guildID, cmdID) + } + + _, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + return +} + +// ApplicationCommands retrieves all commands in application. If guild is specified - retrieves all guild specific application commands. +func (s *Session) ApplicationCommands(guildID string) (cmd []*ApplicationCommand, err error) { + endpoint := EndpointApplicationGlobalCommands(s.State.User.ID) + if guildID != "" { + endpoint = EndpointApplicationGuildCommands(s.State.User.ID, guildID) + } + + body, err := s.RequestWithBucketID("GET", endpoint, nil, endpoint) + + err = unmarshal(body, &cmd) + return +} From af426c146a59781984df1701c04efa33bb92293c Mon Sep 17 00:00:00 2001 From: nitroflap Date: Sun, 17 Jan 2021 00:00:09 +0300 Subject: [PATCH 05/28] Some gitignore update --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 34d2efa5b..e860b2910 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ # IDE-specific metadata .idea/ + +# Environment variables. Useful for examples. +.env \ No newline at end of file From c1d8bffdcdbd4953bafd7b4ee24cab577d3a09bf Mon Sep 17 00:00:00 2001 From: nitroflap Date: Sun, 17 Jan 2021 00:07:40 +0300 Subject: [PATCH 06/28] Application commands and interactions API implementation --- endpoints.go | 21 +++++- examples/slash-commands/main.go | 74 ------------------ examples/slash_commands/main.go | 120 ++++++++++++++++++++++++++++++ interactions.go => interaction.go | 66 ++++++++-------- restapi.go | 61 +++++++++++++++ structs.go | 36 --------- webhook.go | 44 +++++++++++ 7 files changed, 278 insertions(+), 144 deletions(-) delete mode 100644 examples/slash-commands/main.go create mode 100644 examples/slash_commands/main.go rename interactions.go => interaction.go (67%) create mode 100644 webhook.go diff --git a/endpoints.go b/endpoints.go index 7789d85a5..5941d7642 100644 --- a/endpoints.go +++ b/endpoints.go @@ -14,7 +14,7 @@ package discordgo import "strconv" // APIVersion is the Discord API version used for the REST and Websocket API. -var APIVersion = "8" +var APIVersion = "6" // Known Discord API Endpoints. var ( @@ -120,6 +120,9 @@ var ( EndpointChannelWebhooks = func(cID string) string { return EndpointChannel(cID) + "/webhooks" } EndpointWebhook = func(wID string) string { return EndpointWebhooks + wID } EndpointWebhookToken = func(wID, token string) string { return EndpointWebhooks + wID + "/" + token } + EndpointWebhookMessage = func(wID, token, messageID string) string { + return EndpointWebhookToken(wID, token) + "/" + "messages/" + messageID + } EndpointMessageReactionsAll = func(cID, mID string) string { return EndpointChannelMessage(cID, mID) + "/reactions" @@ -134,7 +137,6 @@ var ( EndpointApplicationGlobalCommands = func(aID string) string { return EndpointApplication(aID) + "/" + "commands" } - EndpointApplicationGlobalCommand = func(aID, cID string) string { return EndpointApplicationGlobalCommands(aID) + "/" + cID } @@ -145,6 +147,21 @@ var ( EndpointApplicationGuildCommand = func(aID, gID, cID string) string { return EndpointApplicationGuildCommands(aID, gID) + "/" + cID } + EndpointInteraction = func(aID, iToken string) string { + return EndpointAPI + "interactions/" + aID + "/" + iToken + } + EndpointInteractionResponse = func(iID, iToken string) string { + return EndpointInteraction(iID, iToken) + "/" + "callback" + } + EndpointInteractionResponseActions = func(aID, iToken string) string { + return EndpointWebhookMessage(aID, iToken, "@original") + } + EndpointFollowupMessage = func(aID, iToken string) string { + return EndpointWebhookToken(aID, iToken) + } + EndpointFollowupMessageActions = func(aID, iToken, mID string) string { + return EndpointWebhookMessage(aID, iToken, mID) + } EndpointRelationships = func() string { return EndpointUsers + "@me" + "/relationships" } EndpointRelationship = func(uID string) string { return EndpointRelationships() + "/" + uID } diff --git a/examples/slash-commands/main.go b/examples/slash-commands/main.go deleted file mode 100644 index d8a1c7e38..000000000 --- a/examples/slash-commands/main.go +++ /dev/null @@ -1,74 +0,0 @@ -package main - -import ( - dgo "github.com/bwmarrin/discordgo" - dotenv "github.com/joho/godotenv" - "log" - "os" - "os/signal" - "syscall" -) - -func loadEnv() { - err := dotenv.Load() - if err != nil { - log.Fatal(err) - } -} - -func main() { - loadEnv() - client, err := dgo.New("Bot " + os.Getenv("BOT_TOKEN")) - if err != nil { - log.Fatal(err) - } - - client.AddHandler(func(*dgo.Session, *dgo.Ready) { - log.Println("Bot is up!") - }) - client.AddHandler(func(_ *dgo.Session, i *dgo.InteractionCreate) { - log.Println("Received an interaction: ", i.Interaction) - }) - - if err := client.Open(); err != nil { - log.Fatal(err) - } - - cmd, err := client.ApplicationCommandCreate(&dgo.ApplicationCommand{ - Name: "pingme", - Description: "Command for pinging a bot", - Options: []*dgo.ApplicationCommandOption{ - { - Type: dgo.ApplicationCommandOptionBoolean, - Name: "showcmd", - Description: "Show the command in the chat or hide it", - Default: false, - Required: false, - }, - }, - }, "") - - if err != nil { - panic(err) - } - - log.Println("Created command ID: ", cmd.ID) - log.Println("Old command: ", cmd) - cmd, err = client.ApplicationCommandEdit(cmd.ID, "", &dgo.ApplicationCommand{ - Name: "pingme_twice", - Description: "Twice", - }) - - if err != nil { - panic(err) - } - - log.Println("New command: ", cmd) - - ch := make(chan os.Signal, 1) - signal.Notify(ch, syscall.SIGINT, os.Interrupt) - <-ch - if err := client.Close(); err != nil { - log.Fatal(err) - } -} diff --git a/examples/slash_commands/main.go b/examples/slash_commands/main.go new file mode 100644 index 000000000..49a4c547f --- /dev/null +++ b/examples/slash_commands/main.go @@ -0,0 +1,120 @@ +package main + +import ( + "flag" + dgo "github.com/bwmarrin/discordgo" + "log" + "os" + "os/signal" + "syscall" + "time" +) + +var ( + botToken = flag.String("bot_tok", "", "Bot token") + testGuild = flag.String("test_guild", "", "Guild for testing the bot") +) + +func setup() *dgo.Session { + client, err := dgo.New("Bot " + *botToken) + if err != nil { + log.Fatal(err) + } + + client.AddHandler(func(*dgo.Session, *dgo.Ready) { + log.Println("Bot is up!") + }) + + return client +} + +func main() { + flag.Parse() + + client := setup() + + client.AddHandler(func(s *dgo.Session, i *dgo.InteractionCreate) { + log.Println(dgo.InteractionResponseType(i.Interaction.Data.Options[0].Value.(float64))) + err := s.InteractionRespond(i.Interaction, &dgo.InteractionResponse{ + Type: dgo.InteractionResponseType(i.Interaction.Data.Options[0].Value.(float64)), + Data: &dgo.InteractionApplicationCommandResponseData{ + TTS: false, + Content: "here we go", + // Flags: 1 << 6, + }, + }) + log.Println("response", err) + time.Sleep(time.Second * 2) + err = s.InteractionResponseEdit(i.Interaction, &dgo.WebhookEdit{ + Content: "here we go!", + }) + log.Println("response edit", err) + time.Sleep(time.Second * 2) + err = s.InteractionResponseDelete(i.Interaction) + log.Println("response delete", err) + err = s.InteractionResponseDelete(i.Interaction) + log.Println("response delete 2", err) + + followupMessage, err := s.FollowupMessageCreate(i.Interaction, true, &dgo.WebhookParams{ + Content: "followup messages rule!", + }) + log.Println("followup message create", followupMessage, err) + time.Sleep(time.Second * 3) + err = s.FollowupMessageEdit(i.Interaction, followupMessage.ID, &dgo.WebhookEdit{ + Content: "that's true", + }) + log.Println("followup message edit", err) + time.Sleep(time.Second * 3) + err = s.FollowupMessageDelete(i.Interaction, followupMessage.ID) + log.Println("followup message delete", err) + }) + + if err := client.Open(); err != nil { + log.Fatal(err) + } + + cmd, err := client.ApplicationCommandCreate(&dgo.ApplicationCommand{ + Name: "test-slashes", + Description: "Command for testing application commands", + Options: []*dgo.ApplicationCommandOption{ + { + Type: dgo.ApplicationCommandOptionInteger, + Name: "typ", + Description: "Response type", + Choices: []*dgo.ApplicationCommandOptionChoice{ + { + Name: "ACK", + Value: dgo.InteractionResponseAcknowledge, + }, + { + Name: "ACK with source", + Value: dgo.InteractionResponseACKWithSource, + }, + { + Name: "Channel message", + Value: dgo.InteractionResponseChannelMessage, + }, + { + Name: "Channel message with source", + Value: dgo.InteractionResponseChannelMessageWithSource, + }, + }, + Required: false, + }, + }, + }, *testGuild) + + if err != nil { + panic(err) + } + + log.Println("Old command: ", cmd) + log.Println("Created command: ", cmd) + + ch := make(chan os.Signal, 1) + signal.Notify(ch, syscall.SIGINT, os.Interrupt) + <-ch + if err := client.Close(); err != nil { + log.Fatal(err) + } +} diff --git a/interactions.go b/interaction.go similarity index 67% rename from interactions.go rename to interaction.go index 3abff58a1..af33c59b8 100644 --- a/interactions.go +++ b/interaction.go @@ -8,11 +8,11 @@ const InteractionDeadline = time.Second * 3 // ApplicationCommand is representing application's slash command. type ApplicationCommand struct { - ID string `json:"id"` - ApplicationID string `json:"application_id"` - Name string `json:"name"` - Description string `json:"description"` - Options []*ApplicationCommandOption `json:"options"` + ID string `json:"id,omitempty"` + ApplicationID string `json:"application_id,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Options []*ApplicationCommandOption `json:"options,omitempty"` } // ApplicationCommandOptionType is type of an slash-command's option. @@ -40,19 +40,19 @@ const ( // ApplicationCommandOption is representing an option/subcommand/subcommands group. type ApplicationCommandOption struct { - Type ApplicationCommandOptionType `json:"type"` - Name string `json:"name"` - Description string `json:"description"` - Default bool `json:"default"` - Required bool `json:"required"` - Choices []*ApplicationCommandOptionChoice `json:"choices"` - Options []*ApplicationCommandOption `json:"options"` + Type ApplicationCommandOptionType `json:"type,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Default bool `json:"default,omitempty"` + Required bool `json:"required,omitempty"` + Choices []*ApplicationCommandOptionChoice `json:"choices,omitempty"` + Options []*ApplicationCommandOption `json:"options,omitempty"` } // ApplicationCommandOption is representing slash-command's option choice. type ApplicationCommandOptionChoice struct { - Name string `json:"name"` - Value interface{} `json:"value"` + Name string `json:"name,omitempty"` + Value interface{} `json:"value,omitempty"` } // InteractionType is representing interaction type. @@ -68,14 +68,14 @@ const ( // Interaction is representing interaction with application. 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"` - Member *Member `json:"member"` - Token string `json:"token"` - Version int `json:"version"` + ID string `json:"id,omitempty"` + Type InteractionType `json:"type,omitempty"` + Data ApplicationCommandInteractionData `json:"data,omitempty"` + GuildID string `json:"guild_id,omitempty"` + ChannelID string `json:"channel_id,omitempty"` + Member *Member `json:"member,omitempty"` + Token string `json:"token,omitempty"` + Version int `json:"version,omitempty"` } // ApplicationCommandInteractionData is representing interaction data for application command @@ -87,10 +87,10 @@ type ApplicationCommandInteractionData struct { // ApplicationCommandInteractionDataOption is representing an option of application's command type ApplicationCommandInteractionDataOption struct { - Name string `json:"name"` + Name string `json:"name,omitempty"` // Contains the value specified by InteractionType - Value interface{} `json:"value"` - Options []*ApplicationCommandInteractionDataOption `json:"options"` + Value interface{} `json:"value,omitempty"` + Options []*ApplicationCommandInteractionDataOption `json:"options,omitempty"` } // InteractionResponseType is type of interaction response @@ -112,14 +112,16 @@ const ( // InteractionResponse is representing response for interaction with application type InteractionResponse struct { - Type InteractionResponseType `json:"type"` - Data *InteractionApplicationCommandCallbackData `json:"data"` + Type InteractionResponseType `json:"type,omitempty"` + Data *InteractionApplicationCommandResponseData `json:"data,omitempty"` } // InteractionApplicationCommandCallbackData is callback data for application command interaction -type InteractionApplicationCommandCallbackData struct { - TTS bool `json:"tts"` - Content string `json:"content"` - Embeds []*MessageEmbed `json:"embeds"` - AllowedMentions *MessageAllowedMentions `json:"allowed_mentions"` +type InteractionApplicationCommandResponseData struct { + TTS bool `json:"tts,omitempty"` + Content string `json:"content,omitempty"` + Embeds []*MessageEmbed `json:"embeds,omitempty"` + AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` + + Flags uint64 `json:"flags,omitempty"` } diff --git a/restapi.go b/restapi.go index 16eebdcf5..ff875dd3f 100644 --- a/restapi.go +++ b/restapi.go @@ -2174,6 +2174,29 @@ func (s *Session) WebhookExecute(webhookID, token string, wait bool, data *Webho return } +// WebhookMessageEdit edits webhook's message. +// webhookID : The ID of a webhook +// token : The auth token for the webhook +// messageID : The ID of message to edit +func (s *Session) WebhookMessageEdit(webhookID, token, messageID string, data *WebhookEdit) (err error) { + uri := EndpointWebhookMessage(webhookID, token, messageID) + + _, err = s.RequestWithBucketID("PATCH", uri, data, EndpointWebhookToken("", "")) + + return +} + +// WebhookMessageEdit deletes webhook's message. +// webhookID : The ID of a webhook +// token : The auth token for the webhook +// messageID : The ID of message to edit +func (s *Session) WebhookMessageDelete(webhookID, token, messageID string) (err error) { + uri := EndpointWebhookMessage(webhookID, token, messageID) + + _, err = s.RequestWithBucketID("DELETE", uri, nil, EndpointWebhookToken("", "")) + return +} + // MessageReactionAdd creates an emoji reaction to a message. // channelID : The channel ID. // messageID : The message ID. @@ -2329,6 +2352,10 @@ func (s *Session) RelationshipsMutualGet(userID string) (mf []*User, err error) return } +// ------------------------------------------------------------------------------------------------ +// Functions specific to application (slash) commands +// ------------------------------------------------------------------------------------------------ + // ApplicationCommandCreate creates an global application command. If guild is specified - creates guild specific application command. func (s *Session) ApplicationCommandCreate(cmd *ApplicationCommand, guildID string) (rcmd *ApplicationCommand, err error) { endpoint := EndpointApplicationGlobalCommands(s.State.User.ID) @@ -2390,3 +2417,37 @@ func (s *Session) ApplicationCommands(guildID string) (cmd []*ApplicationCommand err = unmarshal(body, &cmd) return } + +// InteractionRespond creates the response to an interaction. +func (s *Session) InteractionRespond(interaction *Interaction, resp *InteractionResponse) (err error) { + endpoint := EndpointInteractionResponse(interaction.ID, interaction.Token) + _, err = s.RequestWithBucketID("POST", endpoint, *resp, endpoint) + return +} + +// InteractionResponseEdit edits the response to an interaction. +func (s *Session) InteractionResponseEdit(interaction *Interaction, newresp *WebhookEdit) (err error) { + return s.WebhookMessageEdit(s.State.User.ID, interaction.Token, "@original", newresp) +} + +// InteractionResponseDelete deletes the response to an interaction. +func (s *Session) InteractionResponseDelete(interaction *Interaction) (err error) { + endpoint := EndpointInteractionResponseActions(s.State.User.ID, interaction.Token) + _, err = s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) + return +} + +// FollowupMessageCreate creates the followup message for an interaction. +func (s *Session) FollowupMessageCreate(interaction *Interaction, wait bool, data *WebhookParams) (st *Message, err error) { + return s.WebhookExecute(s.State.User.ID, interaction.Token, wait, data) +} + +// FollowupMessageEdit edits a followup message of an interaction. +func (s *Session) FollowupMessageEdit(interaction *Interaction, messageID string, data *WebhookEdit) (err error) { + return s.WebhookMessageEdit(s.State.User.ID, interaction.Token, messageID, data) +} + +// FollowupMessageDelete deletes a followup message of an interaction. +func (s *Session) FollowupMessageDelete(interaction *Interaction, messageID string) (err error) { + return s.WebhookMessageDelete(s.State.User.ID, interaction.Token, messageID) +} diff --git a/structs.go b/structs.go index 13eb99342..3c98b19f7 100644 --- a/structs.go +++ b/structs.go @@ -1054,42 +1054,6 @@ type APIErrorMessage struct { Message string `json:"message"` } -// Webhook stores the data for a webhook. -type Webhook struct { - ID string `json:"id"` - Type WebhookType `json:"type"` - GuildID string `json:"guild_id"` - ChannelID string `json:"channel_id"` - User *User `json:"user"` - Name string `json:"name"` - Avatar string `json:"avatar"` - Token string `json:"token"` - - // ApplicationID is the bot/OAuth2 application that created this webhook - ApplicationID string `json:"application_id,omitempty"` -} - -// WebhookType is the type of Webhook (see WebhookType* consts) in the Webhook struct -// https://discord.com/developers/docs/resources/webhook#webhook-object-webhook-types -type WebhookType int - -// Valid WebhookType values -const ( - WebhookTypeIncoming WebhookType = iota - WebhookTypeChannelFollower -) - -// WebhookParams is a struct for webhook params, used in the WebhookExecute command. -type WebhookParams struct { - Content string `json:"content,omitempty"` - Username string `json:"username,omitempty"` - AvatarURL string `json:"avatar_url,omitempty"` - TTS bool `json:"tts,omitempty"` - File string `json:"file,omitempty"` - Embeds []*MessageEmbed `json:"embeds,omitempty"` - AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` -} - // MessageReaction stores the data for a message reaction. type MessageReaction struct { UserID string `json:"user_id"` diff --git a/webhook.go b/webhook.go new file mode 100644 index 000000000..3fa47bfe1 --- /dev/null +++ b/webhook.go @@ -0,0 +1,44 @@ +package discordgo + +// Webhook stores the data for a webhook. +type Webhook struct { + ID string `json:"id"` + Type WebhookType `json:"type"` + GuildID string `json:"guild_id"` + ChannelID string `json:"channel_id"` + User *User `json:"user"` + Name string `json:"name"` + Avatar string `json:"avatar"` + Token string `json:"token"` + + // ApplicationID is the bot/OAuth2 application that created this webhook + ApplicationID string `json:"application_id,omitempty"` +} + +// WebhookType is the type of Webhook (see WebhookType* consts) in the Webhook struct +// https://discord.com/developers/docs/resources/webhook#webhook-object-webhook-types +type WebhookType int + +// Valid WebhookType values +const ( + WebhookTypeIncoming WebhookType = iota + WebhookTypeChannelFollower +) + +// WebhookParams is a struct for webhook params, used in the WebhookExecute command. +type WebhookParams struct { + Content string `json:"content,omitempty"` + Username string `json:"username,omitempty"` + AvatarURL string `json:"avatar_url,omitempty"` + TTS bool `json:"tts,omitempty"` + File string `json:"file,omitempty"` + Embeds []*MessageEmbed `json:"embeds,omitempty"` + AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` +} + +// WebhookEdit stores data for editing webhook's message +type WebhookEdit struct { + Content string `json:"content,omitempty"` + Embeds []*MessageEmbed `json:"embeds,omitempty"` + AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` +} From 3f37ec0c0c198d128a378645a38c5736faf75662 Mon Sep 17 00:00:00 2001 From: nitroflap Date: Sun, 17 Jan 2021 05:18:02 +0300 Subject: [PATCH 07/28] Some fixes --- endpoints.go | 10 ++++----- examples/slash_commands/main.go | 2 +- interaction.go | 39 ++++++++++++++------------------- oauth2.go | 14 ++++++------ ratelimit.go | 2 +- restapi.go | 8 +++++-- webhook.go | 2 +- 7 files changed, 38 insertions(+), 39 deletions(-) diff --git a/endpoints.go b/endpoints.go index 5941d7642..9b11b8c3e 100644 --- a/endpoints.go +++ b/endpoints.go @@ -176,11 +176,11 @@ var ( EndpointEmoji = func(eID string) string { return EndpointCDN + "emojis/" + eID + ".png" } EndpointEmojiAnimated = func(eID string) string { return EndpointCDN + "emojis/" + eID + ".gif" } - EndpointOauth2 = EndpointAPI + "oauth2/" - EndpointOauth2Applications = EndpointOauth2 + "applications" + EndpointOAuth2 = EndpointAPI + "oauth2/" + EndpointOAuth2Applications = EndpointOAuth2 + "applications" EndpointApplications = EndpointAPI + "applications" EndpointApplication = func(aID string) string { return EndpointApplications + "/" + aID } - EndpointOauth2Application = func(aID string) string { return EndpointOauth2Applications + "/" + aID } - EndpointOauth2ApplicationsBot = func(aID string) string { return EndpointOauth2Applications + "/" + aID + "/bot" } - EndpointOauth2ApplicationAssets = func(aID string) string { return EndpointOauth2Applications + "/" + aID + "/assets" } + EndpointOAuth2Application = func(aID string) string { return EndpointOAuth2Applications + "/" + aID } + EndpointOAuth2ApplicationsBot = func(aID string) string { return EndpointOAuth2Applications + "/" + aID + "/bot" } + EndpointOAuth2ApplicationAssets = func(aID string) string { return EndpointOAuth2Applications + "/" + aID + "/assets" } ) diff --git a/examples/slash_commands/main.go b/examples/slash_commands/main.go index 49a4c547f..f15a9e504 100644 --- a/examples/slash_commands/main.go +++ b/examples/slash_commands/main.go @@ -105,7 +105,7 @@ func main() { }, *testGuild) if err != nil { - panic(err) + log.Fatal(err) } log.Println("Old command: ", cmd) diff --git a/interaction.go b/interaction.go index af33c59b8..bf3ade6c2 100644 --- a/interaction.go +++ b/interaction.go @@ -18,23 +18,17 @@ type ApplicationCommand struct { // ApplicationCommandOptionType is type of an slash-command's option. type ApplicationCommandOptionType uint8 +// Application command option types. const ( _ = ApplicationCommandOptionType(iota) - // ApplicationCommandOptionSubCommand is a type of option for sub-commands. + ApplicationCommandOptionSubCommand - // ApplicationCommandOptionSubCommand is a type of option for sub-commands groups. ApplicationCommandOptionSubCommandGroup - // ApplicationCommandOptionSubCommand is a type of option for string options. ApplicationCommandOptionString - // ApplicationCommandOptionSubCommand is a type of option for integer options. ApplicationCommandOptionInteger - // ApplicationCommandOptionSubCommand is a type of option for boolean options. ApplicationCommandOptionBoolean - // ApplicationCommandOptionSubCommand is a type of option when user id/mention is needed. ApplicationCommandOptionUser - // ApplicationCommandOptionSubCommand is a type of option when channel id/mention is needed. ApplicationCommandOptionChannel - // ApplicationCommandOptionSubCommand is a type of option when role id/mention is needed. ApplicationCommandOptionRole ) @@ -68,24 +62,24 @@ const ( // Interaction is representing interaction with application. type Interaction struct { - ID string `json:"id,omitempty"` - Type InteractionType `json:"type,omitempty"` - Data ApplicationCommandInteractionData `json:"data,omitempty"` - GuildID string `json:"guild_id,omitempty"` - ChannelID string `json:"channel_id,omitempty"` - Member *Member `json:"member,omitempty"` - Token string `json:"token,omitempty"` - Version int `json:"version,omitempty"` + ID string `json:"id"` + Type InteractionType `json:"type"` + Data ApplicationCommandInteractionData `json:"data"` + GuildID string `json:"guild_id"` + ChannelID string `json:"channel_id"` + Member *Member `json:"member"` + Token string `json:"token"` + Version int `json:"version"` } -// ApplicationCommandInteractionData is representing interaction data for application command +// ApplicationCommandInteractionData is representing interaction data for application command. type ApplicationCommandInteractionData struct { ID string Name string Options []*ApplicationCommandInteractionDataOption } -// ApplicationCommandInteractionDataOption is representing an option of application's command +// ApplicationCommandInteractionDataOption is representing an option of application's command. type ApplicationCommandInteractionDataOption struct { Name string `json:"name,omitempty"` // Contains the value specified by InteractionType @@ -93,9 +87,10 @@ type ApplicationCommandInteractionDataOption struct { Options []*ApplicationCommandInteractionDataOption `json:"options,omitempty"` } -// InteractionResponseType is type of interaction response +// InteractionResponseType is type of interaction response. type InteractionResponseType uint8 +// Interaction response types. const ( _ = InteractionResponseType(iota) // InteractionResponsePong is an interaction response type when you need to just ACK a "Ping". @@ -110,18 +105,18 @@ const ( InteractionResponseACKWithSource ) -// InteractionResponse is representing response for interaction with application +// InteractionResponse is representing response for interaction with application. type InteractionResponse struct { Type InteractionResponseType `json:"type,omitempty"` Data *InteractionApplicationCommandResponseData `json:"data,omitempty"` } -// InteractionApplicationCommandCallbackData is callback data for application command interaction +// InteractionApplicationCommandCallbackData is callback data for application command interaction. type InteractionApplicationCommandResponseData struct { TTS bool `json:"tts,omitempty"` Content string `json:"content,omitempty"` Embeds []*MessageEmbed `json:"embeds,omitempty"` AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` - Flags uint64 `json:"flags,omitempty"` + Flags uint64 `json:"flags,omitempty"` // NOTE: Undocumented feature, be careful with it. } diff --git a/oauth2.go b/oauth2.go index dc1c67280..cafc04070 100644 --- a/oauth2.go +++ b/oauth2.go @@ -61,7 +61,7 @@ type Application struct { // appID : The ID of an Application func (s *Session) Application(appID string) (st *Application, err error) { - body, err := s.RequestWithBucketID("GET", EndpointOauth2Application(appID), nil, EndpointOauth2Application("")) + body, err := s.RequestWithBucketID("GET", EndpointOAuth2Application(appID), nil, EndpointOAuth2Application("")) if err != nil { return } @@ -73,7 +73,7 @@ func (s *Session) Application(appID string) (st *Application, err error) { // Applications returns all applications for the authenticated user func (s *Session) Applications() (st []*Application, err error) { - body, err := s.RequestWithBucketID("GET", EndpointOauth2Applications, nil, EndpointOauth2Applications) + body, err := s.RequestWithBucketID("GET", EndpointOAuth2Applications, nil, EndpointOAuth2Applications) if err != nil { return } @@ -93,7 +93,7 @@ func (s *Session) ApplicationCreate(ap *Application) (st *Application, err error RedirectURIs *[]string `json:"redirect_uris,omitempty"` }{ap.Name, ap.Description, ap.RedirectURIs} - body, err := s.RequestWithBucketID("POST", EndpointOauth2Applications, data, EndpointOauth2Applications) + body, err := s.RequestWithBucketID("POST", EndpointOAuth2Applications, data, EndpointOAuth2Applications) if err != nil { return } @@ -112,7 +112,7 @@ func (s *Session) ApplicationUpdate(appID string, ap *Application) (st *Applicat RedirectURIs *[]string `json:"redirect_uris,omitempty"` }{ap.Name, ap.Description, ap.RedirectURIs} - body, err := s.RequestWithBucketID("PUT", EndpointOauth2Application(appID), data, EndpointOauth2Application("")) + body, err := s.RequestWithBucketID("PUT", EndpointOAuth2Application(appID), data, EndpointOAuth2Application("")) if err != nil { return } @@ -125,7 +125,7 @@ func (s *Session) ApplicationUpdate(appID string, ap *Application) (st *Applicat // appID : The ID of an Application func (s *Session) ApplicationDelete(appID string) (err error) { - _, err = s.RequestWithBucketID("DELETE", EndpointOauth2Application(appID), nil, EndpointOauth2Application("")) + _, err = s.RequestWithBucketID("DELETE", EndpointOAuth2Application(appID), nil, EndpointOAuth2Application("")) if err != nil { return } @@ -143,7 +143,7 @@ type Asset struct { // ApplicationAssets returns an application's assets func (s *Session) ApplicationAssets(appID string) (ass []*Asset, err error) { - body, err := s.RequestWithBucketID("GET", EndpointOauth2ApplicationAssets(appID), nil, EndpointOauth2ApplicationAssets("")) + body, err := s.RequestWithBucketID("GET", EndpointOAuth2ApplicationAssets(appID), nil, EndpointOAuth2ApplicationAssets("")) if err != nil { return } @@ -163,7 +163,7 @@ func (s *Session) ApplicationAssets(appID string) (ass []*Asset, err error) { // NOTE: func name may change, if I can think up something better. func (s *Session) ApplicationBotCreate(appID string) (st *User, err error) { - body, err := s.RequestWithBucketID("POST", EndpointOauth2ApplicationsBot(appID), nil, EndpointOauth2ApplicationsBot("")) + body, err := s.RequestWithBucketID("POST", EndpointOAuth2ApplicationsBot(appID), nil, EndpointOAuth2ApplicationsBot("")) if err != nil { return } diff --git a/ratelimit.go b/ratelimit.go index dc48c9240..4666bce13 100644 --- a/ratelimit.go +++ b/ratelimit.go @@ -32,7 +32,7 @@ func NewRatelimiter() *RateLimiter { buckets: make(map[string]*Bucket), global: new(int64), customRateLimits: []*customRateLimit{ - &customRateLimit{ + { suffix: "//reactions//", requests: 1, reset: 200 * time.Millisecond, diff --git a/restapi.go b/restapi.go index ff875dd3f..4c22e22d8 100644 --- a/restapi.go +++ b/restapi.go @@ -2353,7 +2353,7 @@ func (s *Session) RelationshipsMutualGet(userID string) (mf []*User, err error) } // ------------------------------------------------------------------------------------------------ -// Functions specific to application (slash) commands +// Functions specific to application (slash) commands // ------------------------------------------------------------------------------------------------ // ApplicationCommandCreate creates an global application command. If guild is specified - creates guild specific application command. @@ -2401,7 +2401,11 @@ func (s *Session) ApplicationCommand(cmdID, guildID string) (cmd *ApplicationCom endpoint = EndpointApplicationGuildCommand(s.State.User.ID, guildID, cmdID) } - _, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + body, err := s.RequestWithBucketID("GET", endpoint, nil, endpoint) + if err == nil { + err = unmarshal(body, &cmd) + } + return } diff --git a/webhook.go b/webhook.go index 3fa47bfe1..b8b3abcde 100644 --- a/webhook.go +++ b/webhook.go @@ -36,7 +36,7 @@ type WebhookParams struct { AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` } -// WebhookEdit stores data for editing webhook's message +// WebhookEdit stores data for editing of a webhook message. type WebhookEdit struct { Content string `json:"content,omitempty"` Embeds []*MessageEmbed `json:"embeds,omitempty"` From 5b23e3426d4df2bb4eac17b9ff9f30d86625a41a Mon Sep 17 00:00:00 2001 From: nitroflap Date: Sun, 17 Jan 2021 20:32:52 +0300 Subject: [PATCH 08/28] Some improvements of slash-commands example and slash-commands API --- examples/slash_commands/main.go | 29 +++++----- interaction.go | 6 +-- restapi.go | 95 ++++++++++++++++++++++----------- 3 files changed, 84 insertions(+), 46 deletions(-) diff --git a/examples/slash_commands/main.go b/examples/slash_commands/main.go index f15a9e504..56fd00891 100644 --- a/examples/slash_commands/main.go +++ b/examples/slash_commands/main.go @@ -34,38 +34,43 @@ func main() { client := setup() client.AddHandler(func(s *dgo.Session, i *dgo.InteractionCreate) { - log.Println(dgo.InteractionResponseType(i.Interaction.Data.Options[0].Value.(float64))) - err := s.InteractionRespond(i.Interaction, &dgo.InteractionResponse{ - Type: dgo.InteractionResponseType(i.Interaction.Data.Options[0].Value.(float64)), - Data: &dgo.InteractionApplicationCommandResponseData{ + responseType := dgo.InteractionResponseType(i.Interaction.Data.Options[0].Value.(float64)) + responseData := &dgo.InteractionApplicationCommandResponseData{ TTS: false, Content: "here we go", // Flags: 1 << 6, - }, + } + if responseType == dgo.InteractionResponseACKWithSource || responseType == dgo.InteractionResponseAcknowledge { + responseData = nil + } + log.Println("response parameters:", responseType, responseData) + err := s.InteractionRespond(i.Interaction, &dgo.InteractionResponse{ + Type: responseType, + Data: responseData, }) log.Println("response", err) time.Sleep(time.Second * 2) - err = s.InteractionResponseEdit(i.Interaction, &dgo.WebhookEdit{ + err = s.InteractionResponseEdit("", i.Interaction, &dgo.WebhookEdit{ Content: "here we go!", }) log.Println("response edit", err) time.Sleep(time.Second * 2) - err = s.InteractionResponseDelete(i.Interaction) + err = s.InteractionResponseDelete("", i.Interaction) log.Println("response delete", err) - err = s.InteractionResponseDelete(i.Interaction) + err = s.InteractionResponseDelete("", i.Interaction) log.Println("response delete 2", err) - followupMessage, err := s.FollowupMessageCreate(i.Interaction, true, &dgo.WebhookParams{ + followupMessage, err := s.FollowupMessageCreate("", i.Interaction, true, &dgo.WebhookParams{ Content: "followup messages rule!", }) log.Println("followup message create", followupMessage, err) time.Sleep(time.Second * 3) - err = s.FollowupMessageEdit(i.Interaction, followupMessage.ID, &dgo.WebhookEdit{ + err = s.FollowupMessageEdit("", i.Interaction, followupMessage.ID, &dgo.WebhookEdit{ Content: "that's true", }) log.Println("followup message edit", err) time.Sleep(time.Second * 3) - err = s.FollowupMessageDelete(i.Interaction, followupMessage.ID) + err = s.FollowupMessageDelete("", i.Interaction, followupMessage.ID) log.Println("followup message delete", err) }) @@ -73,7 +78,7 @@ func main() { log.Fatal(err) } - cmd, err := client.ApplicationCommandCreate(&dgo.ApplicationCommand{ + cmd, err := client.ApplicationCommandCreate("", &dgo.ApplicationCommand{ Name: "test-slashes", Description: "Command for testing application commands", Options: []*dgo.ApplicationCommandOption{ diff --git a/interaction.go b/interaction.go index bf3ade6c2..18638ef4e 100644 --- a/interaction.go +++ b/interaction.go @@ -3,7 +3,7 @@ package discordgo import ( "time" ) - +// InteractionDeadline is a deadline for responding to an interaction, if you haven't responded in the time, you won't be able to respond later. const InteractionDeadline = time.Second * 3 // ApplicationCommand is representing application's slash command. @@ -43,7 +43,7 @@ type ApplicationCommandOption struct { Options []*ApplicationCommandOption `json:"options,omitempty"` } -// ApplicationCommandOption is representing slash-command's option choice. +// ApplicationCommandOptionChoice is representing slash-command's option choice. type ApplicationCommandOptionChoice struct { Name string `json:"name,omitempty"` Value interface{} `json:"value,omitempty"` @@ -111,7 +111,7 @@ type InteractionResponse struct { Data *InteractionApplicationCommandResponseData `json:"data,omitempty"` } -// InteractionApplicationCommandCallbackData is callback data for application command interaction. +// InteractionApplicationCommandResponseData is callback data for application command interaction. type InteractionApplicationCommandResponseData struct { TTS bool `json:"tts,omitempty"` Content string `json:"content,omitempty"` diff --git a/restapi.go b/restapi.go index 4c22e22d8..0fb8e3835 100644 --- a/restapi.go +++ b/restapi.go @@ -2170,11 +2170,10 @@ func (s *Session) WebhookExecute(webhookID, token string, wait bool, data *Webho } err = unmarshal(response, &st) - return } -// WebhookMessageEdit edits webhook's message. +// WebhookMessageEdit edits a webhook message. // webhookID : The ID of a webhook // token : The auth token for the webhook // messageID : The ID of message to edit @@ -2186,7 +2185,7 @@ func (s *Session) WebhookMessageEdit(webhookID, token, messageID string, data *W return } -// WebhookMessageEdit deletes webhook's message. +// WebhookMessageDelete deletes a webhook message. // webhookID : The ID of a webhook // token : The auth token for the webhook // messageID : The ID of message to edit @@ -2357,48 +2356,61 @@ func (s *Session) RelationshipsMutualGet(userID string) (mf []*User, err error) // ------------------------------------------------------------------------------------------------ // ApplicationCommandCreate creates an global application command. If guild is specified - creates guild specific application command. -func (s *Session) ApplicationCommandCreate(cmd *ApplicationCommand, guildID string) (rcmd *ApplicationCommand, err error) { +func (s *Session) ApplicationCommandCreate(appID string, cmd *ApplicationCommand, guildID string) (rcmd *ApplicationCommand, err error) { + if appID == "" { + appID = s.State.User.ID + } endpoint := EndpointApplicationGlobalCommands(s.State.User.ID) if guildID != "" { endpoint = EndpointApplicationGuildCommands(s.State.User.ID, guildID) } body, err := s.RequestWithBucketID("POST", endpoint, *cmd, endpoint) - if err != nil { - return + if err == nil { + err = unmarshal(body, &rcmd) } - err = unmarshal(body, &rcmd) return } // ApplicationCommandDelete deletes global application command by id. If guild is specified - deletes guild specific application command. -func (s *Session) ApplicationCommandDelete(cmdID, guildID string) (err error) { - endpoint := EndpointApplicationGlobalCommand(s.State.User.ID, cmdID) +func (s *Session) ApplicationCommandDelete(appID, cmdID, guildID string) (err error) { + if appID == "" { + appID = s.State.User.ID + } + endpoint := EndpointApplicationGlobalCommand(appID, cmdID) if guildID != "" { - endpoint = EndpointApplicationGuildCommand(s.State.User.ID, guildID, cmdID) + endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) } _, err = s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) return } // ApplicationCommandEdit edits global application command by id and replaces with contents of "cmd". If guild is specified - edits guild specific application command. -func (s *Session) ApplicationCommandEdit(cmdID, guildID string, cmd *ApplicationCommand) (oldcmd *ApplicationCommand, err error) { - endpoint := EndpointApplicationGlobalCommand(s.State.User.ID, cmdID) +func (s *Session) ApplicationCommandEdit(appID, cmdID, guildID string, cmd *ApplicationCommand) (oldcmd *ApplicationCommand, err error) { + if appID == "" { + appID = s.State.User.ID + } + endpoint := EndpointApplicationGlobalCommand(appID, cmdID) if guildID != "" { - endpoint = EndpointApplicationGuildCommand(s.State.User.ID, guildID, cmdID) + endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) } body, err := s.RequestWithBucketID("PATCH", endpoint, *cmd, endpoint) - err = unmarshal(body, &oldcmd) + if err == nil { + err = unmarshal(body, &cmd) + } return } // ApplicationCommand retrieves a command by given id and returns it. If guild is specified - retrieves guild specific application command. -func (s *Session) ApplicationCommand(cmdID, guildID string) (cmd *ApplicationCommand, err error) { - endpoint := EndpointApplicationGlobalCommand(s.State.User.ID, cmdID) +func (s *Session) ApplicationCommand(appID, cmdID, guildID string) (cmd *ApplicationCommand, err error) { + if appID == "" { + appID = s.State.User.ID + } + endpoint := EndpointApplicationGlobalCommand(appID, cmdID) if guildID != "" { - endpoint = EndpointApplicationGuildCommand(s.State.User.ID, guildID, cmdID) + endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) } body, err := s.RequestWithBucketID("GET", endpoint, nil, endpoint) @@ -2410,15 +2422,21 @@ func (s *Session) ApplicationCommand(cmdID, guildID string) (cmd *ApplicationCom } // ApplicationCommands retrieves all commands in application. If guild is specified - retrieves all guild specific application commands. -func (s *Session) ApplicationCommands(guildID string) (cmd []*ApplicationCommand, err error) { - endpoint := EndpointApplicationGlobalCommands(s.State.User.ID) +func (s *Session) ApplicationCommands(appID, guildID string) (cmd []*ApplicationCommand, err error) { + if appID == "" { + appID = s.State.User.ID + } + endpoint := EndpointApplicationGlobalCommands(appID) if guildID != "" { - endpoint = EndpointApplicationGuildCommands(s.State.User.ID, guildID) + endpoint = EndpointApplicationGuildCommands(appID, guildID) } body, err := s.RequestWithBucketID("GET", endpoint, nil, endpoint) - err = unmarshal(body, &cmd) + if err == nil { + err = unmarshal(body, &cmd) + } + return } @@ -2430,28 +2448,43 @@ func (s *Session) InteractionRespond(interaction *Interaction, resp *Interaction } // InteractionResponseEdit edits the response to an interaction. -func (s *Session) InteractionResponseEdit(interaction *Interaction, newresp *WebhookEdit) (err error) { - return s.WebhookMessageEdit(s.State.User.ID, interaction.Token, "@original", newresp) +func (s *Session) InteractionResponseEdit(appID string, interaction *Interaction, newresp *WebhookEdit) (err error) { + if appID == "" { + appID = s.State.User.ID + } + return s.WebhookMessageEdit(appID, interaction.Token, "@original", newresp) } // InteractionResponseDelete deletes the response to an interaction. -func (s *Session) InteractionResponseDelete(interaction *Interaction) (err error) { - endpoint := EndpointInteractionResponseActions(s.State.User.ID, interaction.Token) +func (s *Session) InteractionResponseDelete(appID string, interaction *Interaction) (err error) { + if appID == "" { + appID = s.State.User.ID + } + endpoint := EndpointInteractionResponseActions(appID, interaction.Token) _, err = s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) return } // FollowupMessageCreate creates the followup message for an interaction. -func (s *Session) FollowupMessageCreate(interaction *Interaction, wait bool, data *WebhookParams) (st *Message, err error) { - return s.WebhookExecute(s.State.User.ID, interaction.Token, wait, data) +func (s *Session) FollowupMessageCreate(appID string, interaction *Interaction, wait bool, data *WebhookParams) (st *Message, err error) { + if appID == "" { + appID = s.State.User.ID + } + return s.WebhookExecute(appID, interaction.Token, wait, data) } // FollowupMessageEdit edits a followup message of an interaction. -func (s *Session) FollowupMessageEdit(interaction *Interaction, messageID string, data *WebhookEdit) (err error) { - return s.WebhookMessageEdit(s.State.User.ID, interaction.Token, messageID, data) +func (s *Session) FollowupMessageEdit(appID string, interaction *Interaction, messageID string, data *WebhookEdit) (err error) { + if appID == "" { + appID = s.State.User.ID + } + return s.WebhookMessageEdit(appID, interaction.Token, messageID, data) } // FollowupMessageDelete deletes a followup message of an interaction. -func (s *Session) FollowupMessageDelete(interaction *Interaction, messageID string) (err error) { - return s.WebhookMessageDelete(s.State.User.ID, interaction.Token, messageID) +func (s *Session) FollowupMessageDelete(appID string, interaction *Interaction, messageID string) (err error) { + if appID == "" { + appID = s.State.User.ID + } + return s.WebhookMessageDelete(appID, interaction.Token, messageID) } From 1c8faf95dd0b9c73cbd77cf292baf5684ea3ac56 Mon Sep 17 00:00:00 2001 From: nitroflap Date: Sun, 17 Jan 2021 20:52:21 +0300 Subject: [PATCH 09/28] OAuth2 endpoints backward compatibility --- endpoints.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/endpoints.go b/endpoints.go index 9b11b8c3e..6d2a24842 100644 --- a/endpoints.go +++ b/endpoints.go @@ -176,11 +176,18 @@ var ( EndpointEmoji = func(eID string) string { return EndpointCDN + "emojis/" + eID + ".png" } EndpointEmojiAnimated = func(eID string) string { return EndpointCDN + "emojis/" + eID + ".gif" } + EndpointApplications = EndpointAPI + "applications" + EndpointApplication = func(aID string) string { return EndpointApplications + "/" + aID } + EndpointOAuth2 = EndpointAPI + "oauth2/" EndpointOAuth2Applications = EndpointOAuth2 + "applications" - EndpointApplications = EndpointAPI + "applications" - EndpointApplication = func(aID string) string { return EndpointApplications + "/" + aID } EndpointOAuth2Application = func(aID string) string { return EndpointOAuth2Applications + "/" + aID } EndpointOAuth2ApplicationsBot = func(aID string) string { return EndpointOAuth2Applications + "/" + aID + "/bot" } EndpointOAuth2ApplicationAssets = func(aID string) string { return EndpointOAuth2Applications + "/" + aID + "/assets" } + + EndpointOauth2 = EndpointOAuth2 + EndpointOauth2Applications = EndpointOAuth2Applications + EndpointOauth2Application = EndpointOAuth2Application + EndpointOauth2ApplicationsBot = EndpointOAuth2ApplicationsBot + EndpointOauth2ApplicationAssets = EndpointOAuth2ApplicationAssets ) From 9c6bf001db7e0748d49d1c02ffb17b18dece553d Mon Sep 17 00:00:00 2001 From: nitroflap Date: Sun, 17 Jan 2021 20:55:20 +0300 Subject: [PATCH 10/28] Gofmt fix --- examples/slash_commands/main.go | 8 ++++---- interaction.go | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/slash_commands/main.go b/examples/slash_commands/main.go index 56fd00891..6e03581f5 100644 --- a/examples/slash_commands/main.go +++ b/examples/slash_commands/main.go @@ -36,10 +36,10 @@ func main() { client.AddHandler(func(s *dgo.Session, i *dgo.InteractionCreate) { responseType := dgo.InteractionResponseType(i.Interaction.Data.Options[0].Value.(float64)) responseData := &dgo.InteractionApplicationCommandResponseData{ - TTS: false, - Content: "here we go", - // Flags: 1 << 6, - } + TTS: false, + Content: "here we go", + // Flags: 1 << 6, + } if responseType == dgo.InteractionResponseACKWithSource || responseType == dgo.InteractionResponseAcknowledge { responseData = nil } diff --git a/interaction.go b/interaction.go index 18638ef4e..ccadc4b2a 100644 --- a/interaction.go +++ b/interaction.go @@ -3,6 +3,7 @@ package discordgo import ( "time" ) + // InteractionDeadline is a deadline for responding to an interaction, if you haven't responded in the time, you won't be able to respond later. const InteractionDeadline = time.Second * 3 From f554a2f8ec76cc2f33468d57e7445e79cacc7476 Mon Sep 17 00:00:00 2001 From: nitroflap Date: Tue, 19 Jan 2021 21:12:59 +0300 Subject: [PATCH 11/28] Requested fixes and documentation improvement for application commands --- interaction.go | 23 ++++++-------- restapi.go | 84 ++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 70 insertions(+), 37 deletions(-) diff --git a/interaction.go b/interaction.go index ccadc4b2a..e55ffc354 100644 --- a/interaction.go +++ b/interaction.go @@ -21,9 +21,7 @@ type ApplicationCommandOptionType uint8 // Application command option types. const ( - _ = ApplicationCommandOptionType(iota) - - ApplicationCommandOptionSubCommand + ApplicationCommandOptionSubCommand = ApplicationCommandOptionType(iota + 1) ApplicationCommandOptionSubCommandGroup ApplicationCommandOptionString ApplicationCommandOptionInteger @@ -53,11 +51,9 @@ type ApplicationCommandOptionChoice struct { // InteractionType is representing interaction type. type InteractionType uint8 +// Interaction types const ( - _ = InteractionType(iota) - // InteractionPing is type of interaction for ping. - InteractionPing - // InteractionApplicationCommand is type of interaction for application commands. + InteractionPing = InteractionType(iota + 1) InteractionApplicationCommand ) @@ -93,16 +89,15 @@ type InteractionResponseType uint8 // Interaction response types. const ( - _ = InteractionResponseType(iota) - // InteractionResponsePong is an interaction response type when you need to just ACK a "Ping". - InteractionResponsePong - // InteractionResponsePong is an interaction response type when you need to ACK a command without sending a message, eating the user's input. + // InteractionResponsePong is for ACK ping event. + InteractionResponsePong = InteractionResponseType(iota + 1) + // InteractionResponseAcknowledge is for ACK a command without sending a message, eating the user's input. InteractionResponseAcknowledge - // InteractionResponsePong is an interaction response type when you need to respond with a message, eating the user's input. + // InteractionResponseChannelMessage is for responding with a message, eating the user's input. InteractionResponseChannelMessage - // InteractionResponsePong is an interaction response type when you need to respond with a message, showing the user's input. + // InteractionResponseChannelMessageWithSource is for responding with a message, showing the user's input. InteractionResponseChannelMessageWithSource - // InteractionResponsePong is an interaction response type when you need to ACK a command without sending a message, showing the user's input. + // InteractionResponseACKWithSource is for ACK a command without sending a message, showing the user's input. InteractionResponseACKWithSource ) diff --git a/restapi.go b/restapi.go index 0fb8e3835..f94255511 100644 --- a/restapi.go +++ b/restapi.go @@ -2355,8 +2355,11 @@ func (s *Session) RelationshipsMutualGet(userID string) (mf []*User, err error) // Functions specific to application (slash) commands // ------------------------------------------------------------------------------------------------ -// ApplicationCommandCreate creates an global application command. If guild is specified - creates guild specific application command. -func (s *Session) ApplicationCommandCreate(appID string, cmd *ApplicationCommand, guildID string) (rcmd *ApplicationCommand, err error) { +// ApplicationCommandCreate creates an global application command and returns it. +// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// guildID : Guild ID to create guild-specific application command. +// cmd : New application command data. +func (s *Session) ApplicationCommandCreate(appID string, guildID string, cmd *ApplicationCommand) (rcmd *ApplicationCommand, err error) { if appID == "" { appID = s.State.User.ID } @@ -2365,15 +2368,20 @@ func (s *Session) ApplicationCommandCreate(appID string, cmd *ApplicationCommand endpoint = EndpointApplicationGuildCommands(s.State.User.ID, guildID) } body, err := s.RequestWithBucketID("POST", endpoint, *cmd, endpoint) - if err == nil { - err = unmarshal(body, &rcmd) + if err != nil { + return } + err = unmarshal(body, &rcmd) return } -// ApplicationCommandDelete deletes global application command by id. If guild is specified - deletes guild specific application command. -func (s *Session) ApplicationCommandDelete(appID, cmdID, guildID string) (err error) { +// ApplicationCommandEdit edits application command and returns old command data. +// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// cmdID : Application command ID to edit. +// guildID : Guild ID to edit guild-specific application command. +// cmd : Updated application command data. +func (s *Session) ApplicationCommandEdit(appID, cmdID, guildID string, cmd *ApplicationCommand) (oldcmd *ApplicationCommand, err error) { if appID == "" { appID = s.State.User.ID } @@ -2381,12 +2389,21 @@ func (s *Session) ApplicationCommandDelete(appID, cmdID, guildID string) (err er if guildID != "" { endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) } - _, err = s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) + + body, err := s.RequestWithBucketID("PATCH", endpoint, *cmd, endpoint) + if err != nil { + return + } + + err = unmarshal(body, &cmd) return } -// ApplicationCommandEdit edits global application command by id and replaces with contents of "cmd". If guild is specified - edits guild specific application command. -func (s *Session) ApplicationCommandEdit(appID, cmdID, guildID string, cmd *ApplicationCommand) (oldcmd *ApplicationCommand, err error) { +// ApplicationCommandDelete deletes application command by id. +// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// cmdID : Application command ID to delete. +// guildID : Guild ID to delete guild-specific application command. +func (s *Session) ApplicationCommandDelete(appID, cmdID, guildID string) (err error) { if appID == "" { appID = s.State.User.ID } @@ -2394,16 +2411,15 @@ func (s *Session) ApplicationCommandEdit(appID, cmdID, guildID string, cmd *Appl if guildID != "" { endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) } - - body, err := s.RequestWithBucketID("PATCH", endpoint, *cmd, endpoint) - - if err == nil { - err = unmarshal(body, &cmd) - } + _, err = s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) return } -// ApplicationCommand retrieves a command by given id and returns it. If guild is specified - retrieves guild specific application command. + +// ApplicationCommand retrieves an application command by given id. +// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// cmdID : Application command ID. +// guildID : Guild ID to retrieve guild-specific application command. func (s *Session) ApplicationCommand(appID, cmdID, guildID string) (cmd *ApplicationCommand, err error) { if appID == "" { appID = s.State.User.ID @@ -2414,14 +2430,17 @@ func (s *Session) ApplicationCommand(appID, cmdID, guildID string) (cmd *Applica } body, err := s.RequestWithBucketID("GET", endpoint, nil, endpoint) - if err == nil { - err = unmarshal(body, &cmd) + if err != nil { + return } - + + err = unmarshal(body, &cmd) return } -// ApplicationCommands retrieves all commands in application. If guild is specified - retrieves all guild specific application commands. +// ApplicationCommands retrieves all commands in application. +// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// guildID : Guild ID to retrieve all guild-specific application commands. func (s *Session) ApplicationCommands(appID, guildID string) (cmd []*ApplicationCommand, err error) { if appID == "" { appID = s.State.User.ID @@ -2432,15 +2451,18 @@ func (s *Session) ApplicationCommands(appID, guildID string) (cmd []*Application } body, err := s.RequestWithBucketID("GET", endpoint, nil, endpoint) - - if err == nil { - err = unmarshal(body, &cmd) + if err != nil { + return } + err = unmarshal(body, &cmd) return } // InteractionRespond creates the response to an interaction. +// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// interaction : Interaction instance. +// resp : Response message data. func (s *Session) InteractionRespond(interaction *Interaction, resp *InteractionResponse) (err error) { endpoint := EndpointInteractionResponse(interaction.ID, interaction.Token) _, err = s.RequestWithBucketID("POST", endpoint, *resp, endpoint) @@ -2448,6 +2470,9 @@ func (s *Session) InteractionRespond(interaction *Interaction, resp *Interaction } // InteractionResponseEdit edits the response to an interaction. +// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// interaction : Interaction instance. +// newresp : Updated response message data. func (s *Session) InteractionResponseEdit(appID string, interaction *Interaction, newresp *WebhookEdit) (err error) { if appID == "" { appID = s.State.User.ID @@ -2456,6 +2481,8 @@ func (s *Session) InteractionResponseEdit(appID string, interaction *Interaction } // InteractionResponseDelete deletes the response to an interaction. +// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// interaction : Interaction instance. func (s *Session) InteractionResponseDelete(appID string, interaction *Interaction) (err error) { if appID == "" { appID = s.State.User.ID @@ -2466,6 +2493,10 @@ func (s *Session) InteractionResponseDelete(appID string, interaction *Interacti } // FollowupMessageCreate creates the followup message for an interaction. +// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// interaction : Interaction instance. +// wait : Waits for server confirmation of message send and ensures that the return struct is populated (it is nil otherwise) +// data : Data of the message to send. func (s *Session) FollowupMessageCreate(appID string, interaction *Interaction, wait bool, data *WebhookParams) (st *Message, err error) { if appID == "" { appID = s.State.User.ID @@ -2474,6 +2505,10 @@ func (s *Session) FollowupMessageCreate(appID string, interaction *Interaction, } // FollowupMessageEdit edits a followup message of an interaction. +// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// interaction : Interaction instance. +// messageID : The followup message ID. +// data : Data to update the message func (s *Session) FollowupMessageEdit(appID string, interaction *Interaction, messageID string, data *WebhookEdit) (err error) { if appID == "" { appID = s.State.User.ID @@ -2482,6 +2517,9 @@ func (s *Session) FollowupMessageEdit(appID string, interaction *Interaction, me } // FollowupMessageDelete deletes a followup message of an interaction. +// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// interaction : Interaction instance. +// messageID : The followup message ID. func (s *Session) FollowupMessageDelete(appID string, interaction *Interaction, messageID string) (err error) { if appID == "" { appID = s.State.User.ID From 2028ce617e0aa97d67b86112b0820d678b8ff560 Mon Sep 17 00:00:00 2001 From: nitroflap Date: Tue, 16 Feb 2021 17:54:28 +0300 Subject: [PATCH 12/28] Some fixes --- .gitignore | 2 +- endpoints.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e860b2910..681a96b19 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ .idea/ # Environment variables. Useful for examples. -.env \ No newline at end of file +.env diff --git a/endpoints.go b/endpoints.go index 72ed7b7ae..8cc8d966a 100644 --- a/endpoints.go +++ b/endpoints.go @@ -186,6 +186,7 @@ var ( EndpointOAuth2ApplicationsBot = func(aID string) string { return EndpointOAuth2Applications + "/" + aID + "/bot" } EndpointOAuth2ApplicationAssets = func(aID string) string { return EndpointOAuth2Applications + "/" + aID + "/assets" } + // TODO: Deprecated, remove in the next release EndpointOauth2 = EndpointOAuth2 EndpointOauth2Applications = EndpointOAuth2Applications EndpointOauth2Application = EndpointOAuth2Application From 75a2e2d626379bad14488802759ca12d12529da9 Mon Sep 17 00:00:00 2001 From: nitroflap Date: Tue, 16 Feb 2021 17:58:16 +0300 Subject: [PATCH 13/28] New and more interesting example of slash-commands usage, merging "interaction.go" and "interactions.go" into a single file. And some fixes. --- examples/slash_commands/main.go | 440 +++++++++++++++++++++++++------- interaction.go | 118 --------- interactions.go | 209 +++++++++++++++ restapi.go | 4 +- 4 files changed, 556 insertions(+), 215 deletions(-) delete mode 100644 interaction.go diff --git a/examples/slash_commands/main.go b/examples/slash_commands/main.go index 6e03581f5..186ea0515 100644 --- a/examples/slash_commands/main.go +++ b/examples/slash_commands/main.go @@ -2,124 +2,374 @@ package main import ( "flag" - dgo "github.com/bwmarrin/discordgo" + "fmt" "log" "os" "os/signal" - "syscall" "time" + + "github.com/bwmarrin/discordgo" ) +// Bot parameters var ( - botToken = flag.String("bot_tok", "", "Bot token") - testGuild = flag.String("test_guild", "", "Guild for testing the bot") + GuildID = flag.String("guild", "", "Test guild ID. If not passed - bot registers commands globally") + BotToken = flag.String("token", "", "Bot access token") + RemoveCommands = flag.Bool("rmcmd", true, "Remove all commands after shutdowning or not") ) -func setup() *dgo.Session { - client, err := dgo.New("Bot " + *botToken) - if err != nil { - log.Fatal(err) - } +var s *discordgo.Session - client.AddHandler(func(*dgo.Session, *dgo.Ready) { - log.Println("Bot is up!") - }) +func init() { flag.Parse() } - return client +func init() { + var err error + s, err = discordgo.New("Bot " + *BotToken) + if err != nil { + log.Fatalf("Invalid bot parameters: %v", err) + } } -func main() { - flag.Parse() - - client := setup() +var ( + commands = []*discordgo.ApplicationCommand { + { + Name: "basic-command", + // All commands and options must have an description + // Commands/options without description will fail the registration + // of the command. + Description: "Basic command", + }, + { + Name: "options", + Description: "Command for demonstrating options", + Options: []*discordgo.ApplicationCommandOption{ - client.AddHandler(func(s *dgo.Session, i *dgo.InteractionCreate) { - responseType := dgo.InteractionResponseType(i.Interaction.Data.Options[0].Value.(float64)) - responseData := &dgo.InteractionApplicationCommandResponseData{ - TTS: false, - Content: "here we go", - // Flags: 1 << 6, - } - if responseType == dgo.InteractionResponseACKWithSource || responseType == dgo.InteractionResponseAcknowledge { - responseData = nil - } - log.Println("response parameters:", responseType, responseData) - err := s.InteractionRespond(i.Interaction, &dgo.InteractionResponse{ - Type: responseType, - Data: responseData, - }) - log.Println("response", err) - time.Sleep(time.Second * 2) - err = s.InteractionResponseEdit("", i.Interaction, &dgo.WebhookEdit{ - Content: "here we go!", - }) - log.Println("response edit", err) - time.Sleep(time.Second * 2) - err = s.InteractionResponseDelete("", i.Interaction) - log.Println("response delete", err) - err = s.InteractionResponseDelete("", i.Interaction) - log.Println("response delete 2", err) - - followupMessage, err := s.FollowupMessageCreate("", i.Interaction, true, &dgo.WebhookParams{ - Content: "followup messages rule!", - }) - log.Println("followup message create", followupMessage, err) - time.Sleep(time.Second * 3) - err = s.FollowupMessageEdit("", i.Interaction, followupMessage.ID, &dgo.WebhookEdit{ - Content: "that's true", - }) - log.Println("followup message edit", err) - time.Sleep(time.Second * 3) - err = s.FollowupMessageDelete("", i.Interaction, followupMessage.ID) - log.Println("followup message delete", err) - }) + { + Type: discordgo.ApplicationCommandOptionString, + Name: "string-option", + Description: "String option", + Required: true, + }, + { + Type: discordgo.ApplicationCommandOptionInteger, + Name: "integer-option", + Description: "Integer option", + Required: true, + }, + { + Type: discordgo.ApplicationCommandOptionBoolean, + Name: "bool-option", + Description: "Boolean option", + Required: true, + }, - if err := client.Open(); err != nil { - log.Fatal(err) - } + // Required options must be listed first, because + // like everyone knows - optional parameters is on the back. + // The same concept applies to Discord's Slash-commands API + + { + Type: discordgo.ApplicationCommandOptionChannel, + Name: "channel-option", + Description: "Channel option", + Required: false, + }, + { + Type: discordgo.ApplicationCommandOptionUser, + Name: "user-option", + Description: "User option", + Required: false, + }, + { + Type: discordgo.ApplicationCommandOptionRole, + Name: "role-option", + Description: "Role option", + Required: false, + }, + }, + }, + { + Name: "subcommands", + Description: "Subcommands and command groups example", + Options: []*discordgo.ApplicationCommandOption{ + // When command have subcommands/subcommand groups + // It must not have top-level options, they aren't accesible in the UI + // in this case (at least, yet), so if command is with + // subcommands/subcommand groups registering top-level options + // will fail the registration of the command - cmd, err := client.ApplicationCommandCreate("", &dgo.ApplicationCommand{ - Name: "test-slashes", - Description: "Command for testing application commands", - Options: []*dgo.ApplicationCommandOption{ - { - Type: dgo.ApplicationCommandOptionInteger, - Name: "typ", - Description: "Response type", - Choices: []*dgo.ApplicationCommandOptionChoice{ - { - Name: "ACK", - Value: dgo.InteractionResponseAcknowledge, - }, - { - Name: "ACK with source", - Value: dgo.InteractionResponseACKWithSource, + { + Name: "scmd-grp", + Description: "Subcommands group", + Options: []*discordgo.ApplicationCommandOption{ + // Also, subcommand groups isn't capable of + // containg options, by the name of them, you can see + // they can contain only subcommands + { + Name: "nst-subcmd", + Description: "Nested subcommand", + Type: discordgo.ApplicationCommandOptionSubCommand, + }, }, - { - Name: "Channel message", - Value: dgo.InteractionResponseChannelMessage, - }, - { - Name: "Channel message with source", - Value: dgo.InteractionResponseChannelMessageWithSource, + Type: discordgo.ApplicationCommandOptionSubCommandGroup, + }, + // Also, you can create both subcommand groups and subcommands + // in the command at the same time. But, there's some limits to + // nesting, count of subcommands (top level and nested) and options. + // Read the intro of slash-commands docs on Discord dev portal + // to get more information + { + Name: "subcmd", + Description: "Top-level subcommand", + Type: discordgo.ApplicationCommandOptionSubCommand, + }, + }, + }, + { + Name: "responses", + Description: "Interaction responses testing initiative", + Options: []*discordgo.ApplicationCommandOption{ + { + Name: "resp-type", + Description: "Response type", + Type: discordgo.ApplicationCommandOptionInteger, + Choices: []*discordgo.ApplicationCommandOptionChoice{ + { + Name: "Acknowledge", + Value: 2, + }, + { + Name: "Channel message", + Value: 3, + }, + { + Name: "Channel message with source", + Value: 4, + }, + { + Name: "Acknowledge with source", + Value: 5, + }, }, }, - Required: false, }, }, - }, *testGuild) - - if err != nil { - log.Fatal(err) + { + Name: "followups", + Description: "Followup messages", + }, } + commandHandlers = map[string]func(s *discordgo.Session, i *discordgo.InteractionCreate) { + "basic-command": func(s *discordgo.Session, i *discordgo.InteractionCreate) { + s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionApplicationCommandResponseData{ + Content: "Hey there! Congratulations, you just executed your first slash command", + }, + }) + }, + "options": func(s *discordgo.Session, i *discordgo.InteractionCreate) { + margs := []interface{}{ + // Here we need to convert raw interface{} value to wanted type. + // 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(), + } + msgformat := + ` Now you just leared how to use command options. Take a look to the value of which you've just entered: + > string_option: %s + > integer_option: %d + > bool_option: %v +` + if len(i.Data.Options) >= 4 { + margs = append(margs, i.Data.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) + msgformat += "> user-option: <@%s>\n" + } + if len(i.Data.Options) >= 6 { + margs = append(margs, i.Data.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{ + Content: fmt.Sprintf( + msgformat, + margs..., + ), + }, + }) + }, + "subcommands": func(s *discordgo.Session, i *discordgo.InteractionCreate) { + content := "" + + // As you can see, the name of subcommand (nested, top-level) or subcommand group + // is provided through arguments. + switch i.Data.Options[0].Name { + case "subcmd": + content = + "The top-level subcommand is executed. Now try to execute nested one." + default: + if i.Data.Options[0].Name != "scmd-grp" { + return + } + switch i.Data.Options[0].Options[0].Name { + case "nst-subcmd": + content = "Nice, now you know how to execute nested commands too" + default: + // I added this in the case something might go wrong + content = "Oops, something gone wrong.\n" + + "Hol' up, you aren't supposed to see this message." + } + } + s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionApplicationCommandResponseData{ + Content: content, + }, + }) + }, + "responses": func(s *discordgo.Session, i *discordgo.InteractionCreate) { + // Responses to a command is really important thing. + // First of all, because you need to react to the interaction + // by sending the response in 3 seconds after receiving, otherwise + // interaction will be considered invalid and you can no longer + // use interaction token and ID for responding to the user's request - log.Println("Old command: ", cmd) - log.Println("Created command: ", cmd) + content := "" + // As you can see, response type names saying by themselvs + // how they're used, but for those who want to get + // more information - read the official documentation + switch i.Data.Options[0].IntValue() { + case int64(discordgo.InteractionResponseChannelMessage): + content = + "Well, you just responded to an interaction, and sent a message.\n" + + "That's all what I wanted to say, yeah." + content += + "\nAlso... you can edit your response, wait 5 seconds and this message will be changed" + case int64(discordgo.InteractionResponseChannelMessageWithSource): + content = + "You just responded to an interaction, sent a message and showed the original one. " + + "Congratulations!" + content += + "\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()), + }) + if err != nil { + s.FollowupMessageCreate("", i.Interaction, true, &discordgo.WebhookParams{ + Content: "Something gone wrong", + }) + } + return + } - ch := make(chan os.Signal, 1) - signal.Notify(ch, syscall.SIGINT, os.Interrupt) - <-ch - if err := client.Close(); err != nil { - log.Fatal(err) + err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseType(i.Data.Options[0].IntValue()), + Data: &discordgo.InteractionApplicationCommandResponseData{ + Content: content, + }, + }) + if err != nil { + s.FollowupMessageCreate("", i.Interaction, true, &discordgo.WebhookParams{ + Content: "Something gone wrong", + }) + return + } + time.AfterFunc(time.Second * 5, func() { + err = s.InteractionResponseEdit("", i.Interaction, &discordgo.WebhookEdit{ + Content: content + "\n\nWell, now you know how to create and edit responses. " + + "But you still don't know how to delete them... so... wait 10 seconds and this " + + "message will be deleted.", + }) + if err != nil { + s.FollowupMessageCreate("", i.Interaction, true, &discordgo.WebhookParams{ + Content: "Something gone wrong", + }) + return + } + time.Sleep(time.Second * 10) + s.InteractionResponseDelete("", i.Interaction) + }) + }, + "followups": func(s *discordgo.Session, i *discordgo.InteractionCreate) { + // Followup messages is basically regular messages (you can create as many of them as you wish), + // but working as they is created by webhooks and their functional + // is for handling additional messages after sending response. + + s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionApplicationCommandResponseData{ + // Note: this isn't documented, but you can use that if you want to. + // This flag just allows to create messages visible only for the caller (user who triggered the command) + // of the command + Flags: 1 << 6, + Content: "Surprise!", + }, + }) + msg, err := s.FollowupMessageCreate("", i.Interaction, true, &discordgo.WebhookParams{ + Content: "Followup message has created, after 5 seconds it will be edited", + }) + if err != nil { + s.FollowupMessageCreate("", i.Interaction, true, &discordgo.WebhookParams{ + Content: "Something gone wrong", + }) + return + } + time.Sleep(time.Second * 5) + + s.FollowupMessageEdit("", i.Interaction, msg.ID, &discordgo.WebhookEdit{ + Content: "Now original message is gone and after 10 seconds this message will ~~self-destruct~~ be deleted.", + }) + + time.Sleep(time.Second * 10) + + s.FollowupMessageDelete ("", i.Interaction, msg.ID) + + s.FollowupMessageCreate("", i.Interaction, true, &discordgo.WebhookParams{ + Content: "For those, who didn't skip anything and followed tutorial along fairly, " + + "take a unicorn :unicorn: as reward!\n" + + "Also, as bonus..., look at the original interaction response :D", + }) + }, } +) + +func init() { + s.AddHandler(func (s *discordgo.Session, i *discordgo.InteractionCreate) { + if h, ok := commandHandlers[i.Data.Name]; ok { + h(s, i) + } + }) } + +func main() { + s.AddHandler(func (s *discordgo.Session, r *discordgo.Ready) { + log.Println("Bot is up!") + }) + err := s.Open() + if err != nil { + log.Fatalf("Cannot open the session: %v", err) + } + + for _, v := range commands { + _, err := s.ApplicationCommandCreate("", *GuildID, v) + if err != nil { + log.Panicf("Cannot create '%v' command: %v", v.Name, err) + } + } + + defer s.Close() + + stop := make(chan os.Signal) + signal.Notify(stop, os.Interrupt) + <-stop + log.Println("Gracefully shutdowning") +} \ No newline at end of file diff --git a/interaction.go b/interaction.go deleted file mode 100644 index e55ffc354..000000000 --- a/interaction.go +++ /dev/null @@ -1,118 +0,0 @@ -package discordgo - -import ( - "time" -) - -// InteractionDeadline is a deadline for responding to an interaction, if you haven't responded in the time, you won't be able to respond later. -const InteractionDeadline = time.Second * 3 - -// ApplicationCommand is representing application's slash command. -type ApplicationCommand struct { - ID string `json:"id,omitempty"` - ApplicationID string `json:"application_id,omitempty"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - Options []*ApplicationCommandOption `json:"options,omitempty"` -} - -// ApplicationCommandOptionType is type of an slash-command's option. -type ApplicationCommandOptionType uint8 - -// Application command option types. -const ( - ApplicationCommandOptionSubCommand = ApplicationCommandOptionType(iota + 1) - ApplicationCommandOptionSubCommandGroup - ApplicationCommandOptionString - ApplicationCommandOptionInteger - ApplicationCommandOptionBoolean - ApplicationCommandOptionUser - ApplicationCommandOptionChannel - ApplicationCommandOptionRole -) - -// ApplicationCommandOption is representing an option/subcommand/subcommands group. -type ApplicationCommandOption struct { - Type ApplicationCommandOptionType `json:"type,omitempty"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - Default bool `json:"default,omitempty"` - Required bool `json:"required,omitempty"` - Choices []*ApplicationCommandOptionChoice `json:"choices,omitempty"` - Options []*ApplicationCommandOption `json:"options,omitempty"` -} - -// ApplicationCommandOptionChoice is representing slash-command's option choice. -type ApplicationCommandOptionChoice struct { - Name string `json:"name,omitempty"` - Value interface{} `json:"value,omitempty"` -} - -// InteractionType is representing interaction type. -type InteractionType uint8 - -// Interaction types -const ( - InteractionPing = InteractionType(iota + 1) - InteractionApplicationCommand -) - -// Interaction is representing interaction with application. -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"` - Member *Member `json:"member"` - Token string `json:"token"` - Version int `json:"version"` -} - -// ApplicationCommandInteractionData is representing interaction data for application command. -type ApplicationCommandInteractionData struct { - ID string - Name string - Options []*ApplicationCommandInteractionDataOption -} - -// ApplicationCommandInteractionDataOption is representing an option of application's command. -type ApplicationCommandInteractionDataOption struct { - Name string `json:"name,omitempty"` - // Contains the value specified by InteractionType - Value interface{} `json:"value,omitempty"` - Options []*ApplicationCommandInteractionDataOption `json:"options,omitempty"` -} - -// InteractionResponseType is type of interaction response. -type InteractionResponseType uint8 - -// Interaction response types. -const ( - // InteractionResponsePong is for ACK ping event. - InteractionResponsePong = InteractionResponseType(iota + 1) - // InteractionResponseAcknowledge is for ACK a command without sending a message, eating the user's input. - InteractionResponseAcknowledge - // InteractionResponseChannelMessage is for responding with a message, eating the user's input. - InteractionResponseChannelMessage - // InteractionResponseChannelMessageWithSource is for responding with a message, showing the user's input. - InteractionResponseChannelMessageWithSource - // InteractionResponseACKWithSource is for ACK a command without sending a message, showing the user's input. - InteractionResponseACKWithSource -) - -// InteractionResponse is representing response for interaction with application. -type InteractionResponse struct { - Type InteractionResponseType `json:"type,omitempty"` - Data *InteractionApplicationCommandResponseData `json:"data,omitempty"` -} - -// InteractionApplicationCommandResponseData is callback data for application command interaction. -type InteractionApplicationCommandResponseData struct { - TTS bool `json:"tts,omitempty"` - Content string `json:"content,omitempty"` - Embeds []*MessageEmbed `json:"embeds,omitempty"` - AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` - - Flags uint64 `json:"flags,omitempty"` // NOTE: Undocumented feature, be careful with it. -} diff --git a/interactions.go b/interactions.go index 6fc2f55ed..265cd87d4 100644 --- a/interactions.go +++ b/interactions.go @@ -7,8 +7,217 @@ import ( "io" "io/ioutil" "net/http" + "time" + // "fmt" ) +// InteractionDeadline is a deadline for responding to an interaction, if you haven't responded in the time, you won't be able to respond later. +const InteractionDeadline = time.Second * 3 + +// ApplicationCommand is representing application's slash command. +type ApplicationCommand struct { + ID string `json:"id"` + ApplicationID string `json:"application_id,omitempty"` + Name string `json:"name"` + Description string `json:"description,omitempty"` + Options []*ApplicationCommandOption `json:"options"` +} + +// ApplicationCommandOptionType is type of an slash-command's option. +type ApplicationCommandOptionType uint8 + +// Application command option types. +const ( + ApplicationCommandOptionSubCommand = ApplicationCommandOptionType(iota + 1) + ApplicationCommandOptionSubCommandGroup + ApplicationCommandOptionString + ApplicationCommandOptionInteger + ApplicationCommandOptionBoolean + ApplicationCommandOptionUser + ApplicationCommandOptionChannel + ApplicationCommandOptionRole +) + +// ApplicationCommandOption is representing an option/subcommand/subcommands group. +type ApplicationCommandOption struct { + Type ApplicationCommandOptionType `json:"type"` + Name string `json:"name"` + Description string `json:"description,omitempty"` + // Default bool `json:"default"` + Required bool `json:"required"` + Choices []*ApplicationCommandOptionChoice `json:"choices"` + Options []*ApplicationCommandOption `json:"options"` +} + +// ApplicationCommandOptionChoice is representing slash-command's option choice. +type ApplicationCommandOptionChoice struct { + Name string `json:"name"` + Value interface{} `json:"value"` +} + +// InteractionType is representing interaction type. +type InteractionType uint8 + +// Interaction types +const ( + InteractionPing = InteractionType(iota + 1) + InteractionApplicationCommand +) + +// Interaction is representing interaction with application. +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"` + Member *Member `json:"member"` + Token string `json:"token"` + Version int `json:"version"` +} + +// ApplicationCommandInteractionData is representing interaction data for application command. +type ApplicationCommandInteractionData struct { + ID string `json:"id"` + Name string `json:"name"` + Options []*ApplicationCommandInteractionDataOption `json:"options"` +} + +// ApplicationCommandInteractionDataOption is representing an option of application's command. +type ApplicationCommandInteractionDataOption struct { + Name string `json:"name"` + // Contains the value specified by InteractionType + Value interface{} `json:"value,omitempty"` + Options []*ApplicationCommandInteractionDataOption `json:"options,omitempty"` +} + +func (o ApplicationCommandInteractionDataOption) IntValue() (i int64) { + if v, ok := o.Value.(float64); ok { + i = int64(v) + } + return +} + +func (o ApplicationCommandInteractionDataOption) UintValue() (i uint64) { + if v, ok := o.Value.(float64); ok { + i = uint64(v) + } + return +} + +func (o ApplicationCommandInteractionDataOption) FloatValue() (n float64) { + if v, ok := o.Value.(float64); ok { + n = float64(v) + } + return +} + +func (o ApplicationCommandInteractionDataOption) StringValue() (s string) { + if v, ok := o.Value.(string); ok { + s = v + } + return +} +func (o ApplicationCommandInteractionDataOption) BoolValue() (b bool) { + if v, ok := o.Value.(bool); ok { + b = v + } + return +} + +func (o ApplicationCommandInteractionDataOption) ChannelValue(s *Session) (ch *Channel) { + chanID := o.StringValue() + if chanID == "" { return } + + if s == nil { + return &Channel {ID: chanID} + } + + ch, err := s.State.Channel(chanID) + if err != nil { + ch, err = s.Channel(chanID) + } + + return +} + +func (o ApplicationCommandInteractionDataOption) RoleValue(s *Session, gID string) (r *Role) { + roleID := o.StringValue() + if roleID == "" { return } + + if s == nil || gID == "" { + return &Role { ID: roleID } + } + + var err error + r, err = s.State.Role(roleID, gID) + if err != nil { + roles, err := s.GuildRoles(gID) + if err != nil { + return + } + for _, r = range roles { + if r.ID == roleID { + return + } + } + r = nil + } + + return +} + +func (o ApplicationCommandInteractionDataOption) UserValue(s *Session) (u *User) { + userID := o.StringValue() + if userID == "" { return } + + if s == nil { + return &User { ID: userID } + } + + u, _ = s.User(userID) + + return +} + +// func (o ApplicationCommandInteractionDataOption) String() string { +// return fmt.Sprintf("%v", o.Value) +// } + +// InteractionResponseType is type of interaction response. +type InteractionResponseType uint8 + +// Interaction response types. +const ( + // InteractionResponsePong is for ACK ping event. + InteractionResponsePong = InteractionResponseType(iota + 1) + // InteractionResponseAcknowledge is for ACK a command without sending a message, eating the user's input. + InteractionResponseAcknowledge + // InteractionResponseChannelMessage is for responding with a message, eating the user's input. + InteractionResponseChannelMessage + // InteractionResponseChannelMessageWithSource is for responding with a message, showing the user's input. + InteractionResponseChannelMessageWithSource + // InteractionResponseACKWithSource is for ACK a command without sending a message, showing the user's input. + InteractionResponseACKWithSource +) + +// InteractionResponse is representing response for interaction with application. +type InteractionResponse struct { + Type InteractionResponseType `json:"type,omitempty"` + Data *InteractionApplicationCommandResponseData `json:"data,omitempty"` +} + +// InteractionApplicationCommandResponseData is callback data for application command interaction. +type InteractionApplicationCommandResponseData struct { + TTS bool `json:"tts,omitempty"` + Content string `json:"content,omitempty"` + Embeds []*MessageEmbed `json:"embeds,omitempty"` + AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` + + Flags uint64 `json:"flags,omitempty"` // NOTE: Undocumented feature, be careful with it. +} + + // VerifyInteraction implements message verification of the discord interactions api // signing algorithm, as documented here: // https://discord.com/developers/docs/interactions/slash-commands#security-and-authorization diff --git a/restapi.go b/restapi.go index 3c6d731d4..6f1c117aa 100644 --- a/restapi.go +++ b/restapi.go @@ -2414,7 +2414,7 @@ func (s *Session) ApplicationCommandEdit(appID, cmdID, guildID string, cmd *Appl // appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. // cmdID : Application command ID to delete. // guildID : Guild ID to delete guild-specific application command. -func (s *Session) ApplicationCommandDelete(appID, cmdID, guildID string) (err error) { +func (s *Session) ApplicationCommandDelete(appID, guildID, cmdID string) (err error) { if appID == "" { appID = s.State.User.ID } @@ -2431,7 +2431,7 @@ func (s *Session) ApplicationCommandDelete(appID, cmdID, guildID string) (err er // appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. // cmdID : Application command ID. // guildID : Guild ID to retrieve guild-specific application command. -func (s *Session) ApplicationCommand(appID, cmdID, guildID string) (cmd *ApplicationCommand, err error) { +func (s *Session) ApplicationCommand(appID, guildID, cmdID string) (cmd *ApplicationCommand, err error) { if appID == "" { appID = s.State.User.ID } From f5375a072d27091e7e69028f197ec5cc3fb1119c Mon Sep 17 00:00:00 2001 From: nitroflap Date: Tue, 16 Feb 2021 18:30:53 +0300 Subject: [PATCH 14/28] Gofmt and documentation fixes --- examples/slash_commands/main.go | 118 ++++++++++++++++---------------- interactions.go | 51 +++++++++----- restapi.go | 3 +- 3 files changed, 92 insertions(+), 80 deletions(-) diff --git a/examples/slash_commands/main.go b/examples/slash_commands/main.go index 186ea0515..3adcea851 100644 --- a/examples/slash_commands/main.go +++ b/examples/slash_commands/main.go @@ -13,8 +13,8 @@ import ( // Bot parameters var ( - GuildID = flag.String("guild", "", "Test guild ID. If not passed - bot registers commands globally") - BotToken = flag.String("token", "", "Bot access token") + GuildID = flag.String("guild", "", "Test guild ID. If not passed - bot registers commands globally") + BotToken = flag.String("token", "", "Bot access token") RemoveCommands = flag.Bool("rmcmd", true, "Remove all commands after shutdowning or not") ) @@ -31,64 +31,64 @@ func init() { } var ( - commands = []*discordgo.ApplicationCommand { + commands = []*discordgo.ApplicationCommand{ { Name: "basic-command", // All commands and options must have an description // Commands/options without description will fail the registration // of the command. - Description: "Basic command", + Description: "Basic command", }, { - Name: "options", + Name: "options", Description: "Command for demonstrating options", Options: []*discordgo.ApplicationCommandOption{ { - Type: discordgo.ApplicationCommandOptionString, - Name: "string-option", + Type: discordgo.ApplicationCommandOptionString, + Name: "string-option", Description: "String option", - Required: true, + Required: true, }, { - Type: discordgo.ApplicationCommandOptionInteger, - Name: "integer-option", + Type: discordgo.ApplicationCommandOptionInteger, + Name: "integer-option", Description: "Integer option", - Required: true, + Required: true, }, { - Type: discordgo.ApplicationCommandOptionBoolean, - Name: "bool-option", + Type: discordgo.ApplicationCommandOptionBoolean, + Name: "bool-option", Description: "Boolean option", - Required: true, + Required: true, }, // Required options must be listed first, because // like everyone knows - optional parameters is on the back. // The same concept applies to Discord's Slash-commands API - + { - Type: discordgo.ApplicationCommandOptionChannel, - Name: "channel-option", + Type: discordgo.ApplicationCommandOptionChannel, + Name: "channel-option", Description: "Channel option", - Required: false, + Required: false, }, { - Type: discordgo.ApplicationCommandOptionUser, - Name: "user-option", + Type: discordgo.ApplicationCommandOptionUser, + Name: "user-option", Description: "User option", - Required: false, + Required: false, }, { - Type: discordgo.ApplicationCommandOptionRole, - Name: "role-option", + Type: discordgo.ApplicationCommandOptionRole, + Name: "role-option", Description: "Role option", - Required: false, + Required: false, }, }, }, { - Name: "subcommands", + Name: "subcommands", Description: "Subcommands and command groups example", Options: []*discordgo.ApplicationCommandOption{ // When command have subcommands/subcommand groups @@ -98,16 +98,16 @@ var ( // will fail the registration of the command { - Name: "scmd-grp", + Name: "scmd-grp", Description: "Subcommands group", Options: []*discordgo.ApplicationCommandOption{ // Also, subcommand groups isn't capable of // containg options, by the name of them, you can see // they can contain only subcommands { - Name: "nst-subcmd", + Name: "nst-subcmd", Description: "Nested subcommand", - Type: discordgo.ApplicationCommandOptionSubCommand, + Type: discordgo.ApplicationCommandOptionSubCommand, }, }, Type: discordgo.ApplicationCommandOptionSubCommandGroup, @@ -118,35 +118,35 @@ var ( // Read the intro of slash-commands docs on Discord dev portal // to get more information { - Name: "subcmd", + Name: "subcmd", Description: "Top-level subcommand", - Type: discordgo.ApplicationCommandOptionSubCommand, + Type: discordgo.ApplicationCommandOptionSubCommand, }, }, }, { - Name: "responses", + Name: "responses", Description: "Interaction responses testing initiative", Options: []*discordgo.ApplicationCommandOption{ { - Name: "resp-type", + Name: "resp-type", Description: "Response type", - Type: discordgo.ApplicationCommandOptionInteger, + Type: discordgo.ApplicationCommandOptionInteger, Choices: []*discordgo.ApplicationCommandOptionChoice{ { - Name: "Acknowledge", + Name: "Acknowledge", Value: 2, }, { - Name: "Channel message", + Name: "Channel message", Value: 3, }, { - Name: "Channel message with source", + Name: "Channel message with source", Value: 4, }, { - Name: "Acknowledge with source", + Name: "Acknowledge with source", Value: 5, }, }, @@ -154,11 +154,11 @@ var ( }, }, { - Name: "followups", + Name: "followups", Description: "Followup messages", }, } - commandHandlers = map[string]func(s *discordgo.Session, i *discordgo.InteractionCreate) { + commandHandlers = map[string]func(s *discordgo.Session, i *discordgo.InteractionCreate){ "basic-command": func(s *discordgo.Session, i *discordgo.InteractionCreate) { s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, @@ -208,16 +208,16 @@ var ( }, "subcommands": func(s *discordgo.Session, i *discordgo.InteractionCreate) { content := "" - + // 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.Data.Options[0].Name { case "subcmd": - content = + content = "The top-level subcommand is executed. Now try to execute nested one." default: if i.Data.Options[0].Name != "scmd-grp" { - return + return } switch i.Data.Options[0].Options[0].Name { case "nst-subcmd": @@ -243,21 +243,21 @@ var ( // use interaction token and ID for responding to the user's request content := "" - // As you can see, response type names saying by themselvs + // As you can see, response type names saying by themselvs // how they're used, but for those who want to get // more information - read the official documentation switch i.Data.Options[0].IntValue() { case int64(discordgo.InteractionResponseChannelMessage): content = "Well, you just responded to an interaction, and sent a message.\n" + - "That's all what I wanted to say, yeah." - content += + "That's all what I wanted to say, yeah." + content += "\nAlso... you can edit your response, wait 5 seconds and this message will be changed" - case int64(discordgo.InteractionResponseChannelMessageWithSource): + case int64(discordgo.InteractionResponseChannelMessageWithSource): content = "You just responded to an interaction, sent a message and showed the original one. " + - "Congratulations!" - content += + "Congratulations!" + content += "\nAlso... you can edit your response, wait 5 seconds and this message will be changed" default: err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ @@ -283,7 +283,7 @@ var ( }) return } - time.AfterFunc(time.Second * 5, func() { + time.AfterFunc(time.Second*5, func() { err = s.InteractionResponseEdit("", i.Interaction, &discordgo.WebhookEdit{ Content: content + "\n\nWell, now you know how to create and edit responses. " + "But you still don't know how to delete them... so... wait 10 seconds and this " + @@ -310,7 +310,7 @@ var ( // Note: this isn't documented, but you can use that if you want to. // This flag just allows to create messages visible only for the caller (user who triggered the command) // of the command - Flags: 1 << 6, + Flags: 1 << 6, Content: "Surprise!", }, }) @@ -328,22 +328,22 @@ var ( s.FollowupMessageEdit("", i.Interaction, msg.ID, &discordgo.WebhookEdit{ Content: "Now original message is gone and after 10 seconds this message will ~~self-destruct~~ be deleted.", }) - + time.Sleep(time.Second * 10) - s.FollowupMessageDelete ("", i.Interaction, msg.ID) + s.FollowupMessageDelete("", i.Interaction, msg.ID) s.FollowupMessageCreate("", i.Interaction, true, &discordgo.WebhookParams{ - Content: "For those, who didn't skip anything and followed tutorial along fairly, " + - "take a unicorn :unicorn: as reward!\n" + - "Also, as bonus..., look at the original interaction response :D", + Content: "For those, who didn't skip anything and followed tutorial along fairly, " + + "take a unicorn :unicorn: as reward!\n" + + "Also, as bonus..., look at the original interaction response :D", }) }, } ) func init() { - s.AddHandler(func (s *discordgo.Session, i *discordgo.InteractionCreate) { + s.AddHandler(func(s *discordgo.Session, i *discordgo.InteractionCreate) { if h, ok := commandHandlers[i.Data.Name]; ok { h(s, i) } @@ -351,7 +351,7 @@ func init() { } func main() { - s.AddHandler(func (s *discordgo.Session, r *discordgo.Ready) { + s.AddHandler(func(s *discordgo.Session, r *discordgo.Ready) { log.Println("Bot is up!") }) err := s.Open() @@ -372,4 +372,4 @@ func main() { signal.Notify(stop, os.Interrupt) <-stop log.Println("Gracefully shutdowning") -} \ No newline at end of file +} diff --git a/interactions.go b/interactions.go index 265cd87d4..866dda9a8 100644 --- a/interactions.go +++ b/interactions.go @@ -40,13 +40,13 @@ const ( // ApplicationCommandOption is representing an option/subcommand/subcommands group. type ApplicationCommandOption struct { - Type ApplicationCommandOptionType `json:"type"` - Name string `json:"name"` - Description string `json:"description,omitempty"` + Type ApplicationCommandOptionType `json:"type"` + Name string `json:"name"` + Description string `json:"description,omitempty"` // Default bool `json:"default"` - Required bool `json:"required"` - Choices []*ApplicationCommandOptionChoice `json:"choices"` - Options []*ApplicationCommandOption `json:"options"` + Required bool `json:"required"` + Choices []*ApplicationCommandOptionChoice `json:"choices"` + Options []*ApplicationCommandOption `json:"options"` } // ApplicationCommandOptionChoice is representing slash-command's option choice. @@ -78,8 +78,8 @@ type Interaction struct { // ApplicationCommandInteractionData is representing interaction data for application command. type ApplicationCommandInteractionData struct { - ID string `json:"id"` - Name string `json:"name"` + ID string `json:"id"` + Name string `json:"name"` Options []*ApplicationCommandInteractionDataOption `json:"options"` } @@ -91,6 +91,7 @@ type ApplicationCommandInteractionDataOption struct { Options []*ApplicationCommandInteractionDataOption `json:"options,omitempty"` } +// IntValue is utility function for casting option value to integer func (o ApplicationCommandInteractionDataOption) IntValue() (i int64) { if v, ok := o.Value.(float64); ok { i = int64(v) @@ -98,6 +99,7 @@ func (o ApplicationCommandInteractionDataOption) IntValue() (i int64) { return } +// UintValue is utility function for casting option value to unsigned integer func (o ApplicationCommandInteractionDataOption) UintValue() (i uint64) { if v, ok := o.Value.(float64); ok { i = uint64(v) @@ -105,6 +107,7 @@ func (o ApplicationCommandInteractionDataOption) UintValue() (i uint64) { return } +// FloatValue is utility function for casting option value to float func (o ApplicationCommandInteractionDataOption) FloatValue() (n float64) { if v, ok := o.Value.(float64); ok { n = float64(v) @@ -112,12 +115,15 @@ func (o ApplicationCommandInteractionDataOption) FloatValue() (n float64) { return } +// StringValue is utility function for casting option value to string func (o ApplicationCommandInteractionDataOption) StringValue() (s string) { if v, ok := o.Value.(string); ok { s = v } return } + +// BoolValue is utility function for casting option value to bool func (o ApplicationCommandInteractionDataOption) BoolValue() (b bool) { if v, ok := o.Value.(bool); ok { b = v @@ -125,12 +131,16 @@ func (o ApplicationCommandInteractionDataOption) BoolValue() (b bool) { return } +// ChannelValue is utility function for casting option value to channel object. +// s : Session object, if not nil, function additionaly fetches all channel's data func (o ApplicationCommandInteractionDataOption) ChannelValue(s *Session) (ch *Channel) { chanID := o.StringValue() - if chanID == "" { return } + if chanID == "" { + return + } if s == nil { - return &Channel {ID: chanID} + return &Channel{ID: chanID} } ch, err := s.State.Channel(chanID) @@ -141,12 +151,16 @@ func (o ApplicationCommandInteractionDataOption) ChannelValue(s *Session) (ch *C return } +// RoleValue is utility function for casting option value to role object. +// s : Session object, if not nil, function additionaly fetches all role's data func (o ApplicationCommandInteractionDataOption) RoleValue(s *Session, gID string) (r *Role) { roleID := o.StringValue() - if roleID == "" { return } + if roleID == "" { + return + } if s == nil || gID == "" { - return &Role { ID: roleID } + return &Role{ID: roleID} } var err error @@ -167,12 +181,16 @@ func (o ApplicationCommandInteractionDataOption) RoleValue(s *Session, gID strin return } +// UserValue is utility function for casting option value to user object. +// s : Session object, if not nil, function additionaly fetches all user's data func (o ApplicationCommandInteractionDataOption) UserValue(s *Session) (u *User) { userID := o.StringValue() - if userID == "" { return } + if userID == "" { + return + } if s == nil { - return &User { ID: userID } + return &User{ID: userID} } u, _ = s.User(userID) @@ -180,10 +198,6 @@ func (o ApplicationCommandInteractionDataOption) UserValue(s *Session) (u *User) return } -// func (o ApplicationCommandInteractionDataOption) String() string { -// return fmt.Sprintf("%v", o.Value) -// } - // InteractionResponseType is type of interaction response. type InteractionResponseType uint8 @@ -217,7 +231,6 @@ type InteractionApplicationCommandResponseData struct { Flags uint64 `json:"flags,omitempty"` // NOTE: Undocumented feature, be careful with it. } - // VerifyInteraction implements message verification of the discord interactions api // signing algorithm, as documented here: // https://discord.com/developers/docs/interactions/slash-commands#security-and-authorization diff --git a/restapi.go b/restapi.go index 6f1c117aa..3b956a167 100644 --- a/restapi.go +++ b/restapi.go @@ -2426,7 +2426,6 @@ func (s *Session) ApplicationCommandDelete(appID, guildID, cmdID string) (err er return } - // ApplicationCommand retrieves an application command by given id. // appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. // cmdID : Application command ID. @@ -2444,7 +2443,7 @@ func (s *Session) ApplicationCommand(appID, guildID, cmdID string) (cmd *Applica if err != nil { return } - + err = unmarshal(body, &cmd) return } From 00e6233536c959e688450c9a1e7e361a312d56bf Mon Sep 17 00:00:00 2001 From: nitroflap Date: Wed, 17 Feb 2021 14:28:29 +0300 Subject: [PATCH 15/28] More fixes --- endpoints.go | 8 ++--- interactions.go | 80 +++++++++++++++++++++++++++---------------------- restapi.go | 59 +++++++++++++++++++++++++++++------- 3 files changed, 97 insertions(+), 50 deletions(-) diff --git a/endpoints.go b/endpoints.go index 8cc8d966a..c8c9d4c5e 100644 --- a/endpoints.go +++ b/endpoints.go @@ -122,7 +122,7 @@ var ( EndpointWebhook = func(wID string) string { return EndpointWebhooks + wID } EndpointWebhookToken = func(wID, token string) string { return EndpointWebhooks + wID + "/" + token } EndpointWebhookMessage = func(wID, token, messageID string) string { - return EndpointWebhookToken(wID, token) + "/" + "messages/" + messageID + return EndpointWebhookToken(wID, token) + "/messages/" + messageID } EndpointMessageReactionsAll = func(cID, mID string) string { @@ -136,14 +136,14 @@ var ( } EndpointApplicationGlobalCommands = func(aID string) string { - return EndpointApplication(aID) + "/" + "commands" + return EndpointApplication(aID) + "/commands" } EndpointApplicationGlobalCommand = func(aID, cID string) string { return EndpointApplicationGlobalCommands(aID) + "/" + cID } EndpointApplicationGuildCommands = func(aID, gID string) string { - return EndpointApplication(aID) + "/" + "guilds" + "/" + gID + "/" + "commands" + return EndpointApplication(aID) + "guilds/" + gID + "/commands" } EndpointApplicationGuildCommand = func(aID, gID, cID string) string { return EndpointApplicationGuildCommands(aID, gID) + "/" + cID @@ -152,7 +152,7 @@ var ( return EndpointAPI + "interactions/" + aID + "/" + iToken } EndpointInteractionResponse = func(iID, iToken string) string { - return EndpointInteraction(iID, iToken) + "/" + "callback" + return EndpointInteraction(iID, iToken) + "/callback" } EndpointInteractionResponseActions = func(aID, iToken string) string { return EndpointWebhookMessage(aID, iToken, "@original") diff --git a/interactions.go b/interactions.go index 866dda9a8..82b6fbc17 100644 --- a/interactions.go +++ b/interactions.go @@ -8,7 +8,6 @@ import ( "io/ioutil" "net/http" "time" - // "fmt" ) // InteractionDeadline is a deadline for responding to an interaction, if you haven't responded in the time, you won't be able to respond later. @@ -43,6 +42,8 @@ type ApplicationCommandOption struct { Type ApplicationCommandOptionType `json:"type"` Name string `json:"name"` Description string `json:"description,omitempty"` + // NOTE: This feature was on the API but at some point developers decided to remove it. + // So I commented it, until it will be officially on the docs. // Default bool `json:"default"` Required bool `json:"required"` Choices []*ApplicationCommandOptionChoice `json:"choices"` @@ -92,51 +93,56 @@ type ApplicationCommandInteractionDataOption struct { } // IntValue is utility function for casting option value to integer -func (o ApplicationCommandInteractionDataOption) IntValue() (i int64) { +func (o ApplicationCommandInteractionDataOption) IntValue() int64 { if v, ok := o.Value.(float64); ok { - i = int64(v) + return int64(v) } - return + + return 0 } // UintValue is utility function for casting option value to unsigned integer -func (o ApplicationCommandInteractionDataOption) UintValue() (i uint64) { +func (o ApplicationCommandInteractionDataOption) UintValue() uint64 { if v, ok := o.Value.(float64); ok { - i = uint64(v) + return uint64(v) } - return + + return 0 } // FloatValue is utility function for casting option value to float -func (o ApplicationCommandInteractionDataOption) FloatValue() (n float64) { +func (o ApplicationCommandInteractionDataOption) FloatValue() float64 { if v, ok := o.Value.(float64); ok { - n = float64(v) + return float64(v) } - return + + return 0.0 } // StringValue is utility function for casting option value to string -func (o ApplicationCommandInteractionDataOption) StringValue() (s string) { +func (o ApplicationCommandInteractionDataOption) StringValue() string { if v, ok := o.Value.(string); ok { - s = v + return v } - return + + return "" } // BoolValue is utility function for casting option value to bool -func (o ApplicationCommandInteractionDataOption) BoolValue() (b bool) { +func (o ApplicationCommandInteractionDataOption) BoolValue() bool { if v, ok := o.Value.(bool); ok { - b = v + return v } - return + + return false } // ChannelValue is utility function for casting option value to channel object. // s : Session object, if not nil, function additionaly fetches all channel's data -func (o ApplicationCommandInteractionDataOption) ChannelValue(s *Session) (ch *Channel) { +func (o ApplicationCommandInteractionDataOption) ChannelValue(s *Session) *Channel { chanID := o.StringValue() if chanID == "" { - return + return nil } if s == nil { @@ -146,56 +152,60 @@ func (o ApplicationCommandInteractionDataOption) ChannelValue(s *Session) (ch *C ch, err := s.State.Channel(chanID) if err != nil { ch, err = s.Channel(chanID) + if err != nil { + return &Channel{ID: chanID} + } } - return + return ch } // RoleValue is utility function for casting option value to role object. // s : Session object, if not nil, function additionaly fetches all role's data -func (o ApplicationCommandInteractionDataOption) RoleValue(s *Session, gID string) (r *Role) { +func (o ApplicationCommandInteractionDataOption) RoleValue(s *Session, gID string) *Role { roleID := o.StringValue() if roleID == "" { - return + return nil } if s == nil || gID == "" { return &Role{ID: roleID} } - var err error - r, err = s.State.Role(roleID, gID) + r, err := s.State.Role(roleID, gID) if err != nil { roles, err := s.GuildRoles(gID) - if err != nil { - return - } - for _, r = range roles { - if r.ID == roleID { - return + if err == nil { + for _, r = range roles { + if r.ID == roleID { + return r + } } } - r = nil + return &Role{ID: roleID} } - return + return r } // UserValue is utility function for casting option value to user object. // s : Session object, if not nil, function additionaly fetches all user's data -func (o ApplicationCommandInteractionDataOption) UserValue(s *Session) (u *User) { +func (o ApplicationCommandInteractionDataOption) UserValue(s *Session) *User { userID := o.StringValue() if userID == "" { - return + return nil } if s == nil { return &User{ID: userID} } - u, _ = s.User(userID) + u, err := s.User(userID) + if err != nil { + return &User{ID: userID} + } - return + return u } // InteractionResponseType is type of interaction response. diff --git a/restapi.go b/restapi.go index 3b956a167..3f795786e 100644 --- a/restapi.go +++ b/restapi.go @@ -2368,104 +2368,132 @@ func (s *Session) RelationshipsMutualGet(userID string) (mf []*User, err error) // ApplicationCommandCreate creates an global application command and returns it. // appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. -// guildID : Guild ID to create guild-specific application command. +// guildID : Guild ID to create guild-specific application command. If empty - creates global appication command. // cmd : New application command data. func (s *Session) ApplicationCommandCreate(appID string, guildID string, cmd *ApplicationCommand) (rcmd *ApplicationCommand, err error) { if appID == "" { appID = s.State.User.ID } - endpoint := EndpointApplicationGlobalCommands(s.State.User.ID) + + endpoint := EndpointApplicationGlobalCommands(appID) if guildID != "" { - endpoint = EndpointApplicationGuildCommands(s.State.User.ID, guildID) + endpoint = EndpointApplicationGuildCommands(appID, guildID) } - body, err := s.RequestWithBucketID("POST", endpoint, *cmd, endpoint) + + var body []byte + body, err = s.RequestWithBucketID("POST", endpoint, *cmd, endpoint) if err != nil { return } err = unmarshal(body, &rcmd) + return } // ApplicationCommandEdit edits application command and returns old command data. // appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. // cmdID : Application command ID to edit. -// guildID : Guild ID to edit guild-specific application command. +// guildID : Guild ID to edit guild-specific application command. If empty - edits global application command. // cmd : Updated application command data. func (s *Session) ApplicationCommandEdit(appID, cmdID, guildID string, cmd *ApplicationCommand) (oldcmd *ApplicationCommand, err error) { + if cmdID == "" { + return nil, errors.New("invalid command id") + } + if appID == "" { appID = s.State.User.ID } + endpoint := EndpointApplicationGlobalCommand(appID, cmdID) if guildID != "" { endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) } - body, err := s.RequestWithBucketID("PATCH", endpoint, *cmd, endpoint) + var body []byte + body, err = s.RequestWithBucketID("PATCH", endpoint, *cmd, endpoint) if err != nil { return } err = unmarshal(body, &cmd) + return } // ApplicationCommandDelete deletes application command by id. // appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. // cmdID : Application command ID to delete. -// guildID : Guild ID to delete guild-specific application command. +// guildID : Guild ID to delete guild-specific application command. If empty - deletes global application command. func (s *Session) ApplicationCommandDelete(appID, guildID, cmdID string) (err error) { + if cmdID == "" { + return errors.New("invalid command id") + } + if appID == "" { appID = s.State.User.ID } + endpoint := EndpointApplicationGlobalCommand(appID, cmdID) if guildID != "" { endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) } + _, err = s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) + return } // ApplicationCommand retrieves an application command by given id. // appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. // cmdID : Application command ID. -// guildID : Guild ID to retrieve guild-specific application command. +// guildID : Guild ID to retrieve guild-specific application command. If empty - retrieves global application command. func (s *Session) ApplicationCommand(appID, guildID, cmdID string) (cmd *ApplicationCommand, err error) { + if cmdID == "" { + return nil, errors.New("invalid command id") + } + if appID == "" { appID = s.State.User.ID } + endpoint := EndpointApplicationGlobalCommand(appID, cmdID) if guildID != "" { endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) } - body, err := s.RequestWithBucketID("GET", endpoint, nil, endpoint) + var body []byte + body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) if err != nil { return } err = unmarshal(body, &cmd) + return } // ApplicationCommands retrieves all commands in application. // appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. -// guildID : Guild ID to retrieve all guild-specific application commands. +// guildID : Guild ID to retrieve all guild-specific application commands. If empty - retrieves global application commands. func (s *Session) ApplicationCommands(appID, guildID string) (cmd []*ApplicationCommand, err error) { if appID == "" { appID = s.State.User.ID } + endpoint := EndpointApplicationGlobalCommands(appID) if guildID != "" { endpoint = EndpointApplicationGuildCommands(appID, guildID) } - body, err := s.RequestWithBucketID("GET", endpoint, nil, endpoint) + var body []byte + body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) if err != nil { return } err = unmarshal(body, &cmd) + return } @@ -2475,7 +2503,9 @@ func (s *Session) ApplicationCommands(appID, guildID string) (cmd []*Application // resp : Response message data. func (s *Session) InteractionRespond(interaction *Interaction, resp *InteractionResponse) (err error) { endpoint := EndpointInteractionResponse(interaction.ID, interaction.Token) + _, err = s.RequestWithBucketID("POST", endpoint, *resp, endpoint) + return } @@ -2487,6 +2517,7 @@ func (s *Session) InteractionResponseEdit(appID string, interaction *Interaction if appID == "" { appID = s.State.User.ID } + return s.WebhookMessageEdit(appID, interaction.Token, "@original", newresp) } @@ -2497,8 +2528,11 @@ func (s *Session) InteractionResponseDelete(appID string, interaction *Interacti if appID == "" { appID = s.State.User.ID } + endpoint := EndpointInteractionResponseActions(appID, interaction.Token) + _, err = s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) + return } @@ -2511,6 +2545,7 @@ func (s *Session) FollowupMessageCreate(appID string, interaction *Interaction, if appID == "" { appID = s.State.User.ID } + return s.WebhookExecute(appID, interaction.Token, wait, data) } @@ -2523,6 +2558,7 @@ func (s *Session) FollowupMessageEdit(appID string, interaction *Interaction, me if appID == "" { appID = s.State.User.ID } + return s.WebhookMessageEdit(appID, interaction.Token, messageID, data) } @@ -2534,5 +2570,6 @@ func (s *Session) FollowupMessageDelete(appID string, interaction *Interaction, if appID == "" { appID = s.State.User.ID } + return s.WebhookMessageDelete(appID, interaction.Token, messageID) } From a1b4fedbacd172dc6255b6f1f98fbc2793f09ccc Mon Sep 17 00:00:00 2001 From: nitroflap Date: Wed, 17 Feb 2021 14:44:35 +0300 Subject: [PATCH 16/28] Gofmt fixes --- interactions.go | 2 +- restapi.go | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/interactions.go b/interactions.go index 82b6fbc17..4e5621202 100644 --- a/interactions.go +++ b/interactions.go @@ -95,7 +95,7 @@ type ApplicationCommandInteractionDataOption struct { // IntValue is utility function for casting option value to integer func (o ApplicationCommandInteractionDataOption) IntValue() int64 { if v, ok := o.Value.(float64); ok { - return int64(v) + return int64(v) } return 0 diff --git a/restapi.go b/restapi.go index 3f795786e..00bd3cf73 100644 --- a/restapi.go +++ b/restapi.go @@ -2417,7 +2417,7 @@ func (s *Session) ApplicationCommandEdit(appID, cmdID, guildID string, cmd *Appl } err = unmarshal(body, &cmd) - + return } @@ -2438,9 +2438,9 @@ func (s *Session) ApplicationCommandDelete(appID, guildID, cmdID string) (err er if guildID != "" { endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) } - + _, err = s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) - + return } @@ -2469,7 +2469,7 @@ func (s *Session) ApplicationCommand(appID, guildID, cmdID string) (cmd *Applica } err = unmarshal(body, &cmd) - + return } @@ -2493,7 +2493,7 @@ func (s *Session) ApplicationCommands(appID, guildID string) (cmd []*Application } err = unmarshal(body, &cmd) - + return } @@ -2505,7 +2505,7 @@ func (s *Session) InteractionRespond(interaction *Interaction, resp *Interaction endpoint := EndpointInteractionResponse(interaction.ID, interaction.Token) _, err = s.RequestWithBucketID("POST", endpoint, *resp, endpoint) - + return } @@ -2517,7 +2517,7 @@ func (s *Session) InteractionResponseEdit(appID string, interaction *Interaction if appID == "" { appID = s.State.User.ID } - + return s.WebhookMessageEdit(appID, interaction.Token, "@original", newresp) } @@ -2530,9 +2530,9 @@ func (s *Session) InteractionResponseDelete(appID string, interaction *Interacti } endpoint := EndpointInteractionResponseActions(appID, interaction.Token) - + _, err = s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) - + return } From 8718e2d378980739f44b20592e925d45f3721e7a Mon Sep 17 00:00:00 2001 From: nitroflap Date: Wed, 17 Feb 2021 21:45:39 +0300 Subject: [PATCH 17/28] More fixes! --- restapi.go | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/restapi.go b/restapi.go index 00bd3cf73..d89832b35 100644 --- a/restapi.go +++ b/restapi.go @@ -2370,7 +2370,7 @@ func (s *Session) RelationshipsMutualGet(userID string) (mf []*User, err error) // appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. // guildID : Guild ID to create guild-specific application command. If empty - creates global appication command. // cmd : New application command data. -func (s *Session) ApplicationCommandCreate(appID string, guildID string, cmd *ApplicationCommand) (rcmd *ApplicationCommand, err error) { +func (s *Session) ApplicationCommandCreate(appID string, guildID string, cmd *ApplicationCommand) (ccmd *ApplicationCommand, err error) { if appID == "" { appID = s.State.User.ID } @@ -2386,7 +2386,7 @@ func (s *Session) ApplicationCommandCreate(appID string, guildID string, cmd *Ap return } - err = unmarshal(body, &rcmd) + err = unmarshal(body, &ccmd) return } @@ -2416,7 +2416,7 @@ func (s *Session) ApplicationCommandEdit(appID, cmdID, guildID string, cmd *Appl return } - err = unmarshal(body, &cmd) + err = unmarshal(body, &oldcmd) return } @@ -2425,7 +2425,7 @@ func (s *Session) ApplicationCommandEdit(appID, cmdID, guildID string, cmd *Appl // appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. // cmdID : Application command ID to delete. // guildID : Guild ID to delete guild-specific application command. If empty - deletes global application command. -func (s *Session) ApplicationCommandDelete(appID, guildID, cmdID string) (err error) { +func (s *Session) ApplicationCommandDelete(appID, guildID, cmdID string) error { if cmdID == "" { return errors.New("invalid command id") } @@ -2439,9 +2439,9 @@ func (s *Session) ApplicationCommandDelete(appID, guildID, cmdID string) (err er endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) } - _, err = s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) + _, err := s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) - return + return err } // ApplicationCommand retrieves an application command by given id. @@ -2501,19 +2501,19 @@ func (s *Session) ApplicationCommands(appID, guildID string) (cmd []*Application // appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. // interaction : Interaction instance. // resp : Response message data. -func (s *Session) InteractionRespond(interaction *Interaction, resp *InteractionResponse) (err error) { +func (s *Session) InteractionRespond(interaction *Interaction, resp *InteractionResponse) error { endpoint := EndpointInteractionResponse(interaction.ID, interaction.Token) - _, err = s.RequestWithBucketID("POST", endpoint, *resp, endpoint) + _, err := s.RequestWithBucketID("POST", endpoint, *resp, endpoint) - return + return err } // InteractionResponseEdit edits the response to an interaction. // appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. // interaction : Interaction instance. // newresp : Updated response message data. -func (s *Session) InteractionResponseEdit(appID string, interaction *Interaction, newresp *WebhookEdit) (err error) { +func (s *Session) InteractionResponseEdit(appID string, interaction *Interaction, newresp *WebhookEdit) error { if appID == "" { appID = s.State.User.ID } @@ -2524,16 +2524,16 @@ func (s *Session) InteractionResponseEdit(appID string, interaction *Interaction // InteractionResponseDelete deletes the response to an interaction. // appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. // interaction : Interaction instance. -func (s *Session) InteractionResponseDelete(appID string, interaction *Interaction) (err error) { +func (s *Session) InteractionResponseDelete(appID string, interaction *Interaction) error { if appID == "" { appID = s.State.User.ID } endpoint := EndpointInteractionResponseActions(appID, interaction.Token) - _, err = s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) + _, err := s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) - return + return err } // FollowupMessageCreate creates the followup message for an interaction. @@ -2541,7 +2541,7 @@ func (s *Session) InteractionResponseDelete(appID string, interaction *Interacti // interaction : Interaction instance. // wait : Waits for server confirmation of message send and ensures that the return struct is populated (it is nil otherwise) // data : Data of the message to send. -func (s *Session) FollowupMessageCreate(appID string, interaction *Interaction, wait bool, data *WebhookParams) (st *Message, err error) { +func (s *Session) FollowupMessageCreate(appID string, interaction *Interaction, wait bool, data *WebhookParams) (*Message, error) { if appID == "" { appID = s.State.User.ID } @@ -2554,7 +2554,7 @@ func (s *Session) FollowupMessageCreate(appID string, interaction *Interaction, // interaction : Interaction instance. // messageID : The followup message ID. // data : Data to update the message -func (s *Session) FollowupMessageEdit(appID string, interaction *Interaction, messageID string, data *WebhookEdit) (err error) { +func (s *Session) FollowupMessageEdit(appID string, interaction *Interaction, messageID string, data *WebhookEdit) error { if appID == "" { appID = s.State.User.ID } @@ -2566,7 +2566,7 @@ func (s *Session) FollowupMessageEdit(appID string, interaction *Interaction, me // appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. // interaction : Interaction instance. // messageID : The followup message ID. -func (s *Session) FollowupMessageDelete(appID string, interaction *Interaction, messageID string) (err error) { +func (s *Session) FollowupMessageDelete(appID string, interaction *Interaction, messageID string) error { if appID == "" { appID = s.State.User.ID } From 4b3f1414b0db923c3fd3b63f670d2734465f2a0d Mon Sep 17 00:00:00 2001 From: nitroflap Date: Mon, 1 Mar 2021 01:52:26 +0300 Subject: [PATCH 18/28] Doc and endpoint fixes --- endpoints.go | 2 +- interactions.go | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/endpoints.go b/endpoints.go index c8c9d4c5e..ac8d4488a 100644 --- a/endpoints.go +++ b/endpoints.go @@ -143,7 +143,7 @@ var ( } EndpointApplicationGuildCommands = func(aID, gID string) string { - return EndpointApplication(aID) + "guilds/" + gID + "/commands" + return EndpointApplication(aID) + "/guilds/" + gID + "/commands" } EndpointApplicationGuildCommand = func(aID, gID, cID string) string { return EndpointApplicationGuildCommands(aID, gID) + "/" + cID diff --git a/interactions.go b/interactions.go index 4e5621202..b158de6a4 100644 --- a/interactions.go +++ b/interactions.go @@ -42,7 +42,7 @@ type ApplicationCommandOption struct { Type ApplicationCommandOptionType `json:"type"` Name string `json:"name"` Description string `json:"description,omitempty"` - // NOTE: This feature was on the API but at some point developers decided to remove it. + // NOTE: This feature was on the API, but at some point developers decided to remove it. // So I commented it, until it will be officially on the docs. // Default bool `json:"default"` Required bool `json:"required"` @@ -87,7 +87,7 @@ type ApplicationCommandInteractionData struct { // ApplicationCommandInteractionDataOption is representing an option of application's command. type ApplicationCommandInteractionDataOption struct { Name string `json:"name"` - // Contains the value specified by InteractionType + // NOTE: Contains the value specified by InteractionType. Value interface{} `json:"value,omitempty"` Options []*ApplicationCommandInteractionDataOption `json:"options,omitempty"` } @@ -238,7 +238,8 @@ type InteractionApplicationCommandResponseData struct { Embeds []*MessageEmbed `json:"embeds,omitempty"` AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` - Flags uint64 `json:"flags,omitempty"` // NOTE: Undocumented feature, be careful with it. + // NOTE: Undocumented feature, be careful with it. + Flags uint64 `json:"flags,omitempty"` } // VerifyInteraction implements message verification of the discord interactions api From 3bc10114208ae1dfaa8bf46b478ba949ff5a1d6d Mon Sep 17 00:00:00 2001 From: nitroflap Date: Mon, 1 Mar 2021 01:58:22 +0300 Subject: [PATCH 19/28] Gofmt fix --- interactions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactions.go b/interactions.go index b158de6a4..0739d1102 100644 --- a/interactions.go +++ b/interactions.go @@ -239,7 +239,7 @@ type InteractionApplicationCommandResponseData struct { AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` // NOTE: Undocumented feature, be careful with it. - Flags uint64 `json:"flags,omitempty"` + Flags uint64 `json:"flags,omitempty"` } // VerifyInteraction implements message verification of the discord interactions api From 1f39a0ca4da1b48559ea23cb358a834c1c761656 Mon Sep 17 00:00:00 2001 From: Carson Hoffman Date: Sun, 28 Feb 2021 20:57:14 -0500 Subject: [PATCH 20/28] Remove dependence on open gateway connection --- restapi.go | 62 ++++++++++-------------------------------------------- 1 file changed, 11 insertions(+), 51 deletions(-) diff --git a/restapi.go b/restapi.go index d89832b35..974da1042 100644 --- a/restapi.go +++ b/restapi.go @@ -2367,14 +2367,10 @@ func (s *Session) RelationshipsMutualGet(userID string) (mf []*User, err error) // ------------------------------------------------------------------------------------------------ // ApplicationCommandCreate creates an global application command and returns it. -// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// appID : The application ID. // guildID : Guild ID to create guild-specific application command. If empty - creates global appication command. // cmd : New application command data. func (s *Session) ApplicationCommandCreate(appID string, guildID string, cmd *ApplicationCommand) (ccmd *ApplicationCommand, err error) { - if appID == "" { - appID = s.State.User.ID - } - endpoint := EndpointApplicationGlobalCommands(appID) if guildID != "" { endpoint = EndpointApplicationGuildCommands(appID, guildID) @@ -2392,7 +2388,7 @@ func (s *Session) ApplicationCommandCreate(appID string, guildID string, cmd *Ap } // ApplicationCommandEdit edits application command and returns old command data. -// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// appID : The application ID. // cmdID : Application command ID to edit. // guildID : Guild ID to edit guild-specific application command. If empty - edits global application command. // cmd : Updated application command data. @@ -2401,10 +2397,6 @@ func (s *Session) ApplicationCommandEdit(appID, cmdID, guildID string, cmd *Appl return nil, errors.New("invalid command id") } - if appID == "" { - appID = s.State.User.ID - } - endpoint := EndpointApplicationGlobalCommand(appID, cmdID) if guildID != "" { endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) @@ -2422,7 +2414,7 @@ func (s *Session) ApplicationCommandEdit(appID, cmdID, guildID string, cmd *Appl } // ApplicationCommandDelete deletes application command by id. -// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// appID : The application ID. // cmdID : Application command ID to delete. // guildID : Guild ID to delete guild-specific application command. If empty - deletes global application command. func (s *Session) ApplicationCommandDelete(appID, guildID, cmdID string) error { @@ -2430,10 +2422,6 @@ func (s *Session) ApplicationCommandDelete(appID, guildID, cmdID string) error { return errors.New("invalid command id") } - if appID == "" { - appID = s.State.User.ID - } - endpoint := EndpointApplicationGlobalCommand(appID, cmdID) if guildID != "" { endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) @@ -2445,7 +2433,7 @@ func (s *Session) ApplicationCommandDelete(appID, guildID, cmdID string) error { } // ApplicationCommand retrieves an application command by given id. -// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// appID : The application ID. // cmdID : Application command ID. // guildID : Guild ID to retrieve guild-specific application command. If empty - retrieves global application command. func (s *Session) ApplicationCommand(appID, guildID, cmdID string) (cmd *ApplicationCommand, err error) { @@ -2453,10 +2441,6 @@ func (s *Session) ApplicationCommand(appID, guildID, cmdID string) (cmd *Applica return nil, errors.New("invalid command id") } - if appID == "" { - appID = s.State.User.ID - } - endpoint := EndpointApplicationGlobalCommand(appID, cmdID) if guildID != "" { endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) @@ -2474,13 +2458,9 @@ func (s *Session) ApplicationCommand(appID, guildID, cmdID string) (cmd *Applica } // ApplicationCommands retrieves all commands in application. -// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// appID : The application ID. // guildID : Guild ID to retrieve all guild-specific application commands. If empty - retrieves global application commands. func (s *Session) ApplicationCommands(appID, guildID string) (cmd []*ApplicationCommand, err error) { - if appID == "" { - appID = s.State.User.ID - } - endpoint := EndpointApplicationGlobalCommands(appID) if guildID != "" { endpoint = EndpointApplicationGuildCommands(appID, guildID) @@ -2498,7 +2478,7 @@ func (s *Session) ApplicationCommands(appID, guildID string) (cmd []*Application } // InteractionRespond creates the response to an interaction. -// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// appID : The application ID. // interaction : Interaction instance. // resp : Response message data. func (s *Session) InteractionRespond(interaction *Interaction, resp *InteractionResponse) error { @@ -2510,25 +2490,17 @@ func (s *Session) InteractionRespond(interaction *Interaction, resp *Interaction } // InteractionResponseEdit edits the response to an interaction. -// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// appID : The application ID. // interaction : Interaction instance. // newresp : Updated response message data. func (s *Session) InteractionResponseEdit(appID string, interaction *Interaction, newresp *WebhookEdit) error { - if appID == "" { - appID = s.State.User.ID - } - return s.WebhookMessageEdit(appID, interaction.Token, "@original", newresp) } // InteractionResponseDelete deletes the response to an interaction. -// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// appID : The application ID. // interaction : Interaction instance. func (s *Session) InteractionResponseDelete(appID string, interaction *Interaction) error { - if appID == "" { - appID = s.State.User.ID - } - endpoint := EndpointInteractionResponseActions(appID, interaction.Token) _, err := s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) @@ -2537,39 +2509,27 @@ func (s *Session) InteractionResponseDelete(appID string, interaction *Interacti } // FollowupMessageCreate creates the followup message for an interaction. -// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// appID : The application ID. // interaction : Interaction instance. // wait : Waits for server confirmation of message send and ensures that the return struct is populated (it is nil otherwise) // data : Data of the message to send. func (s *Session) FollowupMessageCreate(appID string, interaction *Interaction, wait bool, data *WebhookParams) (*Message, error) { - if appID == "" { - appID = s.State.User.ID - } - return s.WebhookExecute(appID, interaction.Token, wait, data) } // FollowupMessageEdit edits a followup message of an interaction. -// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// appID : The application ID. // interaction : Interaction instance. // messageID : The followup message ID. // data : Data to update the message func (s *Session) FollowupMessageEdit(appID string, interaction *Interaction, messageID string, data *WebhookEdit) error { - if appID == "" { - appID = s.State.User.ID - } - return s.WebhookMessageEdit(appID, interaction.Token, messageID, data) } // FollowupMessageDelete deletes a followup message of an interaction. -// appID : The application ID. If empty - s.State.User.ID. Note: works only with opened session. +// appID : The application ID. // interaction : Interaction instance. // messageID : The followup message ID. func (s *Session) FollowupMessageDelete(appID string, interaction *Interaction, messageID string) error { - if appID == "" { - appID = s.State.User.ID - } - return s.WebhookMessageDelete(appID, interaction.Token, messageID) } From 4d4cfa28271ad107d3cbf38a62e8ed53eb8e8da2 Mon Sep 17 00:00:00 2001 From: Carson Hoffman Date: Sun, 28 Feb 2021 20:58:01 -0500 Subject: [PATCH 21/28] Remove redundant command ID checks --- restapi.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/restapi.go b/restapi.go index 974da1042..048eb7bf2 100644 --- a/restapi.go +++ b/restapi.go @@ -2393,10 +2393,6 @@ func (s *Session) ApplicationCommandCreate(appID string, guildID string, cmd *Ap // guildID : Guild ID to edit guild-specific application command. If empty - edits global application command. // cmd : Updated application command data. func (s *Session) ApplicationCommandEdit(appID, cmdID, guildID string, cmd *ApplicationCommand) (oldcmd *ApplicationCommand, err error) { - if cmdID == "" { - return nil, errors.New("invalid command id") - } - endpoint := EndpointApplicationGlobalCommand(appID, cmdID) if guildID != "" { endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) @@ -2418,10 +2414,6 @@ func (s *Session) ApplicationCommandEdit(appID, cmdID, guildID string, cmd *Appl // cmdID : Application command ID to delete. // guildID : Guild ID to delete guild-specific application command. If empty - deletes global application command. func (s *Session) ApplicationCommandDelete(appID, guildID, cmdID string) error { - if cmdID == "" { - return errors.New("invalid command id") - } - endpoint := EndpointApplicationGlobalCommand(appID, cmdID) if guildID != "" { endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) @@ -2437,10 +2429,6 @@ func (s *Session) ApplicationCommandDelete(appID, guildID, cmdID string) error { // cmdID : Application command ID. // guildID : Guild ID to retrieve guild-specific application command. If empty - retrieves global application command. func (s *Session) ApplicationCommand(appID, guildID, cmdID string) (cmd *ApplicationCommand, err error) { - if cmdID == "" { - return nil, errors.New("invalid command id") - } - endpoint := EndpointApplicationGlobalCommand(appID, cmdID) if guildID != "" { endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) From 01ab4973647e0387342b8d0466302c0bf92e5699 Mon Sep 17 00:00:00 2001 From: Carson Hoffman Date: Sun, 28 Feb 2021 20:58:33 -0500 Subject: [PATCH 22/28] Fix typo in ApplicationCommandCreate comment --- restapi.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/restapi.go b/restapi.go index 048eb7bf2..3875c84b3 100644 --- a/restapi.go +++ b/restapi.go @@ -2368,7 +2368,7 @@ func (s *Session) RelationshipsMutualGet(userID string) (mf []*User, err error) // ApplicationCommandCreate creates an global application command and returns it. // appID : The application ID. -// guildID : Guild ID to create guild-specific application command. If empty - creates global appication command. +// guildID : Guild ID to create guild-specific application command. If empty - creates global application command. // cmd : New application command data. func (s *Session) ApplicationCommandCreate(appID string, guildID string, cmd *ApplicationCommand) (ccmd *ApplicationCommand, err error) { endpoint := EndpointApplicationGlobalCommands(appID) From 372eb3218c7d686c13521ed71b6f6f747073de6f Mon Sep 17 00:00:00 2001 From: Carson Hoffman Date: Sun, 28 Feb 2021 21:00:00 -0500 Subject: [PATCH 23/28] Tidy up function calls returning body --- restapi.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/restapi.go b/restapi.go index 3875c84b3..908047336 100644 --- a/restapi.go +++ b/restapi.go @@ -2376,8 +2376,7 @@ func (s *Session) ApplicationCommandCreate(appID string, guildID string, cmd *Ap endpoint = EndpointApplicationGuildCommands(appID, guildID) } - var body []byte - body, err = s.RequestWithBucketID("POST", endpoint, *cmd, endpoint) + body, err := s.RequestWithBucketID("POST", endpoint, *cmd, endpoint) if err != nil { return } @@ -2398,8 +2397,7 @@ func (s *Session) ApplicationCommandEdit(appID, cmdID, guildID string, cmd *Appl endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) } - var body []byte - body, err = s.RequestWithBucketID("PATCH", endpoint, *cmd, endpoint) + body, err := s.RequestWithBucketID("PATCH", endpoint, *cmd, endpoint) if err != nil { return } @@ -2434,8 +2432,7 @@ func (s *Session) ApplicationCommand(appID, guildID, cmdID string) (cmd *Applica endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) } - var body []byte - body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + body, err := s.RequestWithBucketID("GET", endpoint, nil, endpoint) if err != nil { return } @@ -2454,8 +2451,7 @@ func (s *Session) ApplicationCommands(appID, guildID string) (cmd []*Application endpoint = EndpointApplicationGuildCommands(appID, guildID) } - var body []byte - body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + body, err := s.RequestWithBucketID("GET", endpoint, nil, endpoint) if err != nil { return } From f6396f4a5267f94f0505e52239667056fc745d35 Mon Sep 17 00:00:00 2001 From: Carson Hoffman Date: Sun, 28 Feb 2021 21:10:33 -0500 Subject: [PATCH 24/28] Add upcoming API changes --- interactions.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/interactions.go b/interactions.go index 0739d1102..1afc61a0b 100644 --- a/interactions.go +++ b/interactions.go @@ -216,13 +216,16 @@ const ( // InteractionResponsePong is for ACK ping event. InteractionResponsePong = InteractionResponseType(iota + 1) // InteractionResponseAcknowledge is for ACK a command without sending a message, eating the user's input. + // NOTE: this type is being imminently deprecated, and **will be removed when this occurs.** InteractionResponseAcknowledge // InteractionResponseChannelMessage is for responding with a message, eating the user's input. + // NOTE: this type is being imminently deprecated, and **will be removed when this occurs.** InteractionResponseChannelMessage // InteractionResponseChannelMessageWithSource is for responding with a message, showing the user's input. InteractionResponseChannelMessageWithSource - // InteractionResponseACKWithSource is for ACK a command without sending a message, showing the user's input. - InteractionResponseACKWithSource + // InteractionResponseDeferredChannelMessageWithSource acknowledges that the event was received, and that a follow-up will come later. + // It was previously named InteractionResponseACKWithSource. + InteractionResponseDeferredChannelMessageWithSource ) // InteractionResponse is representing response for interaction with application. From ae60d65100c4bea1ad01bfcd79dbf38b6b735175 Mon Sep 17 00:00:00 2001 From: Carson Hoffman Date: Sun, 28 Feb 2021 21:16:29 -0500 Subject: [PATCH 25/28] Correct return value name, swap parameter order --- restapi.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/restapi.go b/restapi.go index 908047336..139508406 100644 --- a/restapi.go +++ b/restapi.go @@ -2391,7 +2391,7 @@ func (s *Session) ApplicationCommandCreate(appID string, guildID string, cmd *Ap // cmdID : Application command ID to edit. // guildID : Guild ID to edit guild-specific application command. If empty - edits global application command. // cmd : Updated application command data. -func (s *Session) ApplicationCommandEdit(appID, cmdID, guildID string, cmd *ApplicationCommand) (oldcmd *ApplicationCommand, err error) { +func (s *Session) ApplicationCommandEdit(appID, guildID, cmdID string, cmd *ApplicationCommand) (updated *ApplicationCommand, err error) { endpoint := EndpointApplicationGlobalCommand(appID, cmdID) if guildID != "" { endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) @@ -2402,7 +2402,7 @@ func (s *Session) ApplicationCommandEdit(appID, cmdID, guildID string, cmd *Appl return } - err = unmarshal(body, &oldcmd) + err = unmarshal(body, &updated) return } From da4ca39965972e620dcf67bdbdcee1d89592b803 Mon Sep 17 00:00:00 2001 From: Carson Hoffman Date: Sun, 28 Feb 2021 21:16:44 -0500 Subject: [PATCH 26/28] Add Version field to ApplicationCommand --- interactions.go | 1 + 1 file changed, 1 insertion(+) diff --git a/interactions.go b/interactions.go index 1afc61a0b..175b3b5d5 100644 --- a/interactions.go +++ b/interactions.go @@ -19,6 +19,7 @@ type ApplicationCommand struct { ApplicationID string `json:"application_id,omitempty"` Name string `json:"name"` Description string `json:"description,omitempty"` + Version string `json:"version,omitempty"` Options []*ApplicationCommandOption `json:"options"` } From 2274e22622dbc574ca0bea730f312a13ecb9c178 Mon Sep 17 00:00:00 2001 From: Carson Hoffman Date: Sun, 28 Feb 2021 21:27:07 -0500 Subject: [PATCH 27/28] Fix up language in comments --- interactions.go | 38 +++++++++++++++++++------------------- restapi.go | 8 ++++---- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/interactions.go b/interactions.go index 175b3b5d5..75c6457bb 100644 --- a/interactions.go +++ b/interactions.go @@ -10,10 +10,10 @@ import ( "time" ) -// InteractionDeadline is a deadline for responding to an interaction, if you haven't responded in the time, you won't be able to respond later. +// InteractionDeadline is the time allowed to respond to an interaction. const InteractionDeadline = time.Second * 3 -// ApplicationCommand is representing application's slash command. +// ApplicationCommand represents an application's slash command. type ApplicationCommand struct { ID string `json:"id"` ApplicationID string `json:"application_id,omitempty"` @@ -23,7 +23,7 @@ type ApplicationCommand struct { Options []*ApplicationCommandOption `json:"options"` } -// ApplicationCommandOptionType is type of an slash-command's option. +// ApplicationCommandOptionType indicates the type of a slash command's option. type ApplicationCommandOptionType uint8 // Application command option types. @@ -38,7 +38,7 @@ const ( ApplicationCommandOptionRole ) -// ApplicationCommandOption is representing an option/subcommand/subcommands group. +// ApplicationCommandOption represents an option/subcommand/subcommands group. type ApplicationCommandOption struct { Type ApplicationCommandOptionType `json:"type"` Name string `json:"name"` @@ -51,13 +51,13 @@ type ApplicationCommandOption struct { Options []*ApplicationCommandOption `json:"options"` } -// ApplicationCommandOptionChoice is representing slash-command's option choice. +// ApplicationCommandOptionChoice represents a slash command option choice. type ApplicationCommandOptionChoice struct { Name string `json:"name"` Value interface{} `json:"value"` } -// InteractionType is representing interaction type. +// InteractionType indicates the type of an interaction event. type InteractionType uint8 // Interaction types @@ -66,7 +66,7 @@ const ( InteractionApplicationCommand ) -// Interaction is representing interaction with application. +// Interaction represents an interaction event created via a slash command. type Interaction struct { ID string `json:"id"` Type InteractionType `json:"type"` @@ -78,14 +78,14 @@ type Interaction struct { Version int `json:"version"` } -// ApplicationCommandInteractionData is representing interaction data for application command. +// ApplicationCommandInteractionData contains data received in an interaction event. type ApplicationCommandInteractionData struct { ID string `json:"id"` Name string `json:"name"` Options []*ApplicationCommandInteractionDataOption `json:"options"` } -// ApplicationCommandInteractionDataOption is representing an option of application's command. +// ApplicationCommandInteractionDataOption represents an option of a slash command. type ApplicationCommandInteractionDataOption struct { Name string `json:"name"` // NOTE: Contains the value specified by InteractionType. @@ -93,7 +93,7 @@ type ApplicationCommandInteractionDataOption struct { Options []*ApplicationCommandInteractionDataOption `json:"options,omitempty"` } -// IntValue is utility function for casting option value to integer +// IntValue is a utility function for casting option value to integer func (o ApplicationCommandInteractionDataOption) IntValue() int64 { if v, ok := o.Value.(float64); ok { return int64(v) @@ -102,7 +102,7 @@ func (o ApplicationCommandInteractionDataOption) IntValue() int64 { return 0 } -// UintValue is utility function for casting option value to unsigned integer +// UintValue is a utility function for casting option value to unsigned integer func (o ApplicationCommandInteractionDataOption) UintValue() uint64 { if v, ok := o.Value.(float64); ok { return uint64(v) @@ -111,7 +111,7 @@ func (o ApplicationCommandInteractionDataOption) UintValue() uint64 { return 0 } -// FloatValue is utility function for casting option value to float +// FloatValue is a utility function for casting option value to float func (o ApplicationCommandInteractionDataOption) FloatValue() float64 { if v, ok := o.Value.(float64); ok { return float64(v) @@ -120,7 +120,7 @@ func (o ApplicationCommandInteractionDataOption) FloatValue() float64 { return 0.0 } -// StringValue is utility function for casting option value to string +// StringValue is a utility function for casting option value to string func (o ApplicationCommandInteractionDataOption) StringValue() string { if v, ok := o.Value.(string); ok { return v @@ -129,7 +129,7 @@ func (o ApplicationCommandInteractionDataOption) StringValue() string { return "" } -// BoolValue is utility function for casting option value to bool +// BoolValue is a utility function for casting option value to bool func (o ApplicationCommandInteractionDataOption) BoolValue() bool { if v, ok := o.Value.(bool); ok { return v @@ -138,7 +138,7 @@ func (o ApplicationCommandInteractionDataOption) BoolValue() bool { return false } -// ChannelValue is utility function for casting option value to channel object. +// ChannelValue is a utility function for casting option value to channel object. // s : Session object, if not nil, function additionaly fetches all channel's data func (o ApplicationCommandInteractionDataOption) ChannelValue(s *Session) *Channel { chanID := o.StringValue() @@ -161,7 +161,7 @@ func (o ApplicationCommandInteractionDataOption) ChannelValue(s *Session) *Chann return ch } -// RoleValue is utility function for casting option value to role object. +// RoleValue is a utility function for casting option value to role object. // s : Session object, if not nil, function additionaly fetches all role's data func (o ApplicationCommandInteractionDataOption) RoleValue(s *Session, gID string) *Role { roleID := o.StringValue() @@ -189,7 +189,7 @@ func (o ApplicationCommandInteractionDataOption) RoleValue(s *Session, gID strin return r } -// UserValue is utility function for casting option value to user object. +// UserValue is a utility function for casting option value to user object. // s : Session object, if not nil, function additionaly fetches all user's data func (o ApplicationCommandInteractionDataOption) UserValue(s *Session) *User { userID := o.StringValue() @@ -229,13 +229,13 @@ const ( InteractionResponseDeferredChannelMessageWithSource ) -// InteractionResponse is representing response for interaction with application. +// InteractionResponse represents a response for an interaction event. type InteractionResponse struct { Type InteractionResponseType `json:"type,omitempty"` Data *InteractionApplicationCommandResponseData `json:"data,omitempty"` } -// InteractionApplicationCommandResponseData is callback data for application command interaction. +// InteractionApplicationCommandResponseData is response data for a slash command interaction. type InteractionApplicationCommandResponseData struct { TTS bool `json:"tts,omitempty"` Content string `json:"content,omitempty"` diff --git a/restapi.go b/restapi.go index 139508406..cfbe320cc 100644 --- a/restapi.go +++ b/restapi.go @@ -2366,7 +2366,7 @@ func (s *Session) RelationshipsMutualGet(userID string) (mf []*User, err error) // Functions specific to application (slash) commands // ------------------------------------------------------------------------------------------------ -// ApplicationCommandCreate creates an global application command and returns it. +// ApplicationCommandCreate creates a global application command and returns it. // appID : The application ID. // guildID : Guild ID to create guild-specific application command. If empty - creates global application command. // cmd : New application command data. @@ -2386,7 +2386,7 @@ func (s *Session) ApplicationCommandCreate(appID string, guildID string, cmd *Ap return } -// ApplicationCommandEdit edits application command and returns old command data. +// ApplicationCommandEdit edits application command and returns new command data. // appID : The application ID. // cmdID : Application command ID to edit. // guildID : Guild ID to edit guild-specific application command. If empty - edits global application command. @@ -2407,7 +2407,7 @@ func (s *Session) ApplicationCommandEdit(appID, guildID, cmdID string, cmd *Appl return } -// ApplicationCommandDelete deletes application command by id. +// ApplicationCommandDelete deletes application command by ID. // appID : The application ID. // cmdID : Application command ID to delete. // guildID : Guild ID to delete guild-specific application command. If empty - deletes global application command. @@ -2422,7 +2422,7 @@ func (s *Session) ApplicationCommandDelete(appID, guildID, cmdID string) error { return err } -// ApplicationCommand retrieves an application command by given id. +// ApplicationCommand retrieves an application command by given ID. // appID : The application ID. // cmdID : Application command ID. // guildID : Guild ID to retrieve guild-specific application command. If empty - retrieves global application command. From 4a31b482368e6013f3fcc2d6657b070ee6685f33 Mon Sep 17 00:00:00 2001 From: Carson Hoffman Date: Sun, 28 Feb 2021 21:39:53 -0500 Subject: [PATCH 28/28] Remove redundant conversion to float64 --- interactions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactions.go b/interactions.go index 75c6457bb..d4ccbe817 100644 --- a/interactions.go +++ b/interactions.go @@ -114,7 +114,7 @@ func (o ApplicationCommandInteractionDataOption) UintValue() uint64 { // FloatValue is a utility function for casting option value to float func (o ApplicationCommandInteractionDataOption) FloatValue() float64 { if v, ok := o.Value.(float64); ok { - return float64(v) + return v } return 0.0