Skip to content

nesquikm/mythwobble

Repository files navigation

MythWobble

AI-powered text adventure games on Discord

Try it on DiscordFeaturesHow It WorksTechnical HighlightsSetupArchitectureLicense


MythWobble is a Discord bot that generates dynamic, AI-driven text adventure games. Players interact through Discord threads using buttons and text input to make choices that shape AI-generated narratives. Supports solo and multiplayer (up to 4 players).

Join the Discord server to try MythWobble without setting up your own instance.

MythWobble gameplay — a sentient rubber duck adventure

Features

  • AI-generated narratives — every playthrough is unique, powered by any OpenAI-compatible LLM (default: Gemini Flash)
  • Multiplayer — up to 4 players per game, with turn-based response collection and per-player targeting
  • Rich game configuration — choose theme, tone, narrative length, number of turns, and language
  • Multi-language support — play in any language the LLM supports; canonical state kept in English, output in player's language
  • 8-block memory system — structured context management that maintains narrative coherence across 50+ turn games within token budgets
  • Deterministic summarization — rule-based turn compression (no LLM call), triggers every 5 turns with fallback extraction
  • Plot generation — story skeletons with act structure, turning points, and multiple ending types (victory, defeat, bittersweet, cliffhanger)
  • Saga mode — chain multiple games into connected story arcs with character/world state carried forward
  • Gameplay resilience — death spiral prevention via action categorization, repetition detection, and stall recovery with director guidance injection
  • Per-game command queues — PostgreSQL-backed FIFO queues with advisory locks for race-free concurrent game processing
  • Prompt injection mitigations — defense-in-depth input sanitization with Unicode normalization, delimiter isolation, and suspicious pattern auditing
  • Scene recovery — survives bot crashes mid-turn; recovers unsent scenes on restart
  • Cost controls — per-session and daily cost limits, per-turn token tracking with 6-decimal precision
  • Timeout handling — automatic warnings at 4 min, timeouts at 5 min, 24-hour abandonment detection
  • Rate limiting — per-user and per-guild rate limits to prevent abuse
  • 1500+ tests — comprehensive unit and integration test coverage

How It Works

  1. A player runs /start in any Discord channel
  2. The bot creates a private thread and guides setup (players, theme, tone, narrative length, turns, language)
  3. A plot synopsis with story skeleton is generated to guide narrative consistency
  4. Each turn, the LLM generates a narrative scene with 3-5 strategically distinct action choices
  5. Players pick an action via buttons or type a custom response
  6. The story continues until the target turn count, ending with a narrative conclusion
  7. Optionally, continue the story as a new chapter via saga mode

The 3-Second Problem

Discord requires interaction acknowledgment within 3 seconds, but LLM calls take 3-30 seconds. MythWobble handles this by immediately deferring the reply (deferReply() in <100ms), queuing the action in PostgreSQL, and editing the reply once the LLM responds.

Memory Architecture

The bot uses an 8-block memory system to keep the LLM context focused within a 6,100-token input budget:

Block Purpose Token Budget
System Preamble Narrator persona, output format, language policy 1,000
Metadata Theme, tone, player list, turn count, progress % 400
Players State Character stats, inventory, known facts 500
World State Locations, NPCs, environment, global facts 500
Plot Summary Compressed history of older turns 1,500
Recent Turns Last 5 turns verbatim (sliding window) 2,000
Control State Expected players, pacing, director guidance 200
Gameplay Tracking Death spiral detection (internal only, not sent to LLM) 0

See docs/MEMORY_SYSTEM.md for the full deep-dive.

Technical Highlights

Concurrency Model

Each game session has a dedicated PostgreSQL-backed FIFO command queue. Actions are processed sequentially per game (no race conditions) while different games process concurrently. Worker serialization uses PostgreSQL advisory locks (pg_advisory_xact_lock) to eliminate TOCTOU race conditions in action claiming. Idempotency is enforced at the database level via UNIQUE constraints on request IDs — not application-level checks.

See docs/CONCURRENCY.md for the full deep-dive.

LLM Integration

Provider-agnostic via the OpenAI-compatible client — works with Gemini, GPT, Claude, Groq, and local models. The response parsing pipeline uses strict Zod validation with multi-stage graceful fallbacks: if JSON parsing fails, it tries markdown extraction, then regex extraction, then uses raw text as narrative. The design principle: never reject an LLM response entirely — a partial narrative is better than an error.

See docs/LLM_INTEGRATION.md for the full deep-dive.

Gameplay Resilience

Actions are categorized via regex heuristics into strategic categories (direct, stealth, social, investigate, creative, retreat). A 5-turn sliding window detects repetition (same approach 3+ times) and stall (5 turns without state progress). When detected, director guidance is injected into the system prompt, forcing the LLM to offer fundamentally different approaches.

Multiplayer Response Aggregation

When multiple players must respond, the pending_responses table with PRIMARY KEY (session_id, user_id) prevents duplicates at the database level. INSERT ... ON CONFLICT DO NOTHING handles concurrent button clicks atomically. After all expected players respond (or timeout), all responses are aggregated and processed in a single LLM call.

Button Payload System

