- TypeScript 99.2%
- JavaScript 0.6%
- Dockerfile 0.1%
`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> |
||
|---|---|---|
| .forgejo/workflows | ||
| docker/pgadmin | ||
| docs | ||
| src | ||
| .dockerignore | ||
| .env.config | ||
| .env.development | ||
| .env.example | ||
| .env.production | ||
| .gitattributes | ||
| .gitignore | ||
| .nvmrc | ||
| .prettierignore | ||
| .prettierrc.json | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| docker-compose.production.yml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| eslint.config.js | ||
| jest.config.js | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| tsconfig.test.json | ||
| user-mappings.example.json | ||
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 useweb_search(Brave) andweb_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
/aufadmincommand - 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
- Clone the repository:
git clone <repository-url>
cd aufhocker
- Install dependencies:
npm install
- Set up environment variables:
cp .env.example .env
# Edit .env with your configuration
- Set up the database:
# Create database and run migrations
npm run db:migrate
- 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:
- Start PostgreSQL and pgAdmin:
docker-compose up -d
-
Access the services:
- PostgreSQL:
localhost:5432 - pgAdmin:
http://localhost:5050- Email:
admin@aufhocker.local - Password:
admin
- Email:
- PostgreSQL:
-
The database is automatically initialized with the required schema.
-
To stop the services:
docker-compose down
- 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.exampleto.envand 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 tokenDISCORD_CLIENT_ID: Your Discord application client IDCLAUDE_API_KEY: Your Anthropic Claude API keyDATABASE_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_searchalso needsBRAVE_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:
- Use
/aufhocker query:<your question>command - Optionally add
context:parameter to specify conversation history scope - Receive a private response with Claude's answer
- Optionally click "📢 Make Public" to share with everyone
- 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 JavaScriptnpm run dev- Run in development mode with hot reloadnpm test- Run all testsnpm run test:watch- Run tests in watch modenpm run test:coverage- Run tests with coverage reportnpm run lint- Run ESLintnpm 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 perRETENTION_DAYS, default 30)user_mappings— Discord-ID → display-name mappingsadmin_audit_trail— audit log of all admin actionstemporary_rate_limit_bypasses— active/expired admin-granted bypassesschema_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
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for new functionality
- Ensure all tests pass
- 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:
/aufadmincommand 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-readuser-mappings.jsonand 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:
- Check the logs in the
logs/directory - Review the Deployment Guide troubleshooting section
- Ensure all required environment variables are set
- Check database connectivity
Security
- Never commit your
.envfile - Rotate API keys regularly
- Use environment-specific configurations
- Monitor usage and rate limits