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:
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")
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
Reference in new issue
Block a user