Discord's custom_id has a strict 100-character limit. MythWobble stores full button payloads server-side with 8-character nanoid references. Format: prefix|sessionId|payloadId (~40 chars). Expired buttons gracefully fall back using the embedded session ID.

Scene Recovery

If the bot crashes after committing a turn to the database but before sending the Discord message, the scene recovery system detects unsent scenes on startup and delivers them. No turns are ever lost.

Story Skeletons

Plot generation uses randomly selected narrative structure templates (e.g., "The Hidden Cost", "The Unreliable Ally") that provide act-by-act story progression guidance. The LLM follows the skeleton while adapting to player choices, producing more satisfying story arcs than unconstrained generation.

State Validation

Before applying state updates from the LLM, the engine validates location IDs exist, NPC references are consistent, and inventory items exist before removal. Invalid updates are skipped with warnings — the narrative always continues.

Tech Stack

Component Technology
Runtime TypeScript / Node.js
Discord discord.js v14
Database PostgreSQL with JSONB + Drizzle ORM
LLM OpenAI-compatible client (Gemini, GPT, Claude, local models)
Validation Zod (env vars, LLM responses, player input)
Testing Vitest (1500+ unit tests + integration tests)
Logging Pino (structured JSON logging)
Token Counting tiktoken
ID Generation nanoid

Development with Claude Code

This project was built entirely with Claude Code and uses several of its advanced features:

Custom Agents (.claude/agents/)

Five specialized agents run during development:

Agent Purpose
documentation-auditor Audits docs for inconsistencies before/after changes
code-reviewer Reviews implementation against patterns and standards
test-coverage-analyzer Identifies test gaps after completing features
architecture-validator Validates design decisions against architecture docs
security-auditor Checks for vulnerabilities before deployment

Skills (.claude/skills/)

Skill What it does
/deploy Full production deployment with DB migration checklist, log viewing, service management
/test-discord E2E testing via Chrome automation — Claude Code calls mcp-rubber-duck which drives Chrome DevTools to navigate Discord, create games, click buttons, and verify gameplay flow. Gemini Flash processes DOM snapshots instead of Opus, reducing cost.

Hooks (.claude/hooks/)

Pre-commit hook runs the full test suite and linter before every git commit, ensuring no broken code is committed.

Setup

Prerequisites

  • Node.js >= 20
  • PostgreSQL (or Docker)
  • A Discord bot token (guide)
  • An LLM API key (Gemini, OpenAI, etc.)

Installation

git clone https://github.com/nesquikm/mythwobble.git
cd mythwobble
npm install

Configuration

Copy the example environment file and fill in your values:

cp .env.example .env

Key variables:

Variable Description
DISCORD_TOKEN Bot token from Discord Developer Portal
DISCORD_CLIENT_ID Application ID from Discord Developer Portal
DATABASE_URL PostgreSQL connection string
LLM_API_KEY API key for your LLM provider
LLM_BASE_URL OpenAI-compatible API endpoint
LLM_MODEL Model name (e.g., gemini-2.5-flash)

See .env.example for the full list with game settings, timeouts, rate limits, cost controls, and scheduler intervals.

Database

Start PostgreSQL via Docker Compose:

docker compose up -d

Run migrations:

npm run db:migrate

Running

# Development (hot reload)
npm run dev

# Production
npm run build
npm start

Discord Bot Setup

  1. Create a new application at discord.com/developers
  2. Go to Bot tab, copy the token → DISCORD_TOKEN
  3. Copy the Application ID → DISCORD_CLIENT_ID
  4. Enable Privileged Gateway Intents: Message Content
  5. Go to OAuth2 → URL Generator, select bot + applications.commands
  6. Bot permissions: Send Messages, Create Public Threads, Send Messages in Threads, Embed Links, Read Message History
  7. Use the generated URL to invite the bot to your server

Self-Hosting

MythWobble runs well on a small VPS (1 vCPU, 1 GB RAM, ~$6/month). For production:

  1. Clone the repo on your server
  2. Set up .env with production values
  3. Set up PostgreSQL (Docker or native)
  4. Build and run: npm run build && npm start
  5. Use systemd or PM2 to keep the bot running

The deploy skill (.claude/skills/deploy/SKILL.md) contains a full deployment reference with migration checklist.

Testing

npm test                  # Unit tests (1500+)
npm run test:integration  # Integration tests (game flow, handlers)
npm run test:all          # Both
npm run lint              # ESLint
npm run typecheck         # TypeScript strict mode

Architecture

Detailed technical documentation in docs/:

Document Content
ARCHITECTURE.md Comprehensive reference — schema, state machine, all subsystems (~1700 lines)
MEMORY_SYSTEM.md Deep-dive into the 8-block memory architecture, token budgeting, and summarization
CONCURRENCY.md Per-game command queues, advisory locks, idempotency, and multiplayer response aggregation
LLM_INTEGRATION.md Prompt engineering, structured output parsing, language enforcement, prompt injection mitigations
RETENTION_DESIGN.md Design exploration for player retention via worlds and profiles
RETENTION_ANALYSIS.md Analysis of retention approaches with web research and alternative mechanisms

License

MIT

About

AI-powered text adventure games on Discord

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages