horsebot: refactor skill server

This commit is contained in:
2026-03-09 10:05:07 -04:00
parent af4e06411d
commit 63659a4934
2 changed files with 113 additions and 115 deletions

View File

@@ -11,7 +11,6 @@ import (
"os"
"os/signal"
"path/filepath"
"strconv"
"time"
"github.com/disgoorg/disgo"
@@ -58,13 +57,16 @@ func main() {
}
slog.SetDefault(slog.New(lh))
byID, byName, err := loadSkills(filepath.Join(dataDir, "skill.json"))
skills, err := loadSkills(filepath.Join(dataDir, "skill.json"))
slog.Info("loaded skills", slog.Int("count", len(skills)))
groups, err2 := loadSkillGroups(filepath.Join(dataDir, "skill-group.json"))
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)
}
skillsByID, skillsByName, skillGroupMap = byID, byName, groups
skillSrv := newSkillServer(skills, groups)
slog.Info("skill server ready")
token, err := os.ReadFile(tokenFile)
if err != nil {
@@ -80,9 +82,9 @@ func main() {
r.Use(middleware.Go)
r.Use(logMiddleware)
r.Route("/skill", func(r handler.Router) {
r.SlashCommand("/", skillHandler)
r.Autocomplete("/", skillAutocomplete)
r.ButtonComponent("/{id}", skillButton)
r.SlashCommand("/", skillSrv.slash)
r.Autocomplete("/", skillSrv.autocomplete)
r.ButtonComponent("/{id}", skillSrv.button)
})
opts := []bot.ConfigOpt{bot.WithDefaultGateway(), bot.WithEventListeners(r)}
@@ -145,33 +147,19 @@ var commands = []discord.ApplicationCommandCreate{
},
}
// TODO(zeph): these globals could go away, but there's a bit of ceremony to doing that
var (
skillsByID map[horse.SkillID]horse.Skill
skillsByName map[string]horse.SkillID
skillGroupMap map[horse.SkillGroupID]horse.SkillGroup
)
func loadSkills(file string) (map[horse.SkillID]horse.Skill, map[string]horse.SkillID, error) {
func loadSkills(file string) ([]horse.Skill, error) {
b, err := os.ReadFile(file)
if err != nil {
return nil, nil, err
return nil, err
}
var skills []horse.Skill
if err := json.Unmarshal(b, &skills); err != nil {
return nil, nil, err
return nil, err
}
byID := make(map[horse.SkillID]horse.Skill, len(skills))
byName := make(map[string]horse.SkillID, len(skills))
for _, s := range skills {
byID[s.ID] = s
byName[s.Name] = s.ID
}
slog.Info("loaded skills", slog.Int("count", len(skills)))
return byID, byName, nil
return skills, nil
}
func loadSkillGroups(file string) (map[horse.SkillGroupID]horse.SkillGroup, error) {
func loadSkillGroups(file string) ([]horse.SkillGroup, error) {
b, err := os.ReadFile(file)
if err != nil {
return nil, err
@@ -180,59 +168,5 @@ func loadSkillGroups(file string) (map[horse.SkillGroupID]horse.SkillGroup, erro
if err := json.Unmarshal(b, &groups); err != nil {
return nil, err
}
m := make(map[horse.SkillGroupID]horse.SkillGroup, len(groups))
for _, s := range groups {
m[s.ID] = s
}
slog.Info("loaded skill groups", slog.Int("count", len(groups)))
return m, nil
}
func skillHandler(data discord.SlashCommandInteractionData, e *handler.CommandEvent) error {
q := data.String("query")
id, err := strconv.ParseInt(q, 10, 32)
if err == nil {
// note inverted condition; this is when we have an id
id = int64(skillsByID[horse.SkillID(id)].ID)
}
if id == 0 {
// Either we weren't given a number or the number doesn't match any skill ID.
v := skillsByName[q]
if v == 0 {
// No such skill.
m := discord.MessageCreate{
Content: "No such skill.",
Flags: discord.MessageFlagEphemeral,
}
return e.CreateMessage(m)
}
id = int64(v)
}
// TODO(zeph): search conditions and effects, give a list
m := discord.MessageCreate{
Components: []discord.LayoutComponent{RenderSkill(horse.SkillID(id), skillsByID, skillGroupMap)},
Flags: discord.MessageFlagIsComponentsV2,
}
return e.CreateMessage(m)
}
func skillAutocomplete(e *handler.AutocompleteEvent) error {
q := e.Data.String("query")
opts := skillGlobalAuto().Find(nil, q)
return e.AutocompleteResult(opts[:min(len(opts), 25)])
}
func skillButton(data discord.ButtonInteractionData, e *handler.ComponentEvent) error {
id, err := strconv.ParseInt(e.Vars["id"], 10, 32)
if err != nil {
m := discord.MessageCreate{
Content: "That button produced an invalid skill ID. That's not supposed to happen.",
Flags: discord.MessageFlagEphemeral,
}
return e.CreateMessage(m)
}
m := discord.MessageUpdate{
Components: &[]discord.LayoutComponent{RenderSkill(horse.SkillID(id), skillsByID, skillGroupMap)},
}
return e.UpdateMessage(m)
return groups, nil
}