Commit Graph

95 Commits

Author SHA1 Message Date
prosolis
83a71173b1 Adv 2.0 D&D Phase R6 polish: standalone-zone harvest persistence
Adds a dnd_zone_run.harvest_nodes_json column and a small persistence
layer (loadStandaloneHarvestNodes / saveStandaloneHarvestNodes) so
!zone enter runs that aren't tied to an expedition can carry per-room
HarvestNode state. Expedition runs continue to use
expedition.region_state for the same data — this is the casual flow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolis
c170adaf05 Adv 2.0 D&D Phase R R3-R5: Combat-link, zone loot, fishing, economy
R3 — Combat Event Integration:
- dnd_expedition_combat.go: Combat Interrupt rolls (§4.2) with
  threat-clock and Ranger-wilderness modifiers; Patrol Encounters
  scaled by threat level; recordZoneKill writer with monsterKillTags.
- Interrupt gate at head of handleHarvestCmd; patrol gate before
  resolveRoom in zoneCmdAdvance; kill writer wired into combat-win
  paths.

R4a — Zone Loot Tables:
- dnd_zone_loot.go: §5 loot drop tables for all 10 zones × 5 tiers
  with §8.1 sell-value bands. dropTierFromCR brackets + boss floor.
- Hooks on combat-win in resolveCombatRoom, resolveBossRoom,
  runHarvestInterrupt, tryPatrolEncounter.

R4b — Fishing Integration:
- dnd_zone_fish.go: fishingZones allow-list, fishingSkillBonus,
  rangerRareCatchUpgrade (§6.2), feywildFishDistortion narration.
- §6.1 fish entries added to resource registry for Forest, Sunken
  Temple, Underdark, Feywild zones.
- !fish wired through handleHarvestCmd; zoneItemFlavor matrix
  populated for all 10 zones × all loot items.

R5 — Economy Integration:
- dnd_economy.go: !sell (post-expedition gate, single CHA Persuasion
  DC 17 → +15% bump), !craft (§8.2 4 exemplar recipes), !lore
  (INT/Arcana DC 15 recipe discovery).
- dnd_known_recipe table for persistent recipe discovery.

Flavor reuse: HarvestInterrupt, PatrolEncounter, CombatVictory,
PlayerDeath, LootDrop*, FeywildTimeDistortion* — all existing pools.
No new flavor file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolis
9608ce554a Adv 2.0 D&D Phase 12 E1a: Expedition data model + persistence
Lays the multi-day expedition substrate per gogobee_expedition_system.md
§4.4/§5.3/§8.4/§9. Schema dnd_expedition + dnd_expedition_log added in
db.go. Expedition struct, ExpeditionSupplies, CampState, ThreatEvent
shipped in dnd_expedition.go with start/get/scan/abandon/complete,
updateSupplies, updateCamp, applyThreatDelta (clamped + sticky siege),
advanceExpeditionDay, append/recent log helpers. Round-trip + concurrent
rejection + threat clamp/siege + log ordering covered.

E1b (supply procurement) and E1c (commands) wire on top in subsequent
commits; day cycle (E1d) and camp commands (E1e) follow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolis
ee3b2977aa Adv 2.0 D&D Phase 11 D1b: DungeonRun state machine + dnd_zone_run schema
dnd_zone_run table (run_id, zone_id, room_seq_json, rooms_cleared,
boss_defeated, abandoned, loot_collected, gm_mood, started_at,
last_action_at, completed_at) added to db.go schema with active-run
index.

