Two things, entangled in the working tree because 20d1f92 was mislabeled
(its message claimed "device grant" but it only deleted registration.yaml.example
and left the failed appservice+/sync hybrid in client.go):
1. Land the MAS OAuth 2.0 device grant that was running in prod but never
committed: masauth.go + client.go rewrite. Bot stays a normal user so /sync
works; refresh token persisted in data/mas_auth.json.
2. Add "appservice" as an alternate transport behind AUTH_MODE (default
"masdevice" = unchanged device-grant path, instant rollback):
- internal/bot/session.go: Session abstraction unifying both transports
(OnEventType/Run/Stop); NewSession dispatches on AuthMode.
- internal/bot/appservice.go: as_token auth (MAS-durable, no login/MFA/expiry),
cryptohelper MSC4190 device creation, crypto machine fed from Synapse
transaction pushes (to-device/device-lists/OTK) instead of /sync, inbound
decrypt+redispatch, and lazy per-room StateStore backfill (encryption +
members) so replies in encrypted rooms encrypt to the right recipients.
- main.go: NewSession/sess.OnEventType/sess.Run in place of the /sync loop.
- .env.example: AUTH_MODE, AS_REGISTRATION, AS_LISTEN_HOST/PORT, HOMESERVER_DOMAIN.
The appservice path fixes the earlier hybrid's fatal flaw (Synapse forbids AS
users from /sync) by using the transaction/push model. Bot-side only; Synapse
registration + experimental_features (msc2409/msc3202/msc4190) and E2EE cutover
are the next steps. Build + vet green (CGO=1 -tags goolm).
New optional plugin (FEATURE_SPACE_INVITER) that detects local homeserver
users who aren't in any Matrix Space and prompts the configured admin via
DM with yes/no/ignore. Persistent prompt log in space_inviter_prompts so
restarts and upgrades don't re-ping for users who already replied.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GM→DM rename across docs and code (GMNarrationType→DMNarrationType,
GMState→DMState, narration constants, comments) so the system reads as
"Dungeon Master" everywhere. Player-visible "GM mood" wording stays
where it appears in flavor.
Streamed zone/expedition combat: zone advance now stages patrol →
patrol play-by-play → patrol resolution → room intro → room play-by-play
→ final outcome through sendZoneCombatMessages with 2–3s pacing
(arena keeps its 5–8s window). Combat narrative lines pick up a compact
d20-vs-AC roll annotation for hit/crit/miss/block events.
Combat outcome polish: dndHPSnapshot lets narration show sheet HP
rather than legacy combat-engine HP, and markAdventureDead clears the
zombie state where hp_current was 0 but the legacy alive flag stayed
true after a D&D-layer KO.
Adv 2.0 announcement (ADVENTURE_2.0_ANNOUNCEMENT.md), README rewrite
covering the new layer, and adv2_scenario_test.go — a full
zone-run + expedition + harvest playthrough against a copy of the prod
DB asserting persisted state end-to-end.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Status sheet now shows lifetime craft count + a hint pointing at
!adventure recipes (only displayed once the player has crafted at
least once — keeps the line absent for non-foragers).
- README gains a Crafting subsection documenting the auto-craft flow,
XP rewards, level gates, the recipes command, and the consumable
protection (Robbie + sell-all both skip them).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
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>
- New Co-op Dungeons subsection covering tier table, all !coop commands,
funding mechanics, floor events, party strength factors, newbie liability,
spectator betting, gift system, loot/masterwork rules, wipe semantics, and
pinning behavior
- Refresh the Adventure feature bullet to mention co-op
- Update Blacksmith section with the post-tuning base rates (T0-T5: 1, 2,
5, 12, 30, 80) and the softened convexity formula
Co-Authored-By: Claude Opus 4.7 (1M context) <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>
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>
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>
Replace Wordnik/LibreTranslate references with DreamDict throughout.
Add multilingual hangman and !wotd force docs.
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>
Forex: add CAD to tracked currencies, add cross-pair syntax (EUR/USD,
EUR|JPY, etc.) for both !fx rate and !fx report with full historical
analysis computed from stored USD-base rates.
Adventure: fix midnight reset silently failing due to SQLite busy
contention with the reminder fire loop — propagate errors so the job
isn't marked complete on failure, add retry with backoff, and add
startup catch-up for missed resets and expired death timers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
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>
- 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>
- 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>
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>
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>
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>
Generates milk carton style "missing person" PNG images using gg library
with embedded Go fonts. Tracks inactive members via daily_activity, with
configurable thresholds (MISSING_THRESHOLD_DAYS, MISSING_MAX_DAYS,
MISSING_MIN_MESSAGES, MISSING_EXCLUDE_USERS). Rate limited to 1 carton
per room per day. Derives flavor text characteristics from user stats
and sentiment data. Avatar fetching with 3-tier fallback.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
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>
- Level-up announcements with Twinbee/Parodius-themed messages and emoji flair
- New plugins: !howami (LLM stat roast), !vibe (room energy), !tldr (conversation summary)
- !game command as primary game lookup with !retro as alias
- Prefetch system: API data cached at 00:05 UTC for reliable scheduled posts
- Nightly maintenance job: prunes old data across 15+ tables
- Sentiment aggregation: new sentiment_stats table with backfill migration
- Access token moved from .env to device.json for security
- Retry logic (sendReact/sendWithRetry) for transient connection failures
- Trivia crash fix: optional chaining on threadEventId across all locations
- Bug fixes: COALESCE for NULL SUM in howami, unused params in vibe, dead try-catch in fun
- License updated from ISC to MIT
- README: weather section expanded, prefetch/maintenance documented
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Full-featured Matrix bot with E2EE via matrix-js-sdk, 32+ plugins,
LLM-powered passive classification, XP/leveling, trivia, game releases,
anime/movie tracking, and more.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>