Players in a co-op were getting their combat action back every midnight
reset and offered the dungeon option in the morning DM. Per spec they
should be "off in the Co-op" and unable to also solo-grind dungeons.
Fix at the DB layer (so all CanDoCombat() paths agree):
- New helper lockCoopCombatActions() sets combat_actions_used=99 for any
user in an active coop run. Called immediately after every
resetAllAdvDailyActions() — at startup and at midnight.
- Render layer: morning DM now shows "combat locked (in Co-op #N, day
X/Y)" and replaces the dungeon section with an explanatory line
pointing them at !coop status.
Combat returns to normal automatically once the run completes/wipes —
the next midnight reset finds status != 'active' and skips the lock.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each gift now has its own 6h voting window, but the only places the
remaining time was visible were the original arrival post and any
edit-triggered re-render after a vote. Mid-window the displayed
countdown was stale.
!coop status now lists pending gifts (anything with vote_result still
NULL on this run) with current open/leave tally and a fresh per-gift
countdown derived from expires_at. The party can poll this any time to
see what's about to close.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The TwinBeeGiftArrival pool entries each end with a templated footer
authored to the spec format ("Open: {count} · Leave it: {count}\nMajority
rules. Ties go to {leader}."), but the placeholders were never being
substituted — players saw the literal "{count}" and "{leader}" strings.
Substitute in renderCoopGiftPost: ordered Replace for the two {count}
positions (opens, then leaves), ReplaceAll for {leader}. Also dropped
the redundant "Current votes:" line my code was appending — the flavor's
own tally line covers it after substitution.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Previous guard skipped resolution if lock_date == today_date, pushing
first-day windows to 24-32h regardless of when in the UTC day the lock
fired. With per-minute lock checks, the original same-tick race no longer
exists, so the date guard was just adding latency.
New guard: at least 12h since lock. Late-night locks still get >12h
funding/voting windows; early-morning locks get 24h+ as before. No effect
on the existing run #1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Admin-only command that creates a gift without the party-member or
harvest-action checks. Lets a solo tester exercise the basket/mimic
mechanic without recruiting a non-party spectator.
Usage: !coop admgift <run_id> <basket|mimic> [sender_user_id]
(sender defaults to the caller)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Locks were gated behind the same once-per-day 08:00 UTC tick as floor
resolution. A run created after morningHour would wait until next day's
tick to be eligible — meaning actual lock latency could be 24-48h, not the
24h advertised. Worse, a run created late on Day N missed the Day N+1 tick
(too early) AND the Day N+2 tick happens 48h after creation.
Fix: lock checks fire on every ticker minute (cheap timestamp scan over
typically 0-5 open runs). Resolution stays daily, gated by JobCompleted.
Real symptom that prompted this: Run #1 created 17:17 UTC, locked never
fired because the morning tick at 08:00 UTC the next day saw the run as
not-yet-eligible (only 15h elapsed), then marked the daily job complete
before the actual 24h elapsed at 17:17 UTC.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Win/end announcements were sent twice in solo-vs-bot: once via
SendMessage(game.RoomID, ...) for the public room, once via broadcastDM
which iterated the human player's DMRoomID — but in solo-vs-bot the
game.RoomID IS the player's DM. Skip players whose DMRoomID matches
game.RoomID in broadcastDM so the room post isn't echoed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Old values (open ±7, leave ±4) had matching EV but asymmetric variance;
risk-averse parties had a strict preference to always leave gifts, which
collapsed the dilemma. New values (all ±6) keep EV=0 for both strategies
AND equalize variance. Choice now depends entirely on inferring sender
intent.
Adds TestCoopGiftVarianceSymmetric — fails loud if anyone reintroduces
asymmetric magnitudes. EV test was already in place but only checked the
mean; missed this on first pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Replace flat 'LLM might be offline' error strings in vibe and howami with
the deadpan 'Thanks, Ollama.' callback. Skipped tarot — its 'union-mandated
vacation' line was already the right register and shouldn't be displaced.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Combat actions are only deducted at lock, never at start or join, so
there's nothing to refund when an unjoined invite expires. Just say so.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aggregates that surface community-level patterns:
- Per-tier outcomes table (open/active/won/wiped/cancelled, win%, avg party size)
- Gold flow: rewards distributed vs funding forfeited to wipes
- Betting: bets placed, distinct bettors, total wagered, paid to winners,
estimated house cut
- Gift activity: basket/mimic sent, open rates by type
- TwinBee helpfulness: last 30 events vs lifetime
Public command (no admin gate) so the community can debate strategy with
shared context.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
- runAutoBabysitDay now logs daily totals (was silently skipping logBabysitActivity)
- Stop wiping babysit_log on expiry/cancel; clear at next purchase instead so history persists between services
- Fix achievement query referencing wrong table name (babysit_log -> adventure_babysit_log)
- Tune blacksmith baseRates ([1,3,8,20,55,150] -> [1,2,5,12,30,80]) and soften the convexity (damage/100 -> damage/200) so mid-game repair drag isn't punitive
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Arena helmet auto-equip now preserves the existing helmet to inventory
(or stashes the new drop if the existing piece is strictly higher tier),
and arena gear can be re-equipped via !adventure equip. Shop only blocks
swaps that aren't an upgrade over current arena tier. DM response window
bumped from 15m to 3h so NPC/treasure/shop prompts don't expire while
players are AFK. MiningSuccess flavor pools no longer name specific ores
in prose — uses {ore}/{location}/{tool} placeholders so the narration
matches the actual loot.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Messages, links, and images each have independent combo streaks
that multiply passive euro earning. Combo grows 3x per step
(+200%), resets after 10 min idle, with daily caps (50 msg,
10 links, 15 images). !combo command shows current streaks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
- 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>
Replaces hardcoded tip scenarios with solver-frequency-backed decisions, adds
equity range display, fixes bet-size matching tolerance (25% threshold), and
adds comprehensive test coverage for scenario validation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces the single-roll probability system for dungeon and arena combat with
a multi-phase simulation engine where gear, buffs, pets, and NPCs are meaningful
mechanical inputs. Adds consumable items (auto-crafted from forage/mine/fish drops),
monster abilities for T2+ enemies, and Dragon Quest-style combat narrative with
phased message delivery.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Gather up to 5 candidate words from DreamDict RandomWord, then use
FrequencyBatch to pick the most commonly used one. Players get
recognizable everyday words instead of obscure ones that just clear
the min_freq floor.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
Concurrency:
- npcTrackMessage now acquires advUserLock to prevent lost updates
on message count and double encounter triggers from rapid messages
- Lock released before spawning encounter goroutine (avoids deadlock)
- Only one NPC encounter fires per message
Security:
- Debit() return value checked for both Misty and Arina payments
- Buff only applied on successful debit; failed debit falls through
to decline/debuff path
- NPC encounter won't overwrite existing pending interactions (shop,
treasure discard, etc.)
Arena sniper rework:
- Sniper checked independently before combat roll, not after
- If sniper fires, combat roll is skipped entirely (not a death save)
- Sniper kill renders as standalone narrative with no combat log
- Sniper flavor text rewritten: reads as random arena chaos with no
hint of Arina's involvement
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
The nudge message after an action was computing harvest remaining
inline with maxHarvestActions + 1 for holidays instead of using the
harvestMax++ pattern used everywhere else. Functionally equivalent
but inconsistent — would break if the holiday bonus ever changed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
- Character sheet now checks isHolidayToday() for correct remaining
action counts on holidays
- Remove renderAdvHolidaySecondPrompt (dead code after economy split)
and its test
- Babysit harvest counter clamped to max to prevent exceeding budget
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
- 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>
- 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>
- 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>
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>
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>
New DictionaryPlugin with !antonym, !pronounce, !etymology, !difficulty,
and !rhyme commands. Integrate antonyms into !define (concurrent 500ms
fetch), difficulty tier into Hangman headers, and etymology into WOTD.
Fix rhyme limit bug (request 50, display 7), variable shadowing, and
add spec-required emoji prefixes to all dictionary command outputs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
Wordle:
- Use DreamDict RandomWord API as primary word source, local pools as fallback
- Expand word length support from 5-7 to 5-20
- Validate DreamDict words are correct length and alpha-only
- Keep max guesses at 6 regardless of word length
- Use wordleMaxGuesses() instead of hardcoded 6 for rehydration
WOTD:
- Reject non-English candidates that are valid English words (fixes "puzzlingly" as Portuguese WOTD)
- Make cross-language translations optional (DreamDict pt-PT coverage is sparse)
- Use LLM fallback to translate foreign words to English when DreamDict lacks translations
- Remove hasTranslations re-prefetch guard (no longer needed with optional translations)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Overlevel penalty reduces loot and XP when effective level exceeds location minimum (gap >3: -15%/level, floor 5%)
- 3-hour per-location cooldown after successful runs, persisted via activity log
- Applies to all activity types (dungeons, mining, foraging, fishing)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
WOTD: Require both English and French translations, validate before
display, fall back to English word if pt-PT search exhausts 100 attempts.
LLM passive: Inject actual WOTD into classification prompt instead of
letting LLM guess at "unusual words". Add DreamDict-based fancy word
detection using frequency data — react with 🎓 for rare vocabulary.
Arena: Reduce death lockout from next-midnight-UTC to 2 hours.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
UNO addbot:
- !uno addbot / !uno removebot for lobby bot management (WinBee, GwinBee)
- Bots ante from community pot, increasing the total pot
- Single human can start a game if bots fill remaining slots
- Per-bot display names throughout (replaces global unoBotName)
Audit fixes:
- Lobby display now always shows both humans and bots (was omitting bots on join)
- Slot check accounts for bots (was only counting humans)
- Color Roulette bot victim name fixed (was showing acting bot's name)
- Lobby display built under lock to prevent stale reads
- Bot name list separated from env var to prevent duplicates
- Extracted shared buildLobbyDisplay helper
- Inlined unnecessary nameOf closure in sudden death
Other:
- T5 masterwork drop rate reduced from 1.5% to 0.5%
- Added fishing stats to !superstatsexplusalpha
- Updated README with all new features
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
Standalone HTTP client for the DreamDict service supporting word
lookup, random word generation, and cross-language translation
(en/fr/pt-PT). Used by hangman, lookup, and WOTD plugins.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2-player UNO (especially No Mercy) regularly stalls past 100 turns.
Sudden death announces at turn 80 with a 20-turn countdown, then
scores hands by card point values — lowest total wins. Applies to
solo (vs bot) and multiplayer when 2 active players remain.
Also adds "It's X's turn" labels to bot plays in multiplayer,
matching the format human plays already had, and adds sudden death
checks to all turn advancement paths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace LibreTranslate-based !translate with DreamDict cross-language
lookup (en/fr/pt-PT) with auto-detect and 5-per-lang cap
- Add multilingual hangman mode (!hangman <lang> [--clue <lang>]) using
DreamDict RandomWord/Translate with retry logic and race-safe placeholder
- Rewrite WOTD to pick pt-PT words with Portuguese definitions and
en/fr translations, add !wotd force for moderators
- Move DreamDict client init earlier in main.go for broader availability
- Wire dictClient into LookupPlugin, HangmanPlugin, and WOTDPlugin
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
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>