From f3698f0fc6e934556cecdb08242bbc881327ad87 Mon Sep 17 00:00:00 2001 From: Branden J Brown Date: Thu, 22 Jan 2026 22:24:24 -0500 Subject: [PATCH] better autocomplete --- README.md | 2 +- autocomplete/autocomplete.go | 9 +++++++++ main.go | 22 ++++++++++++---------- skill.go | 14 +++++++++++--- 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 464ca0c..3068a2d 100644 --- a/README.md +++ b/README.md @@ -7,4 +7,4 @@ Production instance is named Zenno Rob Roy, because she has read all about Umamu ## Running The bot always uses the Gateway API. -If the `-http` argument is provided, it will also use the HTTP API, and `-token` must also be provided. +If the `-http` argument is provided, it will also use the HTTP API, and `-key` must also be provided. diff --git a/autocomplete/autocomplete.go b/autocomplete/autocomplete.go index a7f0423..a341934 100644 --- a/autocomplete/autocomplete.go +++ b/autocomplete/autocomplete.go @@ -111,3 +111,12 @@ func inorder(a, b string) bool { } return true } + +// Metrics gets the number of buckets in the autocomplete set and the length +// of the longest bucket. +func (s *Set) Metrics() (buckets, longest int) { + for _, b := range s.keys { + longest = max(longest, len(b)) + } + return len(s.keys), longest +} diff --git a/main.go b/main.go index b38ea22..896f700 100644 --- a/main.go +++ b/main.go @@ -9,7 +9,6 @@ import ( "os" "os/signal" "strconv" - "strings" "time" "git.sunturtle.xyz/zephyr/horse/horse" @@ -110,6 +109,13 @@ func main() { stop() } slog.Info("ready") + go func() { + // Preload autocomplete in a separate goroutine. + slog.Info("begin computing skill autocomplete") + set := skillGlobalAuto() + buckets, longest := set.Metrics() + slog.Info("done computing skill autocomplete", slog.Int("buckets", buckets), slog.Int("longest", longest)) + }() <-ctx.Done() stop() @@ -162,15 +168,11 @@ func skillHandler(data discord.SlashCommandInteractionData, e *handler.CommandEv } 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 - } - } + q := e.Data.String("query") + opts := skillGlobalAuto().Find(nil, q) + r := make([]discord.AutocompleteChoice, min(len(opts), 25)) + for i, k := range opts[:min(len(opts), len(r))] { + r[i] = discord.AutocompleteChoiceString{Name: k, Value: k} } return e.AutocompleteResult(r) } diff --git a/skill.go b/skill.go index 971896f..90723e1 100644 --- a/skill.go +++ b/skill.go @@ -3,8 +3,11 @@ package main import ( "fmt" "strings" + "sync" "git.sunturtle.xyz/zephyr/horse/horse" + "git.sunturtle.xyz/zephyr/horse/horse/global" + "git.sunturtle.xyz/zephyr/horsebot/autocomplete" "github.com/disgoorg/disgo/discord" ) @@ -115,6 +118,11 @@ func isDebuff(s horse.Skill) bool { return false } -// TODO(zeph): autocomplete -// if we want to backgroundify construction of an autocomplete map, -// use sync.OnceValue and launch a goroutine in main to get the value and discard it +var skillGlobalAuto = sync.OnceValue(func() *autocomplete.Set { + var set autocomplete.Set + for _, id := range global.OrderedSkills { + s := global.AllSkills[id] + set.Add(s.Name, s.Name) + } + return &set +})