TypeScript Discord bot that answers /aufhocker queries with Claude, using recent channel history as context.
  • TypeScript 99.2%
  • JavaScript 0.6%
  • Dockerfile 0.1%
Find a file
Joey Davis 54fe96f81f
All checks were successful
CI / build-and-test (push) Successful in 1m58s
CI / docker-build (push) Successful in 22s
fix(lol): count summoners a poll cycle skipped instead of hiding them
`Polling cycle complete | errors=0` could report a clean cycle while a
watched summoner had silently dropped out of it.

A spectator-v5 call that fails is caught inside pollSummoner, which
returned `no_change` — the same value a successful poll returns when it
finds nothing new. The cycle's `errors` counter only increments when a
poll *throws*, so the swallowed failure was invisible: the summary
claimed errors=0 with summonersPolled still asserting full coverage.
Production showed the pair directly — a 10s spectator timeout at
11:27:57, then errors=0 at 11:30:04 for the cycle that lost it.

pollSummoner now returns a `skipped` outcome (new PollOutcome type) from
both failure branches, the transient network error and the
response-mapping TypeError, since neither one actually checked the
summoner. `no_change` is left meaning only what it says. The cycle
summary reports skipped alongside errors, keeping the distinction
between "the poll threw" and "the poll gave up on this summoner".

No retry was added. A skipped summoner costs one 5-minute cycle: a game
start announces late, a game end is caught next cycle or by the hourly
stale sweep. Counting the skips matters because that number is what
would have to climb before any of that stopped holding, and it was not
visible anywhere.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 13:30:16 -05:00
.forgejo/workflows ci: publish Docker image to Forgejo registry on main (1.4.5) 2026-07-14 09:10:39 -05:00
docker/pgadmin Fix P2-2: run migrations at startup; migrations are the only schema source 2026-07-10 21:19:27 -05:00
docs feat(logging): probe every dependency at boot and report a checklist 2026-08-03 07:23:57 -05:00
src fix(lol): count summoners a poll cycle skipped instead of hiding them 2026-08-03 13:30:16 -05:00
.dockerignore Initial import of aufhocker Discord bot 2026-07-08 19:05:04 -05:00
.env.config feat(lol): stop chasing LoL Classic results Riot never publishes 2026-08-02 21:00:15 -05:00
.env.development fix: reclaim rate-limit entries, bound log files, drop dead LOG_FILE (1.5.3) 2026-07-20 07:42:46 -05:00
.env.example docs(env): clarify Riot Personal vs Development key expiry in .env.example 2026-07-14 22:40:06 -05:00
.env.production fix: reclaim rate-limit entries, bound log files, drop dead LOG_FILE (1.5.3) 2026-07-20 07:42:46 -05:00
.gitattributes Bring repo tooling and CI up to parity with other forgejo projects 2026-07-08 20:31:21 -05:00
.gitignore Relicense MIT, split secrets/config, pace Riot API, fix compose drift 2026-07-12 11:52:13 -05:00
.nvmrc Modernize toolchain: eslint 10, jest 30, TS 6, Node 22, zero CVEs 2026-07-08 23:00:24 -05:00
.prettierignore Bring repo tooling and CI up to parity with other forgejo projects 2026-07-08 20:31:21 -05:00
.prettierrc.json Relicense MIT, split secrets/config, pace Riot API, fix compose drift 2026-07-12 11:52:13 -05:00
CHANGELOG.md fix(lol): count summoners a poll cycle skipped instead of hiding them 2026-08-03 13:30:16 -05:00
CLAUDE.md fix(deps): clear high brace-expansion advisory; sync lockfile version (1.7.1) 2026-07-31 07:09:08 -05:00
docker-compose.production.yml fix(deploy): restore .env.config to the production compose env_file (1.4.4) 2026-07-14 09:06:54 -05:00
docker-compose.yml Relicense MIT, split secrets/config, pace Riot API, fix compose drift 2026-07-12 11:52:13 -05:00
Dockerfile Relicense MIT, split secrets/config, pace Riot API, fix compose drift 2026-07-12 11:52:13 -05:00
eslint.config.js Relicense MIT, split secrets/config, pace Riot API, fix compose drift 2026-07-12 11:52:13 -05:00
jest.config.js P4-5: test-suite structure, AdminCommandHandler + connection coverage 2026-07-12 01:25:43 -05:00
LICENSE Relicense MIT, split secrets/config, pace Riot API, fix compose drift 2026-07-12 11:52:13 -05:00
package-lock.json fix(lol): count summoners a poll cycle skipped instead of hiding them 2026-08-03 13:30:16 -05:00
package.json fix(lol): count summoners a poll cycle skipped instead of hiding them 2026-08-03 13:30:16 -05:00
README.md docs: rewrite deployment docs around the live Swarm topology (1.5.1) 2026-07-19 15:21:20 -05:00
tsconfig.json Fix P0-2: enable noImplicitOverride in tsconfig 2026-07-10 17:51:35 -05:00
tsconfig.test.json Modernize toolchain: eslint 10, jest 30, TS 6, Node 22, zero CVEs 2026-07-08 23:00:24 -05:00
user-mappings.example.json Fix P0-5: stop tracking user-mappings.json (real Discord IDs + names) 2026-07-10 17:57:26 -05:00

