From 667f87f9d0f6a48a2b0d1f2e1174c482fbcac0ea Mon Sep 17 00:00:00 2001 From: prosolis <5590409+prosolis@users.noreply.github.com> Date: Wed, 3 Jun 2026 17:23:53 -0700 Subject: [PATCH] Fix SQL errors for D&D-only players: seed player_meta on setup confirm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit !setup confirm wrote only dnd_character, never the canonical player_meta seed row. A player who built a character via !setup and played purely in the D&D/expedition system (which writes inventory directly, bypassing ensureCharacter) ended up with no player_meta — so every legacy-layer command's loadAdvCharacter returned 'sql: no rows in result set'. dndSetupConfirm now routes through ensureCharacter (which seeds player_meta + tier-0 equipment on first contact) instead of a bare loadAdvCharacter. Also fix !wotd force: it was deleting from a non-existent job_completed/job_key table (silent no-op that left forced re-posts blocked by the stale dedup row). Now clears the real daily_prefetch rows and checks the error. --- internal/plugin/dnd_setup.go | 8 +++++++- internal/plugin/wotd.go | 13 +++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/internal/plugin/dnd_setup.go b/internal/plugin/dnd_setup.go index 7348cf5..ad79f73 100644 --- a/internal/plugin/dnd_setup.go +++ b/internal/plugin/dnd_setup.go @@ -258,7 +258,13 @@ func (p *AdventurePlugin) dndSetupConfirm(ctx MessageContext) error { // Initial D&D level seeded from existing combat_level (v1.1 §4.1). // combat_level "freezes" thereafter — dnd_level is canonical. - advChar, _ := loadAdvCharacter(ctx.Sender) + // + // ensureCharacter (not a bare loadAdvCharacter) so the canonical + // player_meta seed row + tier-0 equipment exist for D&D-only players + // who reach !setup confirm without ever touching the legacy adventure + // path. Without this seed, loadAdvCharacter returns "sql: no rows in + // result set" on every legacy-layer command (arena, npcs, events, …). + advChar, _, _ := p.ensureCharacter(ctx.Sender) startLevel := 1 if advChar != nil { startLevel = dndLevelFromCombatLevel(advChar.CombatLevel) diff --git a/internal/plugin/wotd.go b/internal/plugin/wotd.go index be50a29..5f05319 100644 --- a/internal/plugin/wotd.go +++ b/internal/plugin/wotd.go @@ -247,8 +247,17 @@ func (p *WOTDPlugin) prefetchWord(force bool) error { if _, delErr := d.Exec(`DELETE FROM wotd_log WHERE date = ?`, today); delErr != nil { slog.Error("wotd: force delete failed", "err", delErr) } - // Also clear job-completed flags so PostWOTD will re-post - d.Exec(`DELETE FROM job_completed WHERE job_name = 'wotd' AND job_key LIKE ?`, today+"%") + // Also clear job-completed flags so PostWOTD will re-post. The + // per-room dedup keys are ":" in daily_prefetch, so + // match every room's flag for today. (Was targeting a non-existent + // job_completed/job_key table — a silent no-op that left forced + // re-posts blocked by the stale dedup row.) + if _, delErr := d.Exec( + `DELETE FROM daily_prefetch WHERE job_name = 'wotd' AND date LIKE ?`, + today+"%", + ); delErr != nil { + slog.Error("wotd: force clear dedup flags failed", "err", delErr) + } } _, err := d.Exec(