Files
RedditBot/docs/configuration.md
T
jjbrunton 633bb0ec04 Restructure chat service, web routes, and add test suite
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.
2026-05-21 01:32:10 +01:00

489 lines
13 KiB
Markdown

# Configuration Guide
Complete reference for configuring RedditBot.
---
## Table of Contents
1. [Configuration Overview](#configuration-overview)
2. [Configuration Files](#configuration-files)
3. [Environment Variables](#environment-variables)
4. [config.yaml Reference](#configyaml-reference)
5. [Runtime Configuration](#runtime-configuration)
6. [Configuration Validation](#configuration-validation)
7. [Troubleshooting](#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:
1. Reads `config.yaml` from the project root
2. Merges environment variable overrides
3. Validates the entire configuration tree using Zod
4. Returns a strongly-typed `Config` object
---
## 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:
```env
OPENROUTER_API_KEY=sk-or-v1-abc123...
REDDIT_USERNAME=MyBotUser
REDDIT_PASSWORD=secret123
BROWSER_HEADLESS=true
```
> **Note**: Add `.env` to `.gitignore` - never commit credentials.
**Option 2: Shell export**
```bash
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:
```yaml
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
```yaml
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
```yaml
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
```yaml
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
```yaml
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
```yaml
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
```yaml
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
```yaml
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)
```yaml
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
```yaml
web:
# Port for the web dashboard
port: 3000
```
### Global Settings
```yaml
# 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:
1. Open the settings modal
2. Modify desired values
3. Click "Save"
Via API:
```bash
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**:
1. Verify `config.yaml` is in the project root
2. Check YAML syntax (use a YAML linter)
3. Verify environment variables are set correctly
4. Check bot logs for specific validation errors
### Credentials Not Working
**Symptom**: Browser login fails.
**Steps**:
1. Verify `REDDIT_USERNAME` and `REDDIT_PASSWORD` are correct
2. Check that the account is not locked or requires 2FA
3. Try manual login via `npm run login`
4. Ensure `BROWSER_HEADLESS=false` for debugging login issues
### AI Not Working
**Symptom**: Image analysis fails or returns errors.
**Steps**:
1. Verify `OPENROUTER_API_KEY` is valid
2. Check API key has sufficient balance
3. Verify `openrouter.model` is available on OpenRouter
4. Check fallback models are set correctly
5. Review logs for specific AI errors
### Browser Session Expired
**Symptom**: Bot stops with "Browser session expired" error.
**Steps**:
1. Delete `data/browser/` directory
2. Run `npm run login` to re-authenticate
3. Restart the bot
### Port Already in Use
**Symptom**: Dashboard fails to start with "Port already in use".
**Steps**:
1. Change `web.port` in `config.yaml`
2. Or kill the process using the port: `lsof -i :3000`