Aufhocker Discord Bot

Aufhocker is a Discord bot that integrates with Claude AI, providing AI assistance directly within Discord servers using conversation history as context.

Features

  • Claude AI Integration: Interact with Claude AI through Discord slash commands
  • Private Responses by Default: All responses are private to you initially for privacy
  • Make Public Button: Share responses with everyone using the "📢 Make Public" button
  • Secure Sharing: Only you can make your responses public, with 1-hour expiry for security
  • Conversation Context: Uses server message history to provide relevant context to Claude
  • Configurable Context: Users can specify context scope (message count or time duration)
  • Configurable Message Retention: Automatically stores and prunes message history (default 30 days, via RETENTION_DAYS)
  • Admin Tools (opt-in): With ENABLE_ADMIN_TOOLS, Claude can use web_search (Brave) and web_fetch (SSRF-guarded) for admin users
  • Rate Limiting: Built-in per-user rate limiting with admin bypass capabilities
  • Admin Emergency Controls: Temporary bypasses, rate limit management, and audit trail via the /aufadmin command
  • Optional Plugins: League of Legends live-game monitor/predictions and a Diablo 2 (d2emu) terror-zone / DClone tracker — both off by default
  • Comprehensive Logging: Detailed logging for debugging and monitoring

Quick Start

Prerequisites

  • Node.js 22.0.0 or higher
  • PostgreSQL database (or use the bundled Docker Compose stack)
  • Discord Bot Token
  • Claude API Key

Installation

  1. Clone the repository:
git clone <repository-url>
cd aufhocker
  1. Install dependencies:
npm install
  1. Set up environment variables:
cp .env.example .env
# Edit .env with your configuration
  1. Set up the database:
# Create database and run migrations
npm run db:migrate
  1. Build and start the bot:
npm run build
npm start

For development:

npm run dev

Local Development with Docker

For a quick local development setup using Docker:

  1. Start PostgreSQL and pgAdmin:
docker-compose up -d
  1. Access the services:

    • PostgreSQL: localhost:5432
    • pgAdmin: http://localhost:5050
      • Email: admin@aufhocker.local
      • Password: admin
  2. The database is automatically initialized with the required schema.

  3. To stop the services:

docker-compose down
  1. To reset the database (removes all data):
docker-compose down -v
docker-compose up -d

Docker Environment Variables

The following environment variables can be customized in your .env file for Docker:

Variable Default Description
POSTGRES_USER aufhocker PostgreSQL username
POSTGRES_PASSWORD aufhocker_dev PostgreSQL password
POSTGRES_DB aufhocker Database name
POSTGRES_PORT 5432 PostgreSQL port
PGADMIN_EMAIL admin@aufhocker.local pgAdmin login email
PGADMIN_PASSWORD admin pgAdmin login password
PGADMIN_PORT 5050 pgAdmin web interface port

Configuration