dnd_zone_run.go ships RoomType (entry|exploration|trap|elite|boss),
DungeonRun struct, generateRoomSequence (Entry → ExplorationxN1 → Trap
→ ExplorationxN2 → Elite → Boss within zone's [MinRooms, MaxRooms]),
and persistence helpers: startZoneRun (gates on level tier + active-run
exclusivity), getActiveZoneRun, getZoneRun, markRoomCleared
(advances and auto-completes on boss kill), abandonZoneRun,
adjustGMMood (clamped to [0,100]), addLoot.

8 new tests covering room-sequence shape (entry first, boss last,
exactly one trap/elite, ≥2 explorations), happy-path start,
concurrent-run rejection, unknown-zone rejection, full advance-to-boss
flow with completion, abandon idempotency, GM mood clamping at both
bounds, and loot accumulation order. Full repo test suite green.

Boss-room behavior, !zone command surface, and TwinBee narration
arrive in D1c/D1d.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolis
437460c9b2 Adv 2.0 D&D Phase 10 SUB2a-i: Champion + Berserker + Thief L5/L7
First mechanical slice of the subclass system. Each subclass gets its L5
and L7 abilities wired through the existing combat engine + skill check
substrate; no new resource pools introduced.

Champion (L5/L7):
- Improved Critical: new Mods.CritThreshold lowers crit floor to nat 19+
  in resolvePlayerAttack. Default 20 preserved for non-Champions.
- Remarkable Athlete: +½ proficiency bonus to STR/DEX/CON skill checks
  via subclassSkillBonus, layered on raceSkillBonus.

Berserker (L5/L7):
- Rage: new !arm-able active ability gated to Berserker subclass via
  DnDAbility.Subclass field. Reuses the Fighter stamina pool (3/long-rest
  matches 5e's rage uses). On fire: +2 flat damage per hit, halve incoming
  weapon damage, +50% damage approximation for Frenzy's bonus-attack-per-
  turn (one-shot combat can't model that literally).
- Frenzy exhaustion: new exhaustion column on dnd_character; incremented
  by persistDnDPostCombatSubclass after any rage'd combat. Decremented by
  one per long rest.
- Mindless Rage: documented; charm/frighten conditions don't exist in the
  combat engine yet, so no functional effect until P11 zone bosses land.

Thief (L5/L7):
- Sleight of Hand added to dndSkillTable.
- Fast Hands: +5 to Sleight of Hand checks via subclassSkillBonus.
- Supreme Sneak: advantage on Stealth (best of two d20s) via new
  subclassSkillAdvantage hook in performSkillCheck. 5e's half-speed gate
  dropped — we don't track movement speed.

Plumbing:
- applySubclassPassives layered after applyClassPassives in both arena
  and dungeon combat bridges.
- DnDAbility.Subclass enables per-subclass gating; characterActiveAbilities
  filters !arm and !abilities lists by both class and subclass.
- Existing !respec full-wipe also clears Exhaustion.

Tests: Champion CritThreshold gating across L4/L5/no-subclass; rage Apply
flag-set; rage probabilistic win-rate lift vs tougher enemy; exhaustion
increment only on raged combats; long-rest decrement; Champion bonus on
STR/DEX/CON only and only at L7+; Thief Fast Hands SoH-only; Thief
Supreme Sneak L7+ Stealth-only; statistical advantage roll lifts Thief
stealth average by ≥2 vs plain Rogue; !arm rage subclass gating; schema
roundtrip; characterActiveAbilities visibility.
2026-05-09 14:25:21 -07:00
prosolis
1ee4276bcd Adv 2.0 D&D Phase 10 SUB1: subclass selection system
Implements the identity layer of gogobee_subclass_system.md: 15 subclasses
(3 per class) selectable at L5 via !subclass.

- Schema: new subclass + last_subclass_respec_at columns on dnd_character.
- Registry (dnd_subclass.go): 15 entries with class, display, tagline, and
  TwinBee flavor line; helpers for prompt rendering and input resolution
  (number, id, or display-name; case/space/hyphen-insensitive).
- !subclass command: bare form prints prompt or current; !subclass <name>
  selects (free first time) or changes (500 euros + 30-day cooldown via
  separate timestamp from the full !respec wipe). Save-then-debit ordering
  mirrors !respec audit fix B.
- Level-up DM at L5+ now embeds the real selection prompt instead of the
  Phase 9 placeholder; suppressed once a subclass is set.
- Sheet shows the subclass below the class line, or nudges !subclass when
  unchosen at L5+.
- Existing !respec full-wipe also clears subclass + cooldown timestamp.

Mechanical effects of L5/7/10/15 subclass abilities deferred to SUB2/SUB3.
2026-05-09 14:25:21 -07:00
prosolis
9e1a1f606c Adv 2.0 D&D layer: Phases 1-8 + Phase 9 SP1+SP2
Combines all uncommitted D&D work into one checkpoint:

Phases 1-8 (per gogobee_dnd_session_summary.md):
- Race/class/stats system, !setup flow, !sheet, !respec
- d20-vs-AC combat with race/class passives, auto-migration
- XP/level-up, skill checks, NPC refund hooks
- Equipment slot mapping, rarity, !rest short/long
- Pre-arm active abilities, resource pools
- Bestiary, !roll/!stats/!level, onboarding DM
- Audit fixes A-I, flavor wiring under internal/flavor/
- Phase 8 weapon dice + armor AC tables (gogobee_equipment_appendix)

Phase 9 SP1 — spell system foundations:
- 79 SpellDefinitions (76 in-scope + 3 reaction-deferred)
- dnd_known_spells, dnd_spell_slots tables
- pending_cast / concentration_* columns on dnd_character
- Slot tables for Mage/Cleric/Ranger, DC + attack-bonus math
- Long-rest refreshes slots; respec wipes spell state
- Auto-grant default known list on !setup confirm and auto-migration

Phase 9 SP2 — !cast / !spells / !prepare commands:
- Out-of-combat HEAL and UTILITY resolve immediately
- Damage/control/buff queue as pending_cast for next combat
- Audit-style save-then-debit, slot refund on save failure
- !cast --drop, concentration supersession, prep-cap enforcement
- Reaction spells refused with Phase 11 note

SP3 (combat-time resolution of pending_cast) and SP4 (full prep/learn
tests) are next. Build clean, full test suite green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:21 -07:00
prosolis
8e0fe0230c Death source tracking + combat threat/jitter fixes
- Adventure characters now record DeathSource ("adventure"|"arena") and
  DeathLocation when killed. Threaded through Kill() and transitionDeath.
  Daily report uses the recorded death location instead of misattributing
  arena deaths to the day's adventure log: when DeathSource != "adventure",
  the adventure block shows the alive icon and a separate "Later fell in
  {location}." line. Standout-loss flavor uses DeathLocation. Empty
  DeathSource (legacy rows) falls back to current behavior.
- calcDamage applies a ±15% per-hit jitter, so successive hits in a phase
  no longer produce identical numbers. Previously every hit in a phase was
  flat (e.g. 17, 17, 17, 17) and mirror-symmetric between player/enemy,
  reading as scripted.
- assessThreat rewritten to use the engine's actual penetration formula:
  damage-per-round each direction, then rounds-to-kill ratio. The old
  additive HP+Attack*3 model ignored Defense, so even fights could be
  rated trivial — players died after consumables were skipped with the
  "threat assessed as manageable" message.
- SelectConsumables never skips on trivial when arenaRound > 0. Arena
  losses cost real money and equipment, so the cost of a wrong assessment
  is too high to gamble on; adventure-side fights still skip trivial
  threats to avoid wasting items on chump enemies.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:21 -07:00
prosolis
8e3b8377c0 Adventure crafting: grant foraging XP per attempt, track lifetime crafts
Crafting was a downstream consumer of Foraging skill but never gave any
XP back, so the feedback loop was one-directional — only gathering
contributed to the level that gates better recipes.

Now: each auto-craft attempt grants Foraging XP (success > failure).
Per-tier values:
  tier 1:  +12 / +3
  tier 2:  +25 / +5
  tier 3:  +40 / +8
  tier 4:  +60 / +12
  tier 5:  +90 / +18

Successful T1 ≈ 30% of a Foraging Success haul; T5 ≈ 40%. Failures get a
small consolation grant — the attempt still produced practical knowledge
(and lost ingredients).

Schema: adventure_characters.crafts_succeeded INT for lifetime tracking.
Migration entry included.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 23:19:06 -07:00
prosolis
07ca5288c3 Coop: stack same-type gifts on the same day into a single vote post
Multiple baskets (or multiple mimics) sent during the same day now share
one game-room post, one vote, one resolution. First-in becomes the stack
"lead"; subsequent same-type sends become followers that inherit the
lead's deadline and votes.

Behavior:
- Send a basket → new lead, new post, 6h timer starts
- Send another basket within the window → silently joins the stack,
  edits lead's post to bump count
- At stack size 2, TwinBee adds a "this gift looks REALLY special" line
  (one of 5 variants picked deterministically by lead id). No further
  escalation as the stack grows
- One !coop giftvote on the lead's id covers the whole stack
- Resolution applies the same outcome to every gift in the stack;
  modifier is N × ±6
- End-of-run gift log groups by stack with all senders attributed

Schema: coop_dungeon_gifts.stack_lead_id INTEGER (NULL for lead/standalone,
otherwise points at lead's id). Migration entry included.

Why first-in deadline (vs extending on each follower): exploitability.
Saboteurs could spam-extend a stack to delay resolution. Locked deadline
keeps senders honest about timing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 22:28:23 -07:00
prosolis
9f094549b7 Coop: per-gift voting timers (6h) — gifts resolve throughout the day
Replace the single daily-tick gift resolution with independent per-gift
expiries. Each gift now has its own 6h voting window; once that elapses,
the votes are tallied, the sender gets DM feedback, and the live game-room
post is edited to reveal the type and resolved modifier. The modifier
sits on the run waiting for the next floor resolution to merge it in.

Effect:
- Gifts fire continuously throughout the day rather than all at once at
  08:00 UTC, surfacing sender activity in real time.
- Senders get fast feedback (~6h instead of waiting for the next daily
  tick).
- Floor resolution becomes purely "sum pending modifiers from already-
  resolved gifts" — cleaner separation of concerns.
- Atomic per-gift apply via markCoopGiftApplied (UPDATE...WHERE applied_at
  IS NULL) prevents double-application on retry.

Schema additions:
- coop_dungeon_gifts.expires_at (when voting closes)
- coop_dungeon_gifts.applied_at (when modifier merged into a floor)
- Migration entries provided.

Defensive backstop: floor resolution still force-resolves any gifts whose
expiry was missed (e.g., bot down during the timer window).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 17:55:14 -07:00
prosolis
f3d1e65bf1 Pin co-op invite posts; unpin on lock/cancel
Adds Base.PinEvent / Base.UnpinEvent helpers (m.room.pinned_events state)
that read-modify-write the existing pin list — idempotent on both sides.
The bot needs power level for state events; failures are logged but not
fatal.

Schema: add coop_dungeon_runs.invite_post_id to remember which event to
unpin. Migration entry included.

Wired:
- !coop start: pin the invite post in the games room
- !coop cancel: unpin before posting cancellation
- coopProcessLocks (auto-lock or auto-cancel): unpin before lock/expiry post

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 10:24:21 -07:00
prosolis
e8a3b8b35d Co-op audit fixes: lock scheduler, idempotent payouts, deterministic ties
Audit pass addressed concurrency races, crash-recovery double-pay risks, and
non-determinism in tie tallying.

CRITICAL — concurrency
- coopResolveFloor now acquires advUserLock for every party member (sorted by
  UserID) before mutating state, so concurrent !coop fund / vote / giftvote
  commands serialize against the scheduler.

CRITICAL — crash idempotency
- Add coop_dungeon_runs.last_resolved_day and coop_dungeon_members.member_payout
  columns (with migration entries) for crash-resume tracking.
- Skip resolution only if last_resolved_day >= day AND status is terminal;
  otherwise re-enter and finish via idempotent operations.
- On resume detection (event already has outcome), reuse the saved roll
  instead of re-rolling — prevents different outcomes on retry.
- claimCoopMemberPayout / claimCoopBetPayout: atomic UPDATE...WHERE col IS NULL
  pattern. Each member/bet can only be paid once; retries are silent no-ops.
- createCoopEvent now INSERT OR IGNORE — idempotent on retry.
- last_resolved_day is set as the FINAL write per floor.

HIGH — bugs
- Lazy-create floor event in resolver if missing — covers crash window
  between lockCoopRun and the original createCoopEvent.
- coopTallyVote sorts tied winners alphabetically before fallback (was
  non-deterministic via Go map iteration).
- Removed dead day-1 wipe combat-action refund block — refund check was
  always false because midnight reset clears state before resolution.

LOW
- Log slog.Error on malformed JSON in parseCoopVotes / parseCoopFundingMap
  instead of silently dropping data.

Tests
- Tally test extended to assert determinism across 50 runs (catches map-iteration
  leaks).
- Added coverage for new tie-with-leader-vote-outside-winners case.
- Idempotency-guard sanity check on LastResolvedDay vs CurrentDay.

Race detector: clean. Full suite: green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 09:01:29 -07:00
prosolis
8ad31a0009 Add co-op dungeon system (party runs, voting, betting, gifts, items)
Multi-day party runs with funding decisions, TwinBee-narrated floor events,
spectator parimutuel betting, basket/mimic gift system, and weighted-roll
item distribution including masterwork drops at T4-T5.

Schema (5 tables, 2 fewer than spec by computing helpfulness on-the-fly and
skipping the loot-pending state):
- coop_dungeon_runs / _members (daily funding as JSON column)
- coop_dungeon_events (votes as JSON, used to derive TwinBee helpfulness)
- coop_dungeon_bets / _gifts

Mechanics:
- 2-4 player parties, 24h invite window, locks consume one combat action
- Per-floor success roll: base + sum(funding) + level/pet bonuses + event
  vote modifier + active gift modifiers, clamped to 5..95
- Funding tiers (none/min/std/agg/all_in) with liability cap at +8% for
  under-leveled players
- TwinBee narrates events from existing flavor pool; 20 authored events with
  per-option modifiers and embedded recommendation
- Parimutuel betting with 10% rake; odds line shown on lock/daily/status posts
- Gift modifiers symmetric at 50/50 sender mix so no dominant strategy
- Item drops on success via weight-by-contribution roll; T4 25% / T5 100%
  masterwork chance (random pick from existing T4/T5 defs)

Balance via Monte Carlo (coop_dungeon_balance_test.go):
- All tiers exceed 1.5x solo daily income at average party profile
- Optimal funding strategy walks up tiers correctly (Min/Std/Std/Mixed/Agg)
- All-In stays -EV at every tier (boost lever, not optimal play)

Tests: parsing, liability cap, JSON roundtrips, vote tally with leader
tiebreak, event meta consistency, parimutuel payouts, gift EV symmetry,
weighted-roll distribution (Monte Carlo), masterwork tier gates.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 07:58:13 -07:00
prosolis
148b8d20f2 Add tip audit logging, CFR alignment tests, and multiway scenarios
Three confidence features for the poker tip system:
- Tip audit table (holdem_tip_audit) logs every tip with full context
  for bulk review after real sessions
- CFR alignment test validates all 32 scenarios against the trained
  5M-iteration policy — rules engine never contradicts the bot's AI
- 8 new multiway test scenarios covering all streets and key decisions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 14:11:59 -07:00
prosolis
b15c13cde7 Add version system, tax tracking, lottery winner DMs, fmtEuro, audit fixes
- Version system: per-plugin versions via Versioned interface, crash_log and
  version_history DB tables, !version command, crash stats in !botinfo
- Tax tracking: tax_ledger table, trackTaxPaid across all communityTax and
  communityPotAdd sites, tax paid display in !stats and !superstatsexplusalpha
- Lottery: DM winners with winning ticket details and matched numbers bolded
- fmtEuro: generic currency formatter with thousand separators across all
  economy display paths
- Audit fixes: streak_decayed column for Streak Survivor achievement,
  combat_narrative empty-phase guard, crafting success rate integer division,
  RecordCrash nil-DB guard

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 01:07:25 -07:00
prosolis
76110f61ca Add housing/pets/Thom Krooke, wordle overhaul, boost system, backup strategy
Part 3 (Housing, Thom Krooke & Pets):
- Housing system with tiered upgrades, mortgage loans, FRED API rates
- Thom Krooke realtor NPC with !thom commands and !thom pay extra principal
- Pet system with arrival, naming, leveling, combat, morning defense
- Pet ditch recovery scales with level, name validation

Wordle overhaul:
- Remove daily expiration — puzzles persist until solved/failed
- Auto-start new puzzle after solve, fail, or skip
- Sequential puzzle IDs instead of date-based
- Auto-create puzzle on first guess if none active

Adventure fixes:
- Double XP/money boost toggle (!adv boost, admin only)
- Fix ensureCharacter wiping existing data on query errors
- Fix rival RPS format string bug
- Fix daily summary empty data (load today+yesterday logs)
- Fix holdem DM-to-room reply routing
- Fix Robbie payout to 25% of item value
- Add fishing to leaderboard and TwinBee calculations
- Add Thom to morning menu
- Persist mortgage rate across restarts via db.CacheGet/Set

Infrastructure:
- Daily database backup via VACUUM INTO with 7-day retention
- Admin DM notification on backup failure (async)
- Daily NPC house balance reset for holdem

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 16:50:49 -07:00
prosolis
2ef7a29f02 Add Misty & Arina NPCs, fix tier gating, babysit actions, Robbie filter, streak/scheduler bugs
Misty & Arina: random encounter NPCs triggered by chat message counting
(5-10 msg threshold, 7-day cooldown, 7.5% roll). Misty asks for €100 —
accept gives 7-day arena buff (20% gourmet food heals gear + 5 enemy dmg),
decline gives 7-day debuff (20% crowd revenge damage). Arina asks for €5000 —
accept gives 7-day sniper buff (8% instant kill in arena). Effects integrate
into arena round resolution after combat roll; sniper overrides death.

Other fixes:
- Tier gating now uses base skill only — buffs improve success, not access
- Babysitter uses all harvest actions (3/day, 4 on holidays) instead of 1
- Robbie collects all non-equipped items, not just slotted gear
- Robbie timing: fire on first tick >= target hour, not exact minute match
- Streak: dead players who acted still get streak credit
- Streak: dead players skip shame DM (use LastDeathDate, not Alive flag)
- Midnight ticker: in-memory date cache prevents 1439 wasted DB checks/day
- T1 loot values ~3x across all activities to close T1/T2 gap

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 22:41:21 -07:00
prosolis
e459b6e78d Full codebase audit: 21 security/robustness fixes, 328 tests across 4 packages
Security & economy:
- Credit()/Debit() reject non-positive amounts (closes infinite-money exploit)
- Lottery ticket purchase uses in-transaction count check (closes TOCTOU race)
- Arena bail channel ownership prevents double-close panic
- Entry.ID validated before exec.Command in fetch-esteemed
- Internal errors no longer leaked to users (forex, esteemed)

Robustness:
- safeGo() panic recovery added to 17 goroutine launch sites across 14 plugins
- db.Close() added and called on shutdown
- Miniflux mutex pattern fixed (snapshot-under-lock)
- Silently ignored DB/JSON errors now logged (lottery_db)

Performance:
- Added indexes: idx_arena_runs_user(user_id, status), idx_euro_bal_user(user_id)

UX:
- Empty !buy args shows usage instead of "No item matching ''"
- "Failed to load character" now suggests !adventure

Test coverage:
- internal/util/parser_test.go (19 tests: XP, levels, progress bar, commands, parsing)
- internal/crypto/crypto_test.go (12 tests: encrypt/decrypt, HMAC, ParseKey)
- internal/plugin/helpers_test.go (30 tests: formatNumber, calc, lottery, chat perks)
- internal/plugin/audit_fixes_test.go (25 tests: safeGo, bail channels, error leaks, overflow)
- internal/db/db_test.go (4 tests: Init, Close, schema indexes)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 17:04:40 -07:00
prosolis
68b2f8b7a5 Add chat level perks: XP bonus, shop flavor, rare drop nudge, death pardon (Adventure 2.5 steps 3-6)
- XP bonus: +5% per 10 chat levels (cap +25% at 50+), applies to all
  adventure and arena XP
- Shop flavor: shopkeeper greeting changes at chat levels 10/30/50,
  replacing random flavor for recognized players
- Rare drop nudge: +0.5% per 10 chat levels (cap +2.5%) applied
  additively to treasure and masterwork drop rates
- Death pardon: chat level 20+ gets 33% chance to survive death once
  per 7 days (converts to bad-failure, quiet DM). Does not apply in
  arena. Fires before Sovereign Death's Reprieve check.
- New adventure_chat_perks.go with perk calculation helpers
- LastPardonUsed field + migration on adventure_characters

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 15:50:28 -07:00
prosolis
7e4fbe5ec8 Add action economy split and cross-plugin chat level lookup (Adventure 2.5 steps 1-2)
Action economy: replace single daily action with 1 combat + 3 harvest
actions per day. Holidays grant +1 to each bucket. Rest consumes all
remaining actions. Arena remains outside both buckets.

- Add CombatActionsUsed/HarvestActionsUsed counters to AdventureCharacter
- Add CanDoCombat/CanDoHarvest/AllActionsUsed/HasActedToday helpers
- Update all 14 ActionTakenToday check sites across adventure, scheduler,
  babysit, twinbee, events, and render
- Morning DM shows action budget and grays out exhausted categories
- Activity resolution checks per-bucket availability before proceeding
- Midnight reset zeros both counters
- Post-action nudge shows remaining actions instead of holiday prompt

Cross-plugin lookup: export XPPlugin.GetLevel(), wire into AdventurePlugin
via p.chatLevel(userID) for upcoming chat level perks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 15:24:49 -07:00
prosolis
f57dcd93a5 Fix streak grace period, UNO earnings, hospital UX, wordle/lottery atomicity
- Streak grace now checks LastDeathDate instead of LastActionDate (was
  suppressing streak updates for all active players)
- UNO single-player earnings use net payout (minus wager) not gross
- Hospital sends error message on save failure instead of silent no-op
- Wordle total_earned update moved into stats transaction
- Lottery ticket inserts + community pot add wrapped in single transaction
  with euro refund on failure
- Lottery fixed-tier ticket prize updated with actual prorated amount
- Added last_death_date column to adventure_characters

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 00:54:58 -07:00
prosolis
26d2846481 Add !adv alias, hospital insurance billing, wordle/UNO earnings tracking, streak death protection
- Add !adv shorthand for !adventure commands and DM replies
- Hospital bill now shows insurance deduction and amount due
- Refactor hospital nudge from per-goroutine sleep to shared ticker
- Dead players' streaks are frozen; grace period on revival day
- Track wordle earnings in wordle_stats, show in !stats and superstats
- Show UNO net earnings and trivia accuracy % in superstats
- Disable sentiment/profanity emoji reactions (classification still logs)
- Add URL_PREVIEW_IGNORE_USERS env var to skip bot users
- Add DISABLE_WOTD_POST env var, remove unused wotd.Prefetch()
- README updates for all of the above

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 00:24:51 -07:00
prosolis
05cf6657f5 Add hospital revival, Robbie bandit, MW/shop fixes, float64 scoring
- St. Guildmore's Memorial Hospital: !hospital command with Nurse Joy,
  procedural itemized billing, same-day revival, 6-hour dead timer,
  hospital ad after death, 2-hour nudge follow-up
- Robbie the Friendly Bandit: automated inventory cleaner, random daily
  visits (8-21 UTC, 40% chance), collects sub-tier gear at €50/item,
  donates to community pot, drops Get Out of Medical Debt Free card
- Fix MW auto-equip: T1 MW no longer replaces better equipped gear
- Fix shop MW block: allow purchasing upgrades over lower-tier MW
- Float64 equipment scoring: advEffectiveTier and advEquipmentScore
  return float64, no mid-calculation truncation (MW 1.25x, Arena 1.5x)
- Fix markdown rendering: convert *asterisk* to _underscore_ italics
  across all flavor text files (shop, blacksmith, rival, hospital)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 17:04:03 -07:00
prosolis
6c6d74fb1b Add blacksmith repair system, community lottery, and audit fixes
Blacksmith: equipment repair with tier-based pricing, DM confirmation flow,
masterwork/arena surcharges, full flavor text pools. Added to main adventure
menu as option 6 (rest bumped to 7).

Lottery: weekly draw (Friday 23:59 UTC), ticket purchases with 100/week cap,
Fisher-Yates number generation, fixed+jackpot prize tiers, community pot
funding, Thursday reminders, draw history.

Audit fixes: TOCTOU in blacksmith repair costs (recompute from fresh equipment),
user lock in DM slot selection, partial repair refund tracking, error logging
on save failures, Fisher-Yates bias correction, communityPotDebit return value
checks in draw execution.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 09:37:21 -07:00
prosolis
ad6e652755 Add rival duels, babysitting service, multilingual !define, economy tuning
Rival System:
- RPS-based duels between combat level 5+ players (best of 3, ties re-prompt)
- Stakes scale with level, split 50/50 between winner and community pot
- 3-4 day randomized challenge interval, 7-day same-pair cooldown
- 24h response window with auto-forfeit
- Full flavor text pools, character sheet integration, !adventure rivals

Babysitting Service:
- Weekly/monthly auto-play service targeting weakest skill
- Babysitter rerolls death, claims items, credits gold and XP
- Rivals automatically declined during service
- End-of-service summary with diaper report

Revive fixes:
- On-demand revive in handleMenu, parseAndResolveChoice, handleStatus
- Morning DM respawn check now runs before babysit interception
- Players no longer stuck dead after DeadUntil expires mid-day

Other changes:
- Multilingual !define searches all DreamDict languages by default
- TwinBee empty haul now shows "jackshit" instead of blank
- Wordle solve payouts increased (e.g. 1-guess: €100 -> €500)
- Per-message euro payouts doubled across all tiers
- Audit fixes: user locks on RPS/babysit handlers, TOCTOU on gold
  transfers, dead code removal, deprecated strings.Title replaced,
  error logging on silent failures

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-03 20:11:51 -07:00
prosolis
a0573e67a5 Persist Wordle guesses, fix hardcoded rendering, announce expired puzzles
- Add wordle_guesses table to persist individual guesses to DB
- Reload guesses during rehydration so they survive bot restarts
- Clear stale guesses when puzzle is replaced (skip+new)
- Fix all hardcoded /6 and 🟩🟩🟩🟩🟩 in rendering to use puzzle.MaxGuesses
  and puzzle.WordLength
- Announce unsolved puzzles at midnight rollover instead of silently replacing
- Mark expired puzzles as failed and update stats on rollover

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 22:11:01 -07:00
prosolis
3d6bc1a066 Add WOTD language rotation, fancy word detection, arena improvements, and death consolidation
- Rotate WOTD between Portuguese, French, and English daily with cognate filtering
- Replace LLM-based fancy word detection with DreamDict frequency data (batch API + cache)
- Track fancy word usage per user and add Wordsmith archetype
- Restore full arena combat flavor text and add player miss actions
- Consolidate death logic into Kill() method with 2-hour lockout
- Fix arena death XP grant missing level-up check
- Fix LLM wotd_used classification by injecting actual WOTD into prompt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-31 18:47:49 -07:00
prosolis
2c6f4e48c9 Add multi-tag personality archetype system
Replace single first-match archetype with comprehensive multi-tag system
spanning 25 archetypes across 8 categories (Communication, Temporal,
Emotional, Economy, Games, Adventure, Social). Archetypes are computed
nightly via cron job querying 15+ tables and cached in user_archetypes.
Thresholds calibrated against real community data. Integrates with
!personality, !superstatsexplusalpha, !whois, and milk carton flavor text.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 17:11:45 -07:00
prosolis
0a1359de46 Add masterwork rework, arena combat log, Death's Reprieve fix, and wordle improvements
Adventure — Masterwork Rework:
- Expand from 3 generic items to 15 tiered masterwork items across
  mining (weapon), fishing (armor), and foraging (boots) with per-tier
  drop rates (5%/4%/3%/2%/1.5%) and location gating
- Skill-specific cross-skill bonuses replace flat masterwork check
- Auto-equip if better, silent discard for duplicates, inventory for rest
- Add !adventure equip command for manual masterwork equipping
- Tiered room announcements: T1-2 DM only, T3 quiet, T4-5 full
- First-drop detection with special message
- Character sheet shows /⚔️ markers for masterwork/arena gear
- Sell protection for special gear in shop

Adventure — Arena Combat Log:
- Turn-based Dragon Quest style narrative for arena fights
- Outcome-first design: roll determines win/loss, log assembled backward
- No-repeat flavor text via actionPicker with per-pool tracking
- 60 participation XP on arena loss

Adventure — Death's Reprieve Fix:
- All equipment set to 1 condition instead of death-level degradation

Wordle:
- Refactored word sourcing and expanded fallback word list
- Simplified Wordnik integration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-29 16:24:59 -07:00
prosolis
0d3485c7c2 Add market plugin, arena, holidays, UNO stacking fix, and multiple UX improvements
- Market plugin (#49): daily index snapshots (Yahoo Finance + Finnhub fallback),
  Ollama-generated summaries, !howsthemarket, !marketstatus, !marketreport commands,
  exchange hours with DST support, 30-min room cooldown
- Adventure arena: 5-tier combat gauntlet with 20 monsters, risk-reward cashout,
  death lockout changed from 24h to midnight UTC across all code and flavor text
- Adventure holidays: double daily actions on holidays
- Adventure DM fix: 15-minute response window prevents bare numbers from triggering
  adventure during UNO games
- Adventure scheduler: jitter between morning DMs to avoid Matrix rate limits
- Adventure revive: wire up adv_revived achievement on admin revive
- Column migration system for existing databases (holiday_action_taken)
- Fix italic markdown rendering after newlines
- Fix holdem DMs broken by space groups including DM rooms
- UNO No Mercy: remove stacking escalation rule (any draw card stacks on any other)
- UNO multiplayer: add turn announcements after stack absorption and turn passes,
  jitter between rapid-fire messages with safe mutex handling
- Birthday: add €1,000 gift and 10 XP + €100 community celebration bonus
- Market: guard against division by zero, nil pointer, and timezone errors
- README updates: plugin count 48→49, all new commands, feature flags, architecture

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 21:26:19 -07:00
prosolis
81e6cecad9 Add markov, emojiboard, achievements, miniflux plugins and adventure catch-up fix
New plugins: encrypted Markov chain generation (9 commands), emojiboard
reaction stats (5 views), Miniflux RSS feed integration (8 commands),
and 48 new achievements across 9 categories. Adventure startup now
unconditionally resets daily actions and respawns expired deaths to
handle missed resets from SQLite contention. README updated with all
new features.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 11:55:47 -07:00
prosolis
9c6ded13fa Add forex plugin, stability fixes, and async HTTP dispatch
- Add forex plugin (Frankfurter v2 API) with rate lookups, analysis,
  DM-based alerts, and daily cron poll. Backfills 1 year of history
  on startup for moving averages and buy signal scoring.

- Fix bot hang caused by SQLite lock contention in reminder polling:
  rows cursor was held open while writing to the same DB. Collect
  results first, close cursor, then process. Same fix in milkcarton.

- Add sync retry loop so the bot reconnects after network drops
  instead of silently exiting. StopSync() for clean Ctrl+C shutdown.

- Add panic recovery to all dispatch, syncer, and cron paths.

- Make all HTTP-calling plugin commands async (goroutines) so a slow
  or dead external API cannot block the message dispatch pipeline.
  Affects: lookup, stocks, forex, anime, movies, concerts, gaming,
  retro, wotd, urls, howami.

- Extract DisplayName to Base, add db.Exec helper, convert silent
  error discards across the codebase.

- Fix UNO mercy-kill bug (eliminated bot continues playing), adventure
  DM nag spam, stats column mismatch, per-call regex/replacer allocs.

- Update README: forex commands, Finance section, 47 plugins.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 09:22:02 -07:00
prosolis
c7c1b76589 Add adventure plugin, holdem CFR fixes, and wordle plugin
Adventure: Complete v1 daily idle RPG with DM-driven gameplay, equipment
shop, treasure system, TwinBee NPC, streak/grudge/party mechanics,
flavor text, and scheduled morning/evening/midnight tickers.

Holdem CFR: Fix three critical training bugs (fold not forfeiting pot,
free calls after raises, training/runtime key mismatch). Add performance
optimizations (preflop lookup table, zero-alloc equity, integer keys,
raise cap, regret pruning). Enrich abstraction with 12 equity buckets,
board texture dimension, and 6-char action history. Replace validation
with proper multi-street simulation.

Also includes wordle plugin, holdem seed tooling, and schema additions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 10:09:57 -07:00
prosolis
e9890fd880 Add bot defeat tracking, audit fixes, and README UNO section
Add !twinbeeboard command (GogoBee's trophy wall) with unified
bot_defeats table tracking losses across Blackjack, UNO solo, and
UNO multiplayer. Fix No Mercy audit bugs: wild card color choice
routing, bot Color Roulette double-advance, missing DM notifications
for roulette victims and 7-0 swap targets, autoplay Color Roulette
effect. Remove dead code. Update README with full UNO documentation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 20:47:46 -07:00
prosolis
e0a201ef97 Add multiplayer UNO, room sentiment tracking, and audit fixes
- Multiplayer UNO: lobby system (2-4 humans + bot), DM-based turns,
  30s auto-play timer, winner-takes-all pot, forfeit handling
- Solo UNO: refactor bot AI to shared standalone functions, support
  starting games from DM, passive UNO call system
- Room sentiment: new room_sentiment_stats table and !roomsentiment
  command showing per-room sentiment breakdown with percentages
- Stop purging llm_classifications (retain indefinitely for analytics)
- Fix multiplayer UNO audit issues: dead code in initMultiGame,
  double game.turns++ on human plays, missing !uno status command
- Update README with UNO commands, config, and architecture

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 10:26:50 -07:00
prosolis
7c9dd28021 Add UNO game, hangman threading, DM room reuse, and gameplay fixes
- Add full UNO game plugin (1v1 vs bot via DM, community pot, bot personality)
- Move hangman into threads with direct reply guessing (no !hangman prefix needed)
- Fix DM room duplication by checking m.direct account data with in-memory cache
- Auto-draw when player has no playable cards instead of requiring manual draw
- Remove forced UNO call phase — players are penalized naturally if they forget
- Fix mutex held during network I/O in hangman start
- Fix infinite loop when both sides have no playable cards and deck is empty
- Fix Wild Draw Four penalty being skipped when UNO prompt triggered
- Fix hangman displayPhrase word boundary clarity (triple-wide gaps for spaces)
- Add UNO env vars, db tables, and README updates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 23:52:38 -07:00
prosolis
cce5160057 Add games system: euro economy, Hangman, Blackjack, coin flip
Euro economy: virtual currency earned passively per message (tiered by
word count, 30s cooldown). Starting balance seeded from corpus character
count. Balance, leaderboard, and transfer commands. Debt system with
configurable limit.

Hangman: collaborative game with phrase pool loaded from file. ASCII
gallows display, tiered scoring (Easy/Medium/Hard/Extreme), early
solution bonus, participant tracking with DM verification. Community
phrase submission with LLM screening.

Blackjack: 1-2 players vs dealer. Standard casino rules (hit soft 17).
Auto-play on timeout. Bet validation against balance and debt limit.
Blackjack pays 1.5x. Separate leaderboard.

Games channel restriction: all game commands (trivia, hangman, blackjack,
flip) restricted to GAMES_ROOM. Dice (!roll) exempt. Trivia also
restricted.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 18:30:17 -07:00
prosolis
996bb18566 Add moderation system with deterministic detection and strike ladder
Deterministic-only detection (no LLM): word list with precompiled
leetspeak variation matching, text/image flood, wall of text, repeated
messages (Levenshtein similarity), mention flooding, link rate limiting
for new members, invite flooding, join/leave cycling detection.

Three-strike response ladder: warn + redact → temp mute → permanent ban.
Strikes expire after configurable period. Admin room notifications with
context cards. DMs over public callouts.

Admin commands: !mod warn/mute/unmute/ban/strikes/forgive/history/reload/
status/test. All require ADMIN_USERS membership.

Feature-flagged: disabled by default, set FEATURE_MODERATION=true to
enable. All thresholds configurable via env vars. New member grace
period with stricter thresholds. Word list hot-reload via !mod reload.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 17:49:35 -07:00
prosolis
0fc15668da Add space groups, fix HLTB scraper, fix quote wall E2EE, improve tarot prompts
Space groups: automatic room grouping via membership overlap percentage.
Rooms with sufficient shared members are grouped together so leaderboards,
trivia scores, and stats aggregate across the community. Uses strict
clique-based algorithm (every room must meet threshold with every other
room in group). Configurable via SPACE_GROUP_THRESHOLD env var. Persisted
to SQLite, recomputed hourly and on startup.

HLTB scraper: rewrote for new howlongtobeat.com API (two-step token auth
via /api/finder/init + /api/finder endpoint).

Quote wall: fix E2EE decrypt flow for reply-to-save (ParseRaw before
Decrypt, skip ParseRaw after Decrypt returns pre-parsed event). Fix
subcommand matching for delete/search. Remove broken star-reaction handler
and old quote handler from user.go.

Other fixes:
- Strip Matrix reply fallback before command detection (main.go)
- Increase Ollama context window to 8192
- Improve tarot spread prompts (~4x longer, narrative arc)
- Add "cards are props" instruction to tarot LLM prompt
- Fix movies.go CommandDef name mismatch
- Add Community category to !help
- Remove daily horoscope broadcast cron
- Update README with all changes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 00:45:25 -07:00
prosolis
1c02732445 Add tarot readings, encrypted quote wall, display name resolution, and uptime reporting
New features:
- !tarot and !tarotspread commands with LLM-powered readings (zodiac-aware)
- !quote system with AES-256-GCM encryption at rest, reply-to-save, search, and quoteboard
- Shared crypto utility (internal/crypto) for AES-256-GCM encrypt/decrypt/HMAC
- ResolveUser now matches display names via room membership as fallback
- !vibe and !tldr show bot uptime when insufficient messages buffered

Bug fixes:
- Fix duplicate quotes table schema that would crash quotewall at runtime
- Fix biased random selection in quote retrieval (use rand.Intn)
- Fix XP rank calculation to handle tied scores with COUNT(DISTINCT xp)
- Scope all leaderboards to room members (repboard, pottyboard, insultboard, firstboard, rankings, emojiboard)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 18:08:05 -07:00
prosolis
03def6463f Add horoscope, !time @user, profile achievements; fix mention detection and multiple bugs
- Add !horoscope command and daily horoscope digest using Free Horoscope API
- Enhance !time to support @user timezone lookups
- Add profile completeness achievements (birthday, timezone, both set)
- Fix Matrix mention detection in reputation and LLM passive plugins
- Fix !whois reputation query (reason string mismatch and calculation)
- Fix !time IANA case-sensitivity and username/city collision
- Fix timezone default to empty string for achievement detection
- Scope all leaderboards to room members (XP, rep, potty, insult, first, rankings, emoji)
- Fix XP rank calculation to handle tied scores correctly
- Replace skull emoji with bomb for bot insult reactions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 16:58:15 -07:00
prosolis
298c7bb8f1 Expand sentiment to 10 categories, add DB maintenance job, update README
- Expand LLM sentiment classification from 3 to 10 categories: positive,
  negative, neutral, excited, sarcastic, frustrated, curious, grateful,
  humorous, supportive — with emoji reactions for each
- Add daily DB maintenance job at 03:00 UTC to purge stale caches,
  expired rate limits, old logs, and run SQLite optimize
- Add migrate.sql for upgrading existing databases
- Update README: add !sentiment command, maintenance job, rate limit
  config, in-memory message buffer docs, SDK migration history,
  remove references to previous private repo

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 20:46:18 -07:00
prosolis
2c7c15b282 Add rate limiting, API response caching, and fix progress bar panic
- Rate limit !weather (5/day), !concerts (10/day), fix !translate default to 20/day
- Add generic api_cache table with CacheGet/CacheSet helpers
- Cache !weather (1h), !wiki, !define, !tv (24h), !upcoming movies, !anime season/upcoming (24h)
- Fix ProgressBar panic on negative Repeat count when XP exceeds level threshold
- All rate limits configurable via RATELIMIT_* env vars

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 19:07:48 -07:00
prosolis
ddb196aaad Rewrite from TypeScript to Go
Complete rewrite of the Freebee Matrix bot as GogoBee using mautrix-go.

- E2EE with goolm (pure Go, no CGo/libolm) and cross-signing bootstrap
- 35+ plugins with dependency injection and ordered registration
- SQLite storage via modernc.org/sqlite (no CGo)
- Scheduler via robfig/cron for WOTD, holidays, birthdays, releases, etc.
- Optional LLM integration (Ollama) for sentiment, profanity, roasts, vibes
- Threaded trivia, three-tier profanity tracking, WOTD usage verification
- Multi-country holiday support with deduplication
- Quadratic XP curve, configurable bot display name, encrypted DMs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 18:25:18 -07:00