136 lines
3.4 KiB
Go
136 lines
3.4 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"flag"
|
|
"log/slog"
|
|
"os"
|
|
"os/signal"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.sunturtle.xyz/zephyr/horse/horse"
|
|
"git.sunturtle.xyz/zephyr/horse/horse/global"
|
|
"github.com/disgoorg/disgo"
|
|
"github.com/disgoorg/disgo/bot"
|
|
"github.com/disgoorg/disgo/discord"
|
|
"github.com/disgoorg/disgo/handler"
|
|
"github.com/disgoorg/disgo/handler/middleware"
|
|
"github.com/disgoorg/disgo/rest"
|
|
)
|
|
|
|
func main() {
|
|
var (
|
|
appID string
|
|
pubkey string
|
|
tokenFile string
|
|
)
|
|
flag.StringVar(&appID, "id", "", "Discord application ID")
|
|
flag.StringVar(&pubkey, "key", "", "Discord public key")
|
|
flag.StringVar(&tokenFile, "token", "", "`file` containing the Discord bot token")
|
|
flag.Parse()
|
|
|
|
token, err := os.ReadFile(tokenFile)
|
|
if err != nil {
|
|
slog.Error("reading token", slog.Any("err", err))
|
|
os.Exit(1)
|
|
}
|
|
token = bytes.TrimSuffix(token, []byte{'\n'})
|
|
|
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
|
|
|
|
r := handler.New()
|
|
r.DefaultContext(func() context.Context { return ctx })
|
|
r.Use(middleware.Go)
|
|
r.Use(middleware.Logger)
|
|
r.Route("/skill", func(r handler.Router) {
|
|
r.SlashCommand("/", skillHandler)
|
|
r.Autocomplete("/", skillAutocomplete)
|
|
// TODO(zeph): button handler
|
|
})
|
|
|
|
slog.Info("connect", slog.String("disgo", disgo.Version))
|
|
client, err := disgo.New(string(token), bot.WithDefaultGateway(), bot.WithEventListeners(r))
|
|
if err != nil {
|
|
slog.Error("building bot", slog.Any("err", err))
|
|
os.Exit(1)
|
|
}
|
|
|
|
if err := handler.SyncCommands(client, commands, nil, rest.WithCtx(ctx)); err != nil {
|
|
slog.Error("syncing commands", slog.Any("err", err))
|
|
os.Exit(1)
|
|
}
|
|
|
|
if err := client.OpenGateway(ctx); err != nil {
|
|
slog.Error("opening gateway", slog.Any("err", err))
|
|
stop()
|
|
}
|
|
slog.Info("running")
|
|
<-ctx.Done()
|
|
stop()
|
|
|
|
ctx, stop = context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer stop()
|
|
client.Close(ctx)
|
|
}
|
|
|
|
var commands = []discord.ApplicationCommandCreate{
|
|
discord.SlashCommandCreate{
|
|
Name: "skill",
|
|
Description: "Umamusume skill data",
|
|
Options: []discord.ApplicationCommandOption{
|
|
discord.ApplicationCommandOptionString{
|
|
Name: "query",
|
|
Description: "Skill name or ID",
|
|
Required: true,
|
|
Autocomplete: true,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
func skillHandler(data discord.SlashCommandInteractionData, e *handler.CommandEvent) error {
|
|
q := data.String("query")
|
|
id, err := strconv.ParseInt(q, 10, 32)
|
|
var s horse.Skill
|
|
if err == nil {
|
|
// note inverted condition; this is when we have an id
|
|
s = global.AllSkills[horse.SkillID(id)]
|
|
}
|
|
if s.ID == 0 {
|
|
// Either we weren't given a number or the number doesn't match any skill ID.
|
|
id, ok := global.SkillNameToID[q]
|
|
if !ok {
|
|
// No such skill.
|
|
m := discord.MessageCreate{
|
|
Content: "No such skill.",
|
|
Flags: discord.MessageFlagEphemeral,
|
|
}
|
|
return e.CreateMessage(m)
|
|
}
|
|
s = global.AllSkills[id]
|
|
}
|
|
// TODO(zeph): search conditions and effects, give a list
|
|
m := discord.MessageCreate{
|
|
Components: []discord.LayoutComponent{RenderSkill(s)},
|
|
Flags: discord.MessageFlagIsComponentsV2,
|
|
}
|
|
return e.CreateMessage(m)
|
|
}
|
|
|
|
func skillAutocomplete(e *handler.AutocompleteEvent) error {
|
|
q := strings.ToLower(e.Data.String("query"))
|
|
r := make([]discord.AutocompleteChoice, 0, 25)
|
|
for k, _ := range global.SkillNameToID {
|
|
if strings.HasPrefix(strings.ToLower(k), q) {
|
|
r = append(r, discord.AutocompleteChoiceString{Name: k, Value: k})
|
|
if len(r) == cap(r) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return e.AutocompleteResult(r)
|
|
}
|