cmd/horsebot: allow acting only as api server

This commit is contained in:
2026-06-02 17:26:34 -04:00
parent e3dd37f515
commit 871fd5fdcc

View File

@@ -27,7 +27,6 @@ import (
"zombiezen.com/go/sqlite"
"zombiezen.com/go/sqlite/sqlitex"
"git.sunturtle.xyz/zephyr/horse"
"git.sunturtle.xyz/zephyr/horse/mdb"
)
@@ -67,14 +66,16 @@ func main() {
}
slog.SetDefault(slog.New(lh))
stat, err := os.Stat(public)
if err != nil {
slog.Error("public", slog.Any("err", err))
os.Exit(1)
}
if !stat.IsDir() {
slog.Error("public", slog.String("err", "not a directory"))
os.Exit(1)
if public != "" {
stat, err := os.Stat(public)
if err != nil {
slog.Error("public", slog.Any("err", err))
os.Exit(1)
}
if !stat.IsDir() {
slog.Error("public", slog.String("err", "not a directory"))
os.Exit(1)
}
}
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
@@ -85,43 +86,48 @@ func main() {
os.Exit(1)
}
skills, err := mdb.Skills(ctx, db)
slog.Info("loaded skills", slog.Int("count", len(skills)))
groups, err2 := mdb.SkillGroups(ctx, db)
slog.Info("loaded skill groups", slog.Int("count", len(groups)))
if err = errors.Join(err, err2); err != nil {
slog.Error("loading data", slog.Any("err", err))
os.Exit(1)
}
skillSrv := newSkillServer(skills, groups)
slog.Info("skill server ready")
var client *bot.Client
if tokenFile != "" {
skills, err := mdb.Skills(ctx, db)
slog.Info("loaded skills", slog.Int("count", len(skills)))
groups, err2 := mdb.SkillGroups(ctx, db)
slog.Info("loaded skill groups", slog.Int("count", len(groups)))
if err = errors.Join(err, err2); err != nil {
slog.Error("loading data", slog.Any("err", err))
os.Exit(1)
}
skillSrv := newSkillServer(skills, groups)
slog.Info("skill server ready")
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'})
r := handler.New()
r.DefaultContext(func() context.Context { return ctx })
r.Use(middleware.Go)
r.Use(logMiddleware)
r.Route("/skill", func(r handler.Router) {
r.SlashCommand("/", skillSrv.slash)
r.Autocomplete("/", skillSrv.autocomplete)
r.SelectMenuComponent("/swap", skillSrv.menu)
r.ButtonComponent("/swap/{id}", skillSrv.button)
})
r := handler.New()
r.DefaultContext(func() context.Context { return ctx })
r.Use(middleware.Go)
r.Use(logMiddleware)
r.Route("/skill", func(r handler.Router) {
r.SlashCommand("/", skillSrv.slash)
r.Autocomplete("/", skillSrv.autocomplete)
r.SelectMenuComponent("/swap", skillSrv.menu)
r.ButtonComponent("/swap/{id}", skillSrv.button)
})
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)
slog.Info("connect", slog.String("disgo", disgo.Version))
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'})
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)
}
}
mux := http.NewServeMux()
mux.Handle("GET /", httpmiddle.Compress(5)(http.FileServerFS(os.DirFS(public))))
if public != "" {
mux.Handle("GET /", httpmiddle.Compress(5)(http.FileServerFS(os.DirFS(public))))
}
mux.Handle("GET /api/global/affinity", httpmiddle.Compress(5)(dataroute(ctx, db, mdb.AffinitySummary)))
mux.Handle("GET /api/global/character", httpmiddle.Compress(5)(dataroute(ctx, db, mdb.Characters)))
mux.Handle("GET /api/global/conversation", httpmiddle.Compress(5)(dataroute(ctx, db, mdb.Conversations)))
@@ -162,23 +168,28 @@ func main() {
slog.Error("HTTP server closed", slog.Any("err", err))
}()
if err := handler.SyncCommands(client, commands, nil, rest.WithCtx(ctx)); err != nil {
slog.Error("syncing commands", slog.Any("err", err))
os.Exit(1)
if client != nil {
if err := handler.SyncCommands(client, commands, nil, rest.WithCtx(ctx)); err != nil {
slog.Error("syncing commands", slog.Any("err", err))
os.Exit(1)
}
slog.Info("start gateway")
if err := client.OpenGateway(ctx); err != nil {
slog.Error("starting gateway", slog.Any("err", err))
stop()
}
}
slog.Info("start gateway")
if err := client.OpenGateway(ctx); err != nil {
slog.Error("starting gateway", slog.Any("err", err))
stop()
}
slog.Info("ready")
<-ctx.Done()
stop()
ctx, stop = context.WithTimeout(context.Background(), 5*time.Second)
defer stop()
client.Close(ctx)
if client != nil {
client.Close(ctx)
}
if err := srv.Shutdown(ctx); err != nil {
slog.Error("HTTP API shutdown", slog.Any("err", err))
os.Exit(1)
@@ -204,30 +215,6 @@ var commands = []discord.ApplicationCommandCreate{
},
}
func loadSkills(file string) ([]horse.Skill, error) {
b, err := os.ReadFile(file)
if err != nil {
return nil, err
}
var skills []horse.Skill
if err := json.Unmarshal(b, &skills); err != nil {
return nil, err
}
return skills, nil
}
func loadSkillGroups(file string) ([]horse.SkillGroup, error) {
b, err := os.ReadFile(file)
if err != nil {
return nil, err
}
var groups []horse.SkillGroup
if err := json.Unmarshal(b, &groups); err != nil {
return nil, err
}
return groups, nil
}
func dataroute[T any](ctx context.Context, db *sqlitex.Pool, f func(context.Context, *sqlitex.Pool) ([]T, error)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
data, err := f(ctx, db)