Configuration is split across two files so secrets and non-secret tuning have different lifecycles:

  • .env (git-ignored) — secrets and deployment identity only. Copy .env.example to .env and fill it in.
  • .env.config (committed) — non-secret defaults: models, intervals, feature flags, pool sizes. Edit this for behaviour changes; it contains no credentials.

Both are loaded together (docker-compose env_file: [.env.config, .env]; local runs load .env then .env.config via dotenv). Their keys are disjoint, and a value in .env wins on any overlap. .env.development / .env.production remain full single-file templates for setups that prefer one file.

Required Configuration

  • DISCORD_TOKEN: Your Discord bot token
  • DISCORD_CLIENT_ID: Your Discord application client ID
  • CLAUDE_API_KEY: Your Anthropic Claude API key
  • DATABASE_URL: PostgreSQL connection string

Optional Configuration

  • CLAUDE_MODEL: Claude model to use (default: claude-sonnet-5)
  • CLAUDE_MAX_TOKENS: Maximum tokens per request (default: 4096)
  • CLAUDE_MAX_CONTEXT_TOKENS: Token budget for channel-history context sent to Claude (default: 3000)
  • RETENTION_DAYS: Message retention period in days (default: 30)
  • ENABLE_ADMIN_TOOLS: Enable Claude tool use (web_search/web_fetch) for admin users (default: false; web_search also needs BRAVE_SEARCH_API_KEY)
  • LOG_LEVEL: Logging level (default: info)

Usage

Basic Commands

  • /aufhocker query:<your question> - Ask Claude a question privately (default: last 1 hour context)
  • /aufhocker query:<your question> context:20 messages - Ask with specific message count context
  • /aufhocker query:<your question> context:1 hour - Ask with time-based context
  • /aufhocker query:<your question> context:24 hours - Ask with extended time context

Context Parameter (Optional)

The context parameter is optional and controls how much conversation history Claude sees:

When omitted: Defaults to the last 1 hour of messages Message count: context:20 messages or context:50 (1-100 messages) Time duration: context:2 hours or context:3 days (1-23 hours, 1-30 days) Relative dates: context:yesterday or context:today Specific dates: context:2026-01-10 (ISO format, all times in UTC)

💡 Tip: For simple questions, leave context empty to use the default (last 1 hour).

Privacy & Sharing

🔒 Private by Default: All responses are initially private - only you can see them.

📢 Make Public: Use the "Make Public" button to share responses with everyone in the channel.

🛡️ Security Features:

  • Only the original user can make their response public
  • Public sharing expires after 1 hour for security
  • Context from channel history is always used appropriately

Examples

/aufhocker query:What's the main topic we've been discussing?
/aufhocker query:Can you summarize what John said? context:10 messages
/aufhocker query:What decisions did we make about the project? context:2 hours
/aufhocker query:What was discussed yesterday? context:yesterday
/aufhocker query:What happened on this date? context:2026-01-10

Workflow:

  1. Use /aufhocker query:<your question> command
  2. Optionally add context: parameter to specify conversation history scope
  3. Receive a private response with Claude's answer
  4. Optionally click "📢 Make Public" to share with everyone
  5. Public responses expire after 1 hour automatically

Privacy & Security

Aufhocker is designed with privacy and security in mind:

Private Responses by Default

  • All Claude responses are ephemeral (private) by default
  • Only the user who asked the question can see the initial response
  • This prevents accidental sharing of sensitive information

Controlled Public Sharing

  • Users can choose to make responses public using the "📢 Make Public" button
  • Security: Only the original user can make their own response public
  • Expiry: Public sharing capability expires after 1 hour for security
  • One-time: Each response can only be made public once

Context Privacy

  • Message history is used appropriately for context
  • Bot messages are excluded from context to prevent confusion
  • Context is channel-specific (no cross-channel data leakage)
  • Automatic retention policy (default 30 days, configurable via RETENTION_DAYS) protects long-term privacy

Technical Security Features

  • Response IDs include user verification to prevent unauthorized sharing
  • Automatic cleanup of expired sharing tokens
  • Secure button interaction handling with user validation
  • Comprehensive error handling to prevent information leakage

Development

