Split monolithic chat service into modular structure with Matrix client support, extract web server into per-resource route modules, introduce bootstrap/main-loop separation, and add comprehensive vitest coverage across services, repositories, and utilities. Adds project docs and CLAUDE.md agent instructions.
13 KiB
Configuration Guide
Complete reference for configuring RedditBot.
Table of Contents
- Configuration Overview
- Configuration Files
- Environment Variables
- config.yaml Reference
- Runtime Configuration
- Configuration Validation
- Troubleshooting
Configuration Overview
RedditBot uses a layered configuration approach:
┌──────────────────────────────────────────────────────────────┐
│ Priority Order (High → Low) │
├──────────────────────────────────────────────────────────────┤
│ 1. Environment Variables │
│ (OPENROUTER_API_KEY, REDDIT_USERNAME, etc.) │
│ │
│ 2. config.yaml │
│ (Primary configuration file) │
│ │
│ 3. Defaults │
│ (Hardcoded defaults in code) │
└──────────────────────────────────────────────────────────────┘
Configuration is loaded at startup by loadConfig() in src/config/index.ts, which:
- Reads
config.yamlfrom the project root - Merges environment variable overrides
- Validates the entire configuration tree using Zod
- Returns a strongly-typed
Configobject
Configuration Files
config.yaml
The primary configuration file, located at the project root:
RedditBot/
├── config.yaml # ← Main configuration
├── package.json
├── tsconfig.json
└── src/
Data Directory
Runtime data is stored in data/ (created automatically):
data/
├── reddit-bot.db # SQLite database
├── browser/ # Browser session data (cookies, storage)
├── media/ # Downloaded media files
│ ├── index.json # Media index (SHA-1 → filename)
│ ├── abc123.jpg # Media file 1
│ └── def456.png # Media file 2
└── chats/ # Cached conversation JSON files
├── chat_abc123.json
└── chat_def456.json
Environment Variables
Environment variables override corresponding values in config.yaml.
| Variable | Required | Type | Description | Example |
|---|---|---|---|---|
OPENROUTER_API_KEY |
Yes | string | OpenRouter API key for AI services | sk-or-v1-abc123... |
REDDIT_USERNAME |
Yes | string | Reddit username for authentication | MyBotUser |
REDDIT_PASSWORD |
Yes | string | Reddit password for authentication | secret123 |
BROWSER_HEADLESS |
No | boolean | Run browser in headless mode | true or false |
Setting Environment Variables
Option 1: .env file (recommended for development)
Create a .env file in the project root:
OPENROUTER_API_KEY=sk-or-v1-abc123...
REDDIT_USERNAME=MyBotUser
REDDIT_PASSWORD=secret123
BROWSER_HEADLESS=true
Note
: Add
.envto.gitignore- never commit credentials.
Option 2: Shell export
export OPENROUTER_API_KEY=sk-or-v1-abc123...
export REDDIT_USERNAME=MyBotUser
export REDDIT_PASSWORD=secret123
Option 3: Docker
Pass via docker-compose.yml or Docker CLI:
environment:
- OPENROUTER_API_KEY=sk-or-v1-abc123...
- REDDIT_USERNAME=MyBotUser
- REDDIT_PASSWORD=secret123
config.yaml Reference
Complete reference for every configuration option.
Reddit Settings
reddit:
# Subreddits to monitor for new posts
subreddits:
- gonewild
- ratemyboobs
- HomemadeNsfw
- realgirls
- plussize_gonewild
# ... add or remove subreddits
# Minutes between polling cycles
pollInterval: 15
# Target gender for image analysis filtering
# Options: "F" (female), "M" (male)
targetGender: "F"
# Keywords used to detect sellers/spam profiles
# Posts from users matching these keywords are skipped
sellerKeywords:
- seller
- onlyfans
- link in bio
- check my profile
- promo
- discount
- affiliate
OpenRouter AI Settings
openrouter:
# OpenRouter API key (can also be set via OPENROUTER_API_KEY env var)
apiKey: "sk-or-v1-abc123..."
# Primary AI model for image analysis
# Models must be available on OpenRouter
model: "meta-llama/llama-3.2-90b-vision-preview"
# Fallback models tried if the primary model refuses or fails
fallbackModels:
- "google/gemini-2.0-flash-001"
- "meta-llama/llama-3.2-11b-vision-instruct"
# Quick detection model (cheaper/faster, used for gender pre-filter)
quickModel: "google/gemini-2.0-flash-lite-001"
# System prompt for image analysis
# The AI will describe the image and detect gender
systemPrompt: |
Analyze this image and provide:
1. Gender detected (F or M)
2. Detailed description of the image content
3. Notable features (hair color, clothing, setting, etc.)
Return as JSON: {"gender": "F", "description": "...", "features": [...]}
# System prompt for message generation
# Used when crafting DMs to post authors
messagePrompt: |
Generate a natural, friendly DM for this Reddit post.
Image analysis: {description}
Post title: {title}
Strategy: {strategy}
Guidelines:
- Be complimentary but not creepy
- Reference something specific from the image
- Keep it under 2 sentences
- Sound like a genuine person, not a bot
- Do NOT ask for anything in return
Rate Limiting
rateLimit:
# Minimum delay between messages (milliseconds)
# Higher values reduce detection risk
minDelayMs: 30000
# Maximum messages per hour
# Bot will pause when this limit is reached
maxMessagesPerHour: 20
# Random jitter added to delay (milliseconds)
# Adds unpredictability to timing
jitterMs: 15000
Timing example with defaults:
Minimum delay: 30,000ms (30 seconds)
Jitter: 0 to 15,000ms (0-15 seconds)
Effective delay: 30-45 seconds between messages
Max messages: 20 per hour
Browser Settings
browser:
# Run browser in headless mode (no UI)
# Set to false for debugging
headless: true
# Directory for persistent browser data
# Cookies and session storage are saved here
userDataDir: "./data/browser"
Browser session persistence:
data/browser/
├── Default/
│ ├── Cookies # Reddit session cookies
│ ├── Local Storage # Local storage data
│ └── Preferences # Browser preferences
├── Login Data # Saved login info
└── Preferences # Browser state
Important
: Keep this directory intact. Deleting it requires re-login.
Database Settings
database:
# Path to SQLite database file
path: "./data/reddit-bot.db"
The database is auto-created on first run. Migrations run automatically on startup.
Media Sync Settings
mediaSync:
# Enable periodic media synchronization
enabled: true
# Interval between sync cycles (minutes)
intervalMinutes: 30
Sync behavior:
- Incremental sync (automatic): Downloads media from top 10 conversations
- Full sync (manual via dashboard): Downloads media from all conversations
Auto-Reply Settings
autoReply:
# Enable automatic chat replies
enabled: true
# Auto-send replies (true) or mark as pending for approval (false)
autoSend: false
# Minutes between checking for new messages
checkInterval: 10
Reply modes:
| autoSend | Behavior |
|---|---|
true |
Bot sends AI-generated replies automatically |
false |
Bot suggests replies, user approves via dashboard |
Strategy Variants (A/B Testing)
strategyVariants:
# Variant 1: Compliment-based
- name: "compliment"
prompt: |
Write a friendly compliment about the person's appearance.
Be genuine and specific. Keep it short and natural.
enabled: true
# Variant 2: Question-based
- name: "question"
prompt: |
Write a friendly message that includes a question.
Engage them in conversation naturally.
enabled: true
# Variant 3: Casual observation
- name: "observation"
prompt: |
Write a casual observation about the photo or setting.
Sound like you're commenting to a friend.
enabled: true
Web Dashboard Settings
web:
# Port for the web dashboard
port: 3000
Global Settings
# Global dry run mode
# When true, all actions are logged but not actually performed
dryRun: false
# Number of successful message examples to include in prompts
fewShotExamples: 3
# Minimum responses needed before trusting strategy stats
minStrategySamples: 10
# Exploration rate for A/B testing (0.0 to 1.0)
# 0.15 = 15% of messages use random variants (exploration)
explorationRate: 0.15
Runtime Configuration
The following settings can be modified via the web dashboard without restarting the bot:
| Setting | Endpoint | Type | Description |
|---|---|---|---|
rateLimit.minDelayMs |
PUT /api/config |
number | Message delay |
rateLimit.maxMessagesPerHour |
PUT /api/config |
number | Hourly cap |
rateLimit.jitterMs |
PUT /api/config |
number | Jitter range |
reddit.pollInterval |
PUT /api/config |
number | Poll frequency |
dryRun |
PUT /api/config |
boolean | Dry run mode |
Updating Runtime Config
Via dashboard:
- Open the settings modal
- Modify desired values
- Click "Save"
Via API:
curl -X PUT http://localhost:3000/api/config \
-H "Content-Type: application/json" \
-d '{
"rateLimit": {
"minDelayMs": 45000,
"maxMessagesPerHour": 15,
"jitterMs": 20000
},
"dryRun": true
}'
Note
: Runtime changes are in-memory only. They are lost when the bot restarts. To make changes persistent, update
config.yaml.
Configuration Validation
All configuration is validated using Zod at startup. Invalid configuration will cause the bot to exit with a descriptive error.
Validation Rules
| Field | Validation |
|---|---|
reddit.subreddits |
Must be non-empty array of strings |
reddit.pollInterval |
Must be positive integer |
reddit.targetGender |
Must be "F" or "M" |
openrouter.apiKey |
Must be non-empty string |
openrouter.model |
Must be non-empty string |
openrouter.fallbackModels |
Must be non-empty array |
rateLimit.minDelayMs |
Must be positive integer |
rateLimit.maxMessagesPerHour |
Must be positive integer |
rateLimit.jitterMs |
Must be non-negative integer |
browser.headless |
Must be boolean |
browser.userDataDir |
Must be non-empty string |
database.path |
Must be non-empty string |
mediaSync.enabled |
Must be boolean |
mediaSync.intervalMinutes |
Must be positive integer (if enabled) |
autoReply.enabled |
Must be boolean |
autoReply.autoSend |
Must be boolean |
autoReply.checkInterval |
Must be positive integer (if enabled) |
strategyVariants |
Must be array with name + prompt |
web.port |
Must be integer 1-65535 |
dryRun |
Must be boolean |
Common Validation Errors
Error: Invalid config: openrouter.apiKey must be a string
Fix: Set OPENROUTER_API_KEY environment variable or add apiKey to config.yaml
Error: Invalid config: reddit.subreddits must be a non-empty array
Fix: Add at least one subreddit to reddit.subreddits in config.yaml
Error: Invalid config: browser.userDataDir must be a string
Fix: Ensure browser.userDataDir is set in config.yaml
Troubleshooting
Configuration Not Loading
Symptom: Bot fails to start with validation errors.
Steps:
- Verify
config.yamlis in the project root - Check YAML syntax (use a YAML linter)
- Verify environment variables are set correctly
- Check bot logs for specific validation errors
Credentials Not Working
Symptom: Browser login fails.
Steps:
- Verify
REDDIT_USERNAMEandREDDIT_PASSWORDare correct - Check that the account is not locked or requires 2FA
- Try manual login via
npm run login - Ensure
BROWSER_HEADLESS=falsefor debugging login issues
AI Not Working
Symptom: Image analysis fails or returns errors.
Steps:
- Verify
OPENROUTER_API_KEYis valid - Check API key has sufficient balance
- Verify
openrouter.modelis available on OpenRouter - Check fallback models are set correctly
- Review logs for specific AI errors
Browser Session Expired
Symptom: Bot stops with "Browser session expired" error.
Steps:
- Delete
data/browser/directory - Run
npm run loginto re-authenticate - Restart the bot
Port Already in Use
Symptom: Dashboard fails to start with "Port already in use".
Steps:
- Change
web.portinconfig.yaml - Or kill the process using the port:
lsof -i :3000