Skip to content

Commit

Permalink
discord: Refactor interactions and components
Browse files Browse the repository at this point in the history
This commit gets rid of contain-it-all structs and instead opt for
interface union types containing underlying concrete types with no
overloading.

The code is much more verbose by doing this, but the API is much nicer
to use. The only disadvantage in that regard is the interface assertion
being too verbose and risky for users at times.
  • Loading branch information
diamondburned committed Nov 12, 2021
1 parent 8abd5bc commit 331ec59
Show file tree
Hide file tree
Showing 22 changed files with 1,838 additions and 689 deletions.
107 changes: 57 additions & 50 deletions 0-examples/buttons/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,82 +19,85 @@ func main() {

token := os.Getenv("BOT_TOKEN")
if token == "" {
log.Fatalln("No $BOT_TOKEN given.")
log.Fatalln("no $BOT_TOKEN given")
}

s, err := session.New("Bot " + token)
if err != nil {
log.Fatalln("Session failed:", err)
log.Fatalln("session failed:", err)
return
}

app, err := s.CurrentApplication()
if err != nil {
log.Fatalln("Failed to get application ID:", err)
log.Fatalln("failed to get application ID:", err)
}
appID := app.ID

s.AddHandler(func(e *gateway.InteractionCreateEvent) {
if e.Type == discord.CommandInteraction {
var resp api.InteractionResponse

switch data := e.Data.(type) {
case *discord.CommandInteraction:
if data.Name != "buttons" {
resp = api.InteractionResponse{
Type: api.MessageInteractionWithSource,
Data: &api.InteractionResponseData{
Content: option.NewNullableString("Unknown command: " + data.Name),
},
}
break
}
// Send a message with a button back on slash commands.
data := api.InteractionResponse{
resp = api.InteractionResponse{
Type: api.MessageInteractionWithSource,
Data: &api.InteractionResponseData{
Content: option.NewNullableString("This is a message with a button!"),
Components: &[]discord.Component{
Components: discord.ComponentsPtr(
&discord.ActionRowComponent{
Components: []discord.Component{
&discord.ButtonComponent{
Label: "Hello World!",
CustomID: "first_button",
Emoji: &discord.ButtonEmoji{
Name: "👋",
},
Style: discord.PrimaryButton,
},
&discord.ButtonComponent{
Label: "Secondary",
CustomID: "second_button",
Style: discord.SecondaryButton,
},
&discord.ButtonComponent{
Label: "Success",
CustomID: "success_button",
Style: discord.SuccessButton,
},
&discord.ButtonComponent{
Label: "Danger",
CustomID: "danger_button",
Style: discord.DangerButton,
},
&discord.ButtonComponent{
Label: "Link",
URL: "https://google.com",
Style: discord.LinkButton,
},
&discord.ButtonComponent{
Label: "Hello World!",
CustomID: "first_button",
Emoji: &discord.ComponentEmoji{Name: "👋"},
Style: discord.PrimaryButtonStyle(),
},
&discord.ButtonComponent{
Label: "Secondary",
CustomID: "second_button",
Style: discord.SecondaryButtonStyle(),
},
&discord.ButtonComponent{
Label: "Success",
CustomID: "success_button",
Style: discord.SuccessButtonStyle(),
},
&discord.ButtonComponent{
Label: "Danger",
CustomID: "danger_button",
Style: discord.DangerButtonStyle(),
},
},
},
// This is automatically put into its own row.
&discord.ButtonComponent{
Label: "Link",
Style: discord.LinkButtonStyle("https://google.com"),
},
),
},
}

if err := s.RespondInteraction(e.ID, e.Token, data); err != nil {
log.Println("failed to send interaction callback:", err)
case discord.ComponentInteraction:
resp = api.InteractionResponse{
Type: api.UpdateMessage,
Data: &api.InteractionResponseData{
Content: option.NewNullableString("Custom ID: " + string(data.ID())),
},
}
}

if e.Type != discord.ComponentInteraction {
default:
log.Printf("unknown interaction type %T", e.Data)
return
}
customID := e.Data.(*discord.ComponentInteractionData).CustomID
data := api.InteractionResponse{
Type: api.UpdateMessage,
Data: &api.InteractionResponseData{
Content: option.NewNullableString("Custom ID: " + customID),
},
}

if err := s.RespondInteraction(e.ID, e.Token, data); err != nil {
if err := s.RespondInteraction(e.ID, e.Token, resp); err != nil {
log.Println("failed to send interaction callback:", err)
}
})
Expand Down Expand Up @@ -125,13 +128,17 @@ func main() {
},
}

log.Println("Creating guild commands...")

for _, command := range newCommands {
_, err := s.CreateGuildCommand(appID, guildID, command)
if err != nil {
log.Fatalln("failed to create guild command:", err)
}
}

log.Println("Guild commands created. Bot is ready.")

// Block forever.
select {}
}
Expand Down
9 changes: 4 additions & 5 deletions 0-examples/commands/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"github.com/diamondburned/arikawa/v3/api"
"github.com/diamondburned/arikawa/v3/discord"
"github.com/diamondburned/arikawa/v3/gateway"
"github.com/diamondburned/arikawa/v3/session"
"github.com/diamondburned/arikawa/v3/state"
"github.com/diamondburned/arikawa/v3/utils/json/option"
)

Expand All @@ -22,7 +22,7 @@ func main() {
log.Fatalln("No $BOT_TOKEN given.")
}

s, err := session.New("Bot " + token)
s, err := state.New("Bot " + token)
if err != nil {
log.Fatalln("Session failed:", err)
return
Expand All @@ -32,7 +32,6 @@ func main() {
if err != nil {
log.Fatalln("Failed to get application ID:", err)
}
appID := app.ID

s.AddHandler(func(e *gateway.InteractionCreateEvent) {
data := api.InteractionResponse{
Expand All @@ -57,7 +56,7 @@ func main() {

log.Println("Gateway connected. Getting all guild commands.")

commands, err := s.GuildCommands(appID, guildID)
commands, err := s.GuildCommands(app.ID, guildID)
if err != nil {
log.Fatalln("failed to get guild commands:", err)
}
Expand All @@ -74,7 +73,7 @@ func main() {
}

for _, command := range newCommands {
_, err := s.CreateGuildCommand(appID, guildID, command)
_, err := s.CreateGuildCommand(app.ID, guildID, command)
if err != nil {
log.Fatalln("failed to create guild command:", err)
}
Expand Down
10 changes: 5 additions & 5 deletions api/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ func (c *Client) CurrentApplication() (*discord.Application, error) {

// https://discord.com/developers/docs/interactions/slash-commands#create-global-application-command-json-params
type CreateCommandData struct {
Name string `json:"name"`
Description string `json:"description"`
Options []discord.CommandOption `json:"options,omitempty"`
NoDefaultPermission bool `json:"-"`
Type discord.CommandType `json:"type,omitempty"`
Name string `json:"name"`
Description string `json:"description"`
Options discord.CommandOptions `json:"options,omitempty"`
NoDefaultPermission bool `json:"-"`
Type discord.CommandType `json:"type,omitempty"`
}

func (c CreateCommandData) MarshalJSON() ([]byte, error) {
Expand Down
4 changes: 2 additions & 2 deletions api/interaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ type InteractionResponseData struct {
Embeds *[]discord.Embed `json:"embeds,omitempty"`
// Components is the list of components (such as buttons) to be attached to
// the message.
Components *[]discord.Component `json:"components,omitempty"`
Components *discord.ContainerComponents `json:"components,omitempty"`
// AllowedMentions are the allowed mentions for the message.
AllowedMentions *AllowedMentions `json:"allowed_mentions,omitempty"`
// Flags are the interaction application command callback data flags.
Expand Down Expand Up @@ -162,7 +162,7 @@ type EditInteractionResponseData struct {
// Embeds contains embedded rich content.
Embeds *[]discord.Embed `json:"embeds,omitempty"`
// Components contains the new components to attach.
Components *[]discord.Component `json:"components,omitempty"`
Components *discord.ContainerComponents `json:"components,omitempty"`
// AllowedMentions are the allowed mentions for the message.
AllowedMentions *AllowedMentions `json:"allowed_mentions,omitempty"`
// Attachments are the attached files to keep.
Expand Down
2 changes: 1 addition & 1 deletion api/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ type EditMessageData struct {
// Embeds contains embedded rich content.
Embeds *[]discord.Embed `json:"embeds,omitempty"`
// Components contains the new components to attach.
Components *[]discord.Component `json:"components,omitempty"`
Components *discord.ContainerComponents `json:"components,omitempty"`
// AllowedMentions are the allowed mentions for a message.
AllowedMentions *AllowedMentions `json:"allowed_mentions,omitempty"`
// Attachments are the attached files to keep
Expand Down
2 changes: 1 addition & 1 deletion api/send.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ type SendMessageData struct {
Files []sendpart.File `json:"-"`
// Components is the list of components (such as buttons) to be attached to
// the message.
Components []discord.Component `json:"components,omitempty"`
Components discord.ContainerComponents `json:"components,omitempty"`

// AllowedMentions are the allowed mentions for a message.
AllowedMentions *AllowedMentions `json:"allowed_mentions,omitempty"`
Expand Down
4 changes: 2 additions & 2 deletions api/webhook/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ type ExecuteData struct {

// Components is the list of components (such as buttons) to be attached to
// the message.
Components []discord.Component `json:"components,omitempty"`
Components discord.ContainerComponents `json:"components,omitempty"`

// Files represents a list of files to upload. This will not be
// JSON-encoded and will only be available through WriteMultipart.
Expand Down Expand Up @@ -238,7 +238,7 @@ type EditMessageData struct {
// Embeds contains embedded rich content.
Embeds *[]discord.Embed `json:"embeds,omitempty"`
// Components contains the new components to attach.
Components *[]discord.Component `json:"components,omitempty"`
Components *discord.ContainerComponents `json:"components,omitempty"`
// AllowedMentions are the allowed mentions for a message.
AllowedMentions *api.AllowedMentions `json:"allowed_mentions,omitempty"`
// Attachments are the attached files to keep
Expand Down
Loading

0 comments on commit 331ec59

Please sign in to comment.