examples: Update commands to use cmdroute

This commit is contained in:
diamondburned 2022-12-10 06:03:04 -08:00
parent 080c734b37
commit ced922d377
No known key found for this signature in database
GPG Key ID: D78C4471CE776659
4 changed files with 137 additions and 158 deletions

View File

@ -2,7 +2,6 @@ package main
import ( import (
"context" "context"
"fmt"
"log" "log"
"math/rand" "math/rand"
"net/http" "net/http"
@ -10,12 +9,12 @@ import (
"time" "time"
"github.com/diamondburned/arikawa/v3/api" "github.com/diamondburned/arikawa/v3/api"
"github.com/diamondburned/arikawa/v3/api/cmdroute"
"github.com/diamondburned/arikawa/v3/api/webhook" "github.com/diamondburned/arikawa/v3/api/webhook"
"github.com/diamondburned/arikawa/v3/discord" "github.com/diamondburned/arikawa/v3/discord"
"github.com/diamondburned/arikawa/v3/gateway" "github.com/diamondburned/arikawa/v3/gateway"
"github.com/diamondburned/arikawa/v3/state" "github.com/diamondburned/arikawa/v3/state"
"github.com/diamondburned/arikawa/v3/utils/json/option" "github.com/diamondburned/arikawa/v3/utils/json/option"
"github.com/pkg/errors"
) )
var commands = []api.CreateCommandData{ var commands = []api.CreateCommandData{
@ -46,37 +45,39 @@ func main() {
log.Fatalln("No $BOT_TOKEN given.") log.Fatalln("No $BOT_TOKEN given.")
} }
var h handler
var ( var (
webhookAddr = os.Getenv("WEBHOOK_ADDR") webhookAddr = os.Getenv("WEBHOOK_ADDR")
webhookPubkey = os.Getenv("WEBHOOK_PUBKEY") webhookPubkey = os.Getenv("WEBHOOK_PUBKEY")
) )
if webhookAddr != "" { if webhookAddr != "" {
h.s = state.NewAPIOnlyState(token, nil) state := state.NewAPIOnlyState(token, nil)
srv, err := webhook.NewInteractionServer(webhookPubkey, &h) h := newHandler(state)
if err != nil {
log.Fatalln("cannot create interaction server:", err) if err := overwriteCommands(state); err != nil {
log.Fatalln("cannot update commands:", err)
} }
if err := overwriteCommands(h.s); err != nil { srv, err := webhook.NewInteractionServer(webhookPubkey, h)
log.Fatalln("cannot update commands:", err) if err != nil {
log.Fatalln("cannot create interaction server:", err)
} }
log.Println("listening and serving at", webhookAddr+"/") log.Println("listening and serving at", webhookAddr+"/")
log.Fatalln(http.ListenAndServe(webhookAddr, srv)) log.Fatalln(http.ListenAndServe(webhookAddr, srv))
} else { } else {
h.s = state.New("Bot " + token) state := state.New("Bot " + token)
h.s.AddInteractionHandler(&h) state.AddIntents(gateway.IntentGuilds)
h.s.AddIntents(gateway.IntentGuilds) state.AddHandler(func(*gateway.ReadyEvent) {
h.s.AddHandler(func(*gateway.ReadyEvent) { me, _ := state.Me()
me, _ := h.s.Me()
log.Println("connected to the gateway as", me.Tag()) log.Println("connected to the gateway as", me.Tag())
}) })
if err := overwriteCommands(h.s); err != nil { h := newHandler(state)
state.AddInteractionHandler(h)
if err := overwriteCommands(state); err != nil {
log.Fatalln("cannot update commands:", err) log.Fatalln("cannot update commands:", err)
} }
@ -87,47 +88,34 @@ func main() {
} }
func overwriteCommands(s *state.State) error { func overwriteCommands(s *state.State) error {
app, err := s.CurrentApplication() return cmdroute.OverwriteCommands(s, commands)
if err != nil {
return errors.Wrap(err, "cannot get current app ID")
}
_, err = s.BulkOverwriteCommands(app.ID, commands)
return err
} }
type handler struct { type handler struct {
*cmdroute.Router
s *state.State s *state.State
} }
func (h *handler) HandleInteraction(ev *discord.InteractionEvent) *api.InteractionResponse { func newHandler(s *state.State) *handler {
switch data := ev.Data.(type) { h := &handler{s: s}
case *discord.CommandInteraction:
switch data.Name { h.Router = cmdroute.NewRouter()
case "ping": // Automatically defer handles if they're slow.
return h.cmdPing(ev, data) h.Use(cmdroute.Deferrable(s, cmdroute.DeferOpts{}))
case "echo": h.AddFunc("ping", h.cmdPing)
return h.cmdEcho(ev, data) h.AddFunc("echo", h.cmdEcho)
case "thonk": h.AddFunc("thonk", h.cmdThonk)
return h.cmdThonk(ev, data)
default: return h
return errorResponse(fmt.Errorf("unknown command %q", data.Name)) }
}
default: func (h *handler) cmdPing(ctx context.Context, cmd cmdroute.CommandData) *api.InteractionResponseData {
return errorResponse(fmt.Errorf("unknown interaction %T", ev.Data)) return &api.InteractionResponseData{
Content: option.NewNullableString("Pong!"),
} }
} }
func (h *handler) cmdPing(ev *discord.InteractionEvent, _ *discord.CommandInteraction) *api.InteractionResponse { func (h *handler) cmdEcho(ctx context.Context, data cmdroute.CommandData) *api.InteractionResponseData {
return &api.InteractionResponse{
Type: api.MessageInteractionWithSource,
Data: &api.InteractionResponseData{
Content: option.NewNullableString("Pong!"),
},
}
}
func (h *handler) cmdEcho(ev *discord.InteractionEvent, data *discord.CommandInteraction) *api.InteractionResponse {
var options struct { var options struct {
Arg string `discord:"argument"` Arg string `discord:"argument"`
} }
@ -136,43 +124,23 @@ func (h *handler) cmdEcho(ev *discord.InteractionEvent, data *discord.CommandInt
return errorResponse(err) return errorResponse(err)
} }
return &api.InteractionResponse{ return &api.InteractionResponseData{
Type: api.MessageInteractionWithSource, Content: option.NewNullableString(options.Arg),
Data: &api.InteractionResponseData{ AllowedMentions: &api.AllowedMentions{}, // don't mention anyone
Content: option.NewNullableString(options.Arg),
AllowedMentions: &api.AllowedMentions{},
},
} }
} }
func (h *handler) cmdThonk(ev *discord.InteractionEvent, data *discord.CommandInteraction) *api.InteractionResponse { func (h *handler) cmdThonk(ctx context.Context, data cmdroute.CommandData) *api.InteractionResponseData {
go func() { time.Sleep(time.Duration(3+rand.Intn(5)) * time.Second)
time.Sleep(time.Duration(3+rand.Intn(5)) * time.Second) return &api.InteractionResponseData{
Content: option.NewNullableString("https://tenor.com/view/thonk-thinking-sun-thonk-sun-thinking-sun-gif-14999983"),
h.s.FollowUpInteraction(ev.AppID, ev.Token, api.InteractionResponseData{
Content: option.NewNullableString("https://tenor.com/view/thonk-thinking-sun-thonk-sun-thinking-sun-gif-14999983"),
})
}()
return deferResponse(0)
}
func errorResponse(err error) *api.InteractionResponse {
return &api.InteractionResponse{
Type: api.MessageInteractionWithSource,
Data: &api.InteractionResponseData{
Content: option.NewNullableString("**Error:** " + err.Error()),
Flags: discord.EphemeralMessage,
AllowedMentions: &api.AllowedMentions{ /* none */ },
},
} }
} }
func deferResponse(flags discord.MessageFlags) *api.InteractionResponse { func errorResponse(err error) *api.InteractionResponseData {
return &api.InteractionResponse{ return &api.InteractionResponseData{
Type: api.DeferredMessageInteractionWithSource, Content: option.NewNullableString("**Error:** " + err.Error()),
Data: &api.InteractionResponseData{ Flags: discord.EphemeralMessage,
Flags: flags, AllowedMentions: &api.AllowedMentions{ /* none */ },
},
} }
} }

View File

@ -0,0 +1,34 @@
package main
import (
"context"
"log"
"os"
"github.com/diamondburned/arikawa/v3/api"
"github.com/diamondburned/arikawa/v3/api/cmdroute"
"github.com/diamondburned/arikawa/v3/gateway"
"github.com/diamondburned/arikawa/v3/state"
"github.com/diamondburned/arikawa/v3/utils/json/option"
)
var commands = []api.CreateCommandData{{Name: "ping", Description: "Ping!"}}
func main() {
r := cmdroute.NewRouter()
r.AddFunc("ping", func(ctx context.Context, data cmdroute.CommandData) *api.InteractionResponseData {
return &api.InteractionResponseData{Content: option.NewNullableString("Pong!")}
})
s := state.New("Bot " + os.Getenv("BOT_TOKEN"))
s.AddInteractionHandler(r)
s.AddIntents(gateway.IntentGuilds)
if err := cmdroute.OverwriteCommands(s, commands); err != nil {
log.Fatalln("cannot update commands:", err)
}
if err := s.Connect(context.TODO()); err != nil {
log.Println("cannot connect:", err)
}
}

View File

@ -2,18 +2,18 @@ package main
import ( import (
"context" "context"
"fmt"
"log" "log"
"math/rand" "math/rand"
"os" "os"
"os/signal"
"time" "time"
"github.com/diamondburned/arikawa/v3/api" "github.com/diamondburned/arikawa/v3/api"
"github.com/diamondburned/arikawa/v3/api/cmdroute"
"github.com/diamondburned/arikawa/v3/discord" "github.com/diamondburned/arikawa/v3/discord"
"github.com/diamondburned/arikawa/v3/gateway" "github.com/diamondburned/arikawa/v3/gateway"
"github.com/diamondburned/arikawa/v3/state" "github.com/diamondburned/arikawa/v3/state"
"github.com/diamondburned/arikawa/v3/utils/json/option" "github.com/diamondburned/arikawa/v3/utils/json/option"
"github.com/pkg/errors"
) )
// To run, do `BOT_TOKEN="TOKEN HERE" go run .` // To run, do `BOT_TOKEN="TOKEN HERE" go run .`
@ -46,9 +46,8 @@ func main() {
log.Fatalln("No $BOT_TOKEN given.") log.Fatalln("No $BOT_TOKEN given.")
} }
var h handler h := newHandler(state.New("Bot " + token))
h.s = state.New("Bot " + token) h.s.AddInteractionHandler(h)
h.s.AddInteractionHandler(&h)
h.s.AddIntents(gateway.IntentGuilds) h.s.AddIntents(gateway.IntentGuilds)
h.s.AddHandler(func(*gateway.ReadyEvent) { h.s.AddHandler(func(*gateway.ReadyEvent) {
me, _ := h.s.Me() me, _ := h.s.Me()
@ -59,53 +58,43 @@ func main() {
log.Fatalln("cannot update commands:", err) log.Fatalln("cannot update commands:", err)
} }
if err := h.s.Connect(context.Background()); err != nil { ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
if err := h.s.Connect(ctx); err != nil {
log.Fatalln("cannot connect:", err) log.Fatalln("cannot connect:", err)
} }
} }
func overwriteCommands(s *state.State) error { func overwriteCommands(s *state.State) error {
app, err := s.CurrentApplication() return cmdroute.OverwriteCommands(s, commands)
if err != nil {
return errors.Wrap(err, "cannot get current app ID")
}
_, err = s.BulkOverwriteCommands(app.ID, commands)
return err
} }
type handler struct { type handler struct {
*cmdroute.Router
s *state.State s *state.State
} }
func (h *handler) HandleInteraction(ev *discord.InteractionEvent) *api.InteractionResponse { func newHandler(s *state.State) *handler {
switch data := ev.Data.(type) { h := &handler{s: s}
case *discord.CommandInteraction:
switch data.Name { h.Router = cmdroute.NewRouter()
case "ping": // Automatically defer handles if they're slow.
return h.cmdPing(ev, data) h.Use(cmdroute.Deferrable(s, cmdroute.DeferOpts{}))
case "echo": h.AddFunc("ping", h.cmdPing)
return h.cmdEcho(ev, data) h.AddFunc("echo", h.cmdEcho)
case "thonk": h.AddFunc("thonk", h.cmdThonk)
return h.cmdThonk(ev, data)
default: return h
return errorResponse(fmt.Errorf("unknown command %q", data.Name)) }
}
default: func (h *handler) cmdPing(ctx context.Context, cmd cmdroute.CommandData) *api.InteractionResponseData {
return errorResponse(fmt.Errorf("unknown interaction %T", ev.Data)) return &api.InteractionResponseData{
Content: option.NewNullableString("Pong!"),
} }
} }
func (h *handler) cmdPing(ev *discord.InteractionEvent, _ *discord.CommandInteraction) *api.InteractionResponse { func (h *handler) cmdEcho(ctx context.Context, data cmdroute.CommandData) *api.InteractionResponseData {
return &api.InteractionResponse{
Type: api.MessageInteractionWithSource,
Data: &api.InteractionResponseData{
Content: option.NewNullableString("Pong!"),
},
}
}
func (h *handler) cmdEcho(ev *discord.InteractionEvent, data *discord.CommandInteraction) *api.InteractionResponse {
var options struct { var options struct {
Arg string `discord:"argument"` Arg string `discord:"argument"`
} }
@ -114,43 +103,23 @@ func (h *handler) cmdEcho(ev *discord.InteractionEvent, data *discord.CommandInt
return errorResponse(err) return errorResponse(err)
} }
return &api.InteractionResponse{ return &api.InteractionResponseData{
Type: api.MessageInteractionWithSource, Content: option.NewNullableString(options.Arg),
Data: &api.InteractionResponseData{ AllowedMentions: &api.AllowedMentions{}, // don't mention anyone
Content: option.NewNullableString(options.Arg),
AllowedMentions: &api.AllowedMentions{},
},
} }
} }
func (h *handler) cmdThonk(ev *discord.InteractionEvent, data *discord.CommandInteraction) *api.InteractionResponse { func (h *handler) cmdThonk(ctx context.Context, data cmdroute.CommandData) *api.InteractionResponseData {
go func() { time.Sleep(time.Duration(3+rand.Intn(5)) * time.Second)
time.Sleep(time.Duration(3+rand.Intn(5)) * time.Second) return &api.InteractionResponseData{
Content: option.NewNullableString("https://tenor.com/view/thonk-thinking-sun-thonk-sun-thinking-sun-gif-14999983"),
h.s.FollowUpInteraction(ev.AppID, ev.Token, api.InteractionResponseData{
Content: option.NewNullableString("https://tenor.com/view/thonk-thinking-sun-thonk-sun-thinking-sun-gif-14999983"),
})
}()
return deferResponse(0)
}
func errorResponse(err error) *api.InteractionResponse {
return &api.InteractionResponse{
Type: api.MessageInteractionWithSource,
Data: &api.InteractionResponseData{
Content: option.NewNullableString("**Error:** " + err.Error()),
Flags: discord.EphemeralMessage,
AllowedMentions: &api.AllowedMentions{ /* none */ },
},
} }
} }
func deferResponse(flags discord.MessageFlags) *api.InteractionResponse { func errorResponse(err error) *api.InteractionResponseData {
return &api.InteractionResponse{ return &api.InteractionResponseData{
Type: api.DeferredMessageInteractionWithSource, Content: option.NewNullableString("**Error:** " + err.Error()),
Data: &api.InteractionResponseData{ Flags: discord.EphemeralMessage,
Flags: flags, AllowedMentions: &api.AllowedMentions{ /* none */ },
},
} }
} }

View File

@ -93,23 +93,31 @@ import (
"context" "context"
"log" "log"
"os" "os"
"os/signal"
"github.com/diamondburned/arikawa/v3/api"
"github.com/diamondburned/arikawa/v3/api/cmdroute"
"github.com/diamondburned/arikawa/v3/gateway" "github.com/diamondburned/arikawa/v3/gateway"
"github.com/diamondburned/arikawa/v3/state" "github.com/diamondburned/arikawa/v3/state"
"github.com/diamondburned/arikawa/v3/utils/json/option"
) )
var commands = []api.CreateCommandData{{Name: "ping", Description: "Ping!"}}
func main() { func main() {
s := state.New("Bot " + os.Getenv("BOT_TOKEN")) r := cmdroute.NewRouter()
s.AddIntents(gateway.IntentGuilds | gateway.IntentGuildMessages) r.AddFunc("ping", func(ctx context.Context, data cmdroute.CommandData) *api.InteractionResponseData {
s.AddHandler(func(m *gateway.MessageCreateEvent) { return &api.InteractionResponseData{Content: option.NewNullableString("Pong!")}
log.Printf("%s: %s", m.Author.Username, m.Content)
}) })
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) s := state.New("Bot " + os.Getenv("BOT_TOKEN"))
defer cancel() s.AddInteractionHandler(r)
s.AddIntents(gateway.IntentGuilds)
if err := s.Connect(ctx); err != nil { if err := cmdroute.OverwriteCommands(s, commands); err != nil {
log.Fatalln("cannot update commands:", err)
}
if err := s.Connect(context.TODO()); err != nil {
log.Println("cannot connect:", err) log.Println("cannot connect:", err)
} }
} }