Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slash commands #856

Merged
merged 31 commits into from Mar 1, 2021
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d7f3449
UnknownBan error code addition
FedorLap2006 Dec 11, 2020
356455e
GuildBan method implementation
FedorLap2006 Dec 12, 2020
d703a29
Gofmt fix
FedorLap2006 Dec 12, 2020
854e96d
Merge branch 'master' of github.com:bwmarrin/discordgo into slashes
FedorLap2006 Dec 19, 2020
4aeca90
Interactions: application commands basic API and gateway integration
FedorLap2006 Dec 24, 2020
af426c1
Some gitignore update
FedorLap2006 Jan 16, 2021
c1d8bff
Application commands and interactions API implementation
FedorLap2006 Jan 16, 2021
3f37ec0
Some fixes
FedorLap2006 Jan 17, 2021
5b23e34
Some improvements of slash-commands example and slash-commands API
FedorLap2006 Jan 17, 2021
1c8faf9
OAuth2 endpoints backward compatibility
FedorLap2006 Jan 17, 2021
9c6bf00
Gofmt fix
FedorLap2006 Jan 17, 2021
f554a2f
Requested fixes and documentation improvement for application commands
FedorLap2006 Jan 19, 2021
b76c150
Merge branch 'master' of github.com:bwmarrin/discordgo into slashes
FedorLap2006 Jan 22, 2021
f4cfc0f
Merge branch 'master' of github.com:bwmarrin/discordgo into slashes
FedorLap2006 Feb 6, 2021
2028ce6
Some fixes
FedorLap2006 Feb 16, 2021
75a2e2d
New and more interesting example of slash-commands usage, merging "in…
FedorLap2006 Feb 16, 2021
f5375a0
Gofmt and documentation fixes
FedorLap2006 Feb 16, 2021
00e6233
More fixes
FedorLap2006 Feb 17, 2021
a1b4fed
Gofmt fixes
FedorLap2006 Feb 17, 2021
8718e2d
More fixes!
FedorLap2006 Feb 17, 2021
4b3f141
Doc and endpoint fixes
FedorLap2006 Feb 28, 2021
3bc1011
Gofmt fix
FedorLap2006 Feb 28, 2021
1f39a0c
Remove dependence on open gateway connection
CarsonHoffman Mar 1, 2021
4d4cfa2
Remove redundant command ID checks
CarsonHoffman Mar 1, 2021
01ab497
Fix typo in ApplicationCommandCreate comment
CarsonHoffman Mar 1, 2021
372eb32
Tidy up function calls returning body
CarsonHoffman Mar 1, 2021
f6396f4
Add upcoming API changes
CarsonHoffman Mar 1, 2021
ae60d65
Correct return value name, swap parameter order
CarsonHoffman Mar 1, 2021
da4ca39
Add Version field to ApplicationCommand
CarsonHoffman Mar 1, 2021
2274e22
Fix up language in comments
CarsonHoffman Mar 1, 2021
4a31b48
Remove redundant conversion to float64
CarsonHoffman Mar 1, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
@@ -1,2 +1,5 @@
# IDE-specific metadata
.idea/

# Environment variables. Useful for examples.
.env
FedorLap2006 marked this conversation as resolved.
Show resolved Hide resolved
FedorLap2006 marked this conversation as resolved.
Show resolved Hide resolved
44 changes: 39 additions & 5 deletions endpoints.go
Expand Up @@ -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
FedorLap2006 marked this conversation as resolved.
Show resolved Hide resolved
}

EndpointMessageReactionsAll = func(cID, mID string) string {
return EndpointChannelMessage(cID, mID) + "/reactions"
Expand All @@ -131,6 +134,35 @@ var (
return EndpointMessageReactions(cID, mID, eID) + "/" + uID
}

EndpointApplicationGlobalCommands = func(aID string) string {
return EndpointApplication(aID) + "/" + "commands"
FedorLap2006 marked this conversation as resolved.
Show resolved Hide resolved
}
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
}
EndpointInteraction = func(aID, iToken string) string {
return EndpointAPI + "interactions/" + aID + "/" + iToken
}
EndpointInteractionResponse = func(iID, iToken string) string {
return EndpointInteraction(iID, iToken) + "/" + "callback"
FedorLap2006 marked this conversation as resolved.
Show resolved Hide resolved
}
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 }
EndpointRelationshipsMutual = func(uID string) string { return EndpointUsers + uID + "/relationships" }
Expand All @@ -144,9 +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/"
FedorLap2006 marked this conversation as resolved.
Show resolved Hide resolved
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" }
)
24 changes: 24 additions & 0 deletions eventhandlers.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions events.go
Expand Up @@ -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
}
120 changes: 120 additions & 0 deletions examples/slash_commands/main.go
@@ -0,0 +1,120 @@
package main

import (
"flag"
dgo "github.com/bwmarrin/discordgo"
FedorLap2006 marked this conversation as resolved.
Show resolved Hide resolved
FedorLap2006 marked this conversation as resolved.
Show resolved Hide resolved
"log"
"os"
"os/signal"
"syscall"
"time"
)

var (
botToken = flag.String("bot_tok", "", "Bot token")
FedorLap2006 marked this conversation as resolved.
Show resolved Hide resolved
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 {
log.Fatal(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)
}
}
122 changes: 122 additions & 0 deletions interaction.go
@@ -0,0 +1,122 @@
package discordgo

import (
"time"
)

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 (
FedorLap2006 marked this conversation as resolved.
Show resolved Hide resolved
_ = ApplicationCommandOptionType(iota)
FedorLap2006 marked this conversation as resolved.
Show resolved Hide resolved

ApplicationCommandOptionSubCommand
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"`
}

// ApplicationCommandOption 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

const (
_ = InteractionType(iota)
FedorLap2006 marked this conversation as resolved.
Show resolved Hide resolved
// 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,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 (
_ = 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.
FedorLap2006 marked this conversation as resolved.
Show resolved Hide resolved
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,omitempty"`
Data *InteractionApplicationCommandResponseData `json:"data,omitempty"`
}

// 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"` // NOTE: Undocumented feature, be careful with it.
}