Beta: clean up servers that opted out while the bot was offline

The bot remembers where it registered beta commands (beta_registered) and
removes them from servers that are no longer in the beta at the next start.
Failed cleanups are retried; a server the bot has left is forgotten.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
FernandoJVideiraandClaude Sonnet 5.5 committed 2026-10-23 01:32:20 +01:00
1 parent fcf4f3ccbd
commit 9839bba50e
9 files changed
+393 -30

No files matched your search

@@ -215,3 +215,44 @@ func TestListBetaGuilds(t *testing.T) {
t.Fatal("listed after opting in")
}
}
func TestBetaRegistered(t *testing.T) {
r, _ := testRepo(t)
ctx := context.Background()
if _, err := r.db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS beta_registered (guild_id BIGINT PRIMARY KEY)`); err != nil {
t.Fatal(err)
}
const g = "8800000000000005151"
_ = r.SetBetaRegistered(ctx, g, false)
t.Cleanup(func() { _ = r.SetBetaRegistered(ctx, g, false) })
has := func() bool {
ids, err := r.ListBetaRegistered(ctx)
if err != nil {
t.Fatal(err)
}
for _, id := range ids {
if id == g {
return true
}
}
return false
}
if has() {
t.Fatal("not remembered at first")
}
for i := 0; i < 2; i++ { // twice: harmless
if err := r.SetBetaRegistered(ctx, g, true); err != nil {
t.Fatal(err)
}
}
if !has() {
t.Fatal("remembered after registering")
}
if err := r.SetBetaRegistered(ctx, g, false); err != nil || has() {
t.Fatalf("forgotten after removing: %v", err)
}
if err := r.SetBetaRegistered(ctx, "not-a-number", true); err == nil {
t.Error("a bad ID is refused")
}
}
+34
View File
@@ -441,3 +441,37 @@ func (r *Repo) ListBetaGuilds(ctx context.Context) ([]string, error) {
}
return out, rows.Err()
}
// ListBetaRegistered returns the servers where the bot registered beta
// commands, so it can clean up after itself even if a server opted out while
// it was offline.
func (r *Repo) ListBetaRegistered(ctx context.Context) ([]string, error) {
rows, err := r.db.QueryContext(ctx, `SELECT guild_id FROM beta_registered`)
if err != nil {
return nil, err
}
defer rows.Close()
var out []string
for rows.Next() {
var id int64
if err := rows.Scan(&id); err != nil {
return nil, err
}
out = append(out, strconv.FormatInt(id, 10))
}
return out, rows.Err()
}
// SetBetaRegistered records (or forgets) that beta commands exist in a server.
func (r *Repo) SetBetaRegistered(ctx context.Context, guildID string, registered bool) error {
id, err := strconv.ParseInt(guildID, 10, 64)
if err != nil {
return err
}
if registered {
_, err = r.db.ExecContext(ctx, `INSERT INTO beta_registered (guild_id) VALUES ($1) ON CONFLICT DO NOTHING`, id)
} else {
_, err = r.db.ExecContext(ctx, `DELETE FROM beta_registered WHERE guild_id = $1`, id)
}
return err
}