Scripts

  • npm run build - Build TypeScript to JavaScript
  • npm run dev - Run in development mode with hot reload
  • npm test - Run all tests
  • npm run test:watch - Run tests in watch mode
  • npm run test:coverage - Run tests with coverage report
  • npm run lint - Run ESLint
  • npm run lint:fix - Fix ESLint issues automatically

Testing

The project includes comprehensive testing:

  • Unit Tests: Test individual components in isolation
  • Property-Based Tests: Validate universal correctness properties
  • Integration Tests: Test component interactions

Run tests:

npm test

Project Structure

src/
├── admin/         # Admin domain: AdminService, RateLimiter, AdminAuditService, UserMapper
├── bot/           # Thin host (AufhockerBot): infra + plugin registry dispatch
├── claude/        # Anthropic SDK client, tool executor, SSRF guard, ToolAuditService
├── commands/      # Slash-command parsing + admin command handling
├── config/        # Configuration management
├── context/       # Channel history → Claude context assembly
├── database/      # Postgres connection, message store, migrations
├── discord/       # Discord client, command registration, routing
├── message/       # Message processing and 2000-char splitting
├── plugins/       # Plugin contract + features: query/ (core chat), lol/, d2emu/
├── types/         # Shared TypeScript type definitions
├── utils/         # Cross-cutting infra only: logger, ErrorHandler, ShutdownHandler
└── test/          # Test setup and utilities

Database Schema

The bot uses PostgreSQL. Migrations under src/database/migrations/scripts/ are the single source of truth; they are applied automatically at startup. Core tables:

  • messages — message history (pruned per RETENTION_DAYS, default 30)
  • user_mappings — Discord-ID → display-name mappings
  • admin_audit_trail — audit log of all admin actions
  • temporary_rate_limit_bypasses — active/expired admin-granted bypasses
  • schema_migrations — applied-migration bookkeeping

Plugin tables (created only by their migrations): lol_watchlist, lol_active_games, lol_monitor_config, lol_prediction_history, d2emu_config.

Error Handling

The bot includes comprehensive error handling for:

  • Authentication failures (Discord/Claude)
  • Rate limiting (with exponential backoff)
  • Network errors (with retry logic)
  • Database connection issues
  • Invalid command parameters

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests for new functionality
  5. Ensure all tests pass
  6. Submit a pull request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Deployment

For detailed deployment instructions, see the Deployment Guide:

  • Local Development: Docker Compose setup with PostgreSQL and pgAdmin
  • Production: Docker Swarm — registry-pulled image, shared Postgres, Swarm secrets (the live deployment)
  • Alternative: single-host self-contained stack via docker-compose.production.yml
  • Database Migrations: Schema management and updates
  • Troubleshooting: Common issues and solutions

Quick links:

Admin Controls

For global administrators, Aufhocker provides emergency controls and administrative features through the separate /aufadmin command. See the Admin Controls Guide for details:

  • Separate Admin Command: /aufadmin command with Discord Administrator permission requirement
  • Dual Permission System: Requires both Discord Administrator role AND global admin user ID configuration
  • Temporary Rate Limit Bypasses: Grant temporary relief to users
  • Rate Limit Management: Clear rate limits for users or all users
  • Status Monitoring: Check rate limit status for any user
  • Audit Trail: All admin actions are logged for security and compliance

Admin Commands

Important: Admin commands require both Discord Administrator permission AND being configured as a global admin in the bot settings.

  • /aufadmin quota bypass - Grant temporary rate limit bypass to a user
  • /aufadmin quota revoke - Revoke temporary bypass for a user
  • /aufadmin quota status - Check user rate limit status
  • /aufadmin quota clear - Clear rate limits for a user or all users
  • /aufadmin reload-mappings - Re-read user-mappings.json and sync it to the database

Visibility: Non-admin users will not see the /aufadmin command in their command list. Only users with Discord Administrator role will see it, but execution still requires global admin user ID configuration.

Quick links:

Support

For issues and questions:

  1. Check the logs in the logs/ directory
  2. Review the Deployment Guide troubleshooting section
  3. Ensure all required environment variables are set
  4. Check database connectivity

Security

  • Never commit your .env file
  • Rotate API keys regularly
  • Use environment-specific configurations
  • Monitor usage and rate limits