33 KiB
RedditBot - Software Architecture Documentation
Table of Contents
- Project Overview
- System Architecture
- Directory Structure
- Component Reference
- Data Flow
- Database Schema
- API Reference
- Configuration
- Design Patterns
- External Integrations
- Deployment
- Technical Debt & Recommendations
Project Overview
RedditBot is an automated Reddit monitoring and messaging system that:
- Monitors 30+ NSFW subreddits for image posts
- Uses AI (OpenRouter) to analyze images and detect gender
- Generates personalized direct messages to post authors
- Sends DMs via browser automation (Playwright)
- Monitors for responses and tracks success rates
- Implements A/B testing with explore/exploit strategy selection
- Learns from successful message/response pairs (few-shot learning)
- Provides a real-time web dashboard for monitoring and control
Key Capabilities
| Capability | Description |
|---|---|
| Post Discovery | Polls subreddits for new image posts, filters by gender and spam profiles |
| AI Analysis | Multi-model image analysis with fallback chain via OpenRouter |
| Message Generation | AI-crafted DMs using successful examples as few-shot prompts |
| A/B Testing | Explore/exploit strategy selector optimizes response rates over time |
| Response Monitoring | Background service checks inbox for user replies |
| Chat Automation | Reddit chat scraping via shadow DOM traversal, auto-reply support |
| Media Management | Download, cache, deduplicate, and sync media from conversations |
| Web Dashboard | React operator console with typed SSE events, analytics, and guarded controls |
System Architecture
High-Level Architecture
┌─────────────────────────────────────────────────────────────────────┐
│ main (index.ts) │
│ Orchestrator / Entry Point │
└────────────────────────┬────────────────────────────────────────────┘
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌──────────────┐ ┌──────────────┐
│ Monitor │ │ Processor │ │ Response │
│ Service │ │ │ │ Checker │
│ │ │ 1. Download │ │ │
│ Poll subreddits│ │ 2. Validate │ │ Check inbox │
│ for new posts │ │ 3. Gender │ │ for replies │
└───────┬───────┘ │ Filter │ └──────────────┘
│ │ 4. AI Analysis│ │
│ │ 5. Strategy │ │
│ │ Selection │ │
│ │ 6. Message │ │
│ │ Generation │ │
└────┬────┘ └─────────────┘ │
│ │
▼ ▼
┌───────────────────────┐ ┌───────────────────────┐
│ Strategy Selector │ │ Chat Service │
│ │ │ │
│ A/B Testing │ │ Scrapes chat (shadow │
│ Explore/Exploit │ │ DOM traversal) │
│ Variant Selection │ │ Auto-reply, upsell │
└───────────────────────┘ │ detection │
└───────────────────────┘
│ │
▼ ▼
┌───────────────────────┐ ┌───────────────────────┐
│ Message Sender │ │ Media Service │
│ (Browser) │ │ │
│ │ │ Download, cache, │
│ Playwright DM compose │ │ deduplicate (SHA-1) │
│ Shadow DOM handling │ │ Matrix thumbnail │
└───────────────────────┘ │ resolution │
└───────────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────┐
│ SQLite Database │
│ │
│ processed_posts │ sent_messages │ successful_messages │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ React Dashboard + Express API/SSE host │
│ │
│ Typed contracts │ SSE events │ Analytics │
│ Runtime config │ Bot controls │ Media gallery │
└─────────────────────────────────────────────────────────┘
Service Communication
main.ts (Orchestrator)
│
├──► Config (Zod-validated YAML + env vars)
│
├──► Database (Better-SQLite3)
│ │
│ └──► PostsRepository
│ └──► MessagesRepository
│ └──► SuccessfulMessagesRepository
│
├──► MonitorService ──► RedditApi ──► Playwright (cookies)
│
├──► ProcessorService
│ ├──► Image download (axios)
│ ├──► Gender detection (OpenRouter)
│ ├──► Full analysis (OpenRouter)
│ └──► StrategySelector ──► MessagesRepository
│
├──► MessageSender (Browser - Playwright)
│
├──► ResponseChecker ──► Playwright (inbox navigation)
│
├──► ChatService ──► Playwright (chat.reddit.com shadow DOM)
│
├──► MediaService ──► Playwright (media download)
│
├──► MediaSyncService (background periodic sync)
│
├──► AutoReplyService ──► OpenRouter (reply generation)
│
├──► RateLimiter (p-queue, jitter)
│
└──► WebServer (Express, SSE)
Directory Structure
RedditBot/
├── .dockerignore # Docker ignore rules
├── .gitignore # Git ignore rules
├── agents.md # Engineering standards & agent guidelines
├── config.yaml # Main YAML configuration file
├── docker-compose.yml # Docker Compose setup
├── DOKPLOY_DEPLOY.md # Dokploy deployment guide
├── Dockerfile # Container build definition
├── package.json # Node.js dependencies & scripts
├── tsconfig.json # TypeScript configuration
├── apps/
│ └── dashboard/ # React, TypeScript, React Router, and Vite
│ ├── src/ # Shell, routes, feature slices, API clients
│ └── dist/ # Generated production assets (not committed)
├── packages/
│ └── api-contracts/ # Shared Zod HTTP and realtime schemas
├── scripts/
│ ├── inspect-chat-dom.ts # Debug: inspect chat DOM structure
│ └── login.ts # Manual browser login helper
└── src/
├── index.ts # Main entry point / orchestrator (391 lines)
├── config/
│ └── index.ts # Config loader + Zod validation schema (157 lines)
├── database/
│ ├── client.ts # SQLite setup + migration runner (121 lines)
│ └── repositories.ts # Repository pattern: Posts, Messages, Success (433 lines)
├── reddit/
│ └── api.ts # Reddit API client (axios + Playwright cookies) (233 lines)
├── ai/
│ └── openrouter.ts # OpenRouter AI client (image analysis, message gen) (307 lines)
├── browser/
│ ├── client.ts # Playwright persistent context + login (302 lines)
│ ├── message-page.ts # DM compose page automation + shadow DOM (560 lines)
│ └── media-downloader.ts # Playwright-based media downloader (29 lines)
├── services/
│ ├── monitor.ts # Post discovery service (72 lines)
│ ├── processor.ts # Image download + AI + message pipeline (132 lines)
│ ├── response-checker.ts # Inbox response checker (179 lines)
│ ├── strategy-selector.ts # A/B testing explore/exploit (101 lines)
│ ├── chat.ts # Chat scraping + messaging + auto-reply (1123+ lines)
│ ├── media.ts # Media download, caching, indexing (254 lines)
│ ├── media-sync.ts # Periodic background media sync (119 lines)
│ └── auto-reply.ts # Auto-reply to chat conversations (199 lines)
├── utils/
│ ├── logger.ts # Winston structured logging (32 lines)
│ ├── rate-limiter.ts # Token-bucket rate limiting (114 lines)
│ ├── retry.ts # Exponential backoff wrapper (28 lines)
│ └── image.ts # Image download + base64 + MIME validation (34 lines)
└── web/
├── server.ts # Express API, SSE, and dashboard host
├── dashboard-assets.ts # Required production React-build resolver
└── events/ # Typed event hub and Express SSE adapter
Component Reference
Core Services
MonitorService (src/services/monitor.ts)
Discovers new image posts across configured subreddits.
- Shuffles subreddit order each cycle
- Fetches new posts via
RedditApi - Filters out already-processed posts and previously-messaged users
- Returns the single newest valid post per cycle
ProcessorService (src/services/processor.ts)
Core processing pipeline for each discovered post.
- Downloads image via
ImageService - Validates MIME type and file size
- Runs quick gender detection (cost-saving pre-filter)
- Selects A/B strategy variant via
StrategySelector - Runs full AI analysis + message generation via
OpenRouterService - Handles AI refusals gracefully (marks post, tries next)
StrategySelector (src/services/strategy-selector.ts)
Implements explore/exploit A/B testing for message variants.
- Tracks response rates per strategy variant
- Uses minimum sample thresholds before trusting statistics
- Applies random exploration rate for continued discovery
- Selects best variant based on weighted response rates
ResponseChecker (src/services/response-checker.ts)
Background service that monitors for user replies.
- Navigates Reddit inbox via Playwright
- Identifies responses to sent messages
- Updates
got_responseflag in database - Stores successful message/response pairs for few-shot learning
ChatService (src/services/chat.ts)
Most complex service - manages Reddit chat interactions.
- Scrapes
chat.reddit.comusing JavaScript injection (shadow DOM traversal) - Manages conversation cache (in-memory + disk JSON)
- Loads full conversation history with pagination
- Sends messages with human-like typing delays
- Verifies message delivery
- Detects upsell/spam keywords in chat
- Parameter:
myUsernameis hardcoded (should be from config)
MediaService (src/services/media.ts)
Media download, caching, and indexing.
- Downloads images from chat conversations
- Resolves Matrix blurred thumbnails to full resolution
- Stores files in
data/media/directory - Maintains JSON index with SHA-1 deduplication
- Tracks download status per conversation
MediaSyncService (src/services/media-sync.ts)
Periodic background media synchronization.
- Runs incremental syncs (top 10 chats) or full syncs (all conversations)
- Configurable interval via
mediaSyncInterval - Tracks sync status in memory
- Exposed via web dashboard endpoints
AutoReplyService (src/services/auto-reply.ts)
Automated reply to Reddit chat conversations.
- Checks for unread messages in cached conversations
- Detects upsell/spam keywords
- Generates AI reply suggestions via
OpenRouterService - Sends replies (auto or pending for manual approval)
- Event-based architecture for dashboard integration
External Integrations
RedditApi (src/reddit/api.ts)
Axios-based Reddit API client.
- Fetches posts from subreddits (
/r/{name}/new.json) - Validates posts (image-only, skips deleted authors)
- Extracts image URLs with fallback chain:
preview>i.redd.it>imgur>thumbnail - Checks user profiles against seller/spam keywords
- Uses Playwright browser cookies for authenticated requests
OpenRouterService (src/ai/openrouter.ts)
OpenRouter API client using OpenAI-compatible SDK.
analyzeImage(): Multi-model image analysis with fallback chaindetectGenderOnly(): Quick cost-saving gender detectiongenerateMessage(): DM text with few-shot examples from DBgenerateReply(): AI chat reply from conversation historydetectUpsell(): Keyword-based spam/OnlyFans detection- Refusal detection via heuristic patterns
Browser Client (src/browser/client.ts)
Playwright persistent context manager.
- Browser session persistence (cookies/storage)
- Login verification (multiple UI checks + API fallback)
- Automated login flow (form fill, 2FA/captcha detection)
- Debug screenshot capture on login failure
MessagePage (src/browser/message-page.ts)
Reddit DM compose page automation.
- Fills recipient/subject/body fields
- Extensive Shadow DOM support for
faceplate-*components - Detects recipient errors, verifies submission success
- Supports dry-run mode for testing
Data Layer
Database Client (src/database/client.ts)
Better-SQLite3 database initialization.
- Creates database at configured path
- Enables WAL mode and foreign keys
- Runs migrations on startup
- Safe column-addition for schema evolution
Repositories (src/database/repositories.ts)
Three repository classes abstracting SQLite operations.
- PostsRepository: Processed post CRUD, mark sent, update analysis
- MessagesRepository: Sent message CRUD, response tracking, strategy stats, subreddit stats, daily trends
- SuccessfulMessagesRepository: Successful pairs for few-shot learning, subreddit-weighted retrieval
Utilities
Logger (src/utils/logger.ts)
Winston structured logging.
- JSON format for log files
- Colored console output
- File logging:
error.logand combined log
RateLimiter (src/utils/rate-limiter.ts)
Token-bucket rate limiting.
- p-queue with concurrency=1
- Minimum delay between messages with random jitter
- Hourly message count tracking
- Timer status exposed to dashboard
Retry (src/utils/retry.ts)
Exponential backoff wrapper.
- p-retry with configurable retries (default 3)
- Backoff range: 5s to 60s
- Logging on retries and failures
Image (src/utils/image.ts)
Image processing utilities.
- Download via axios
- Base64 conversion for AI API
- MIME type validation (image/jpeg, image/png, image/webp)
Web Dashboard
WebServer (src/web/server.ts)
Express server with API route modules and SSE transport.
- Stats, activity, messages, chats, conversations, media endpoints
- Bot controls (start/stop)
- Typed resumable events at
/api/events/stream - Temporary legacy log stream at
/api/logs/stream - Analytics: strategy, subreddit, daily trends
- Runtime config management (GET/PUT)
- Debug endpoints
React dashboard (apps/dashboard)
- React Router routes Overview, Conversations, Media, Performance, Operations, and Settings.
- TanStack Query owns authoritative HTTP snapshots and mutation state.
- Feature-local clients validate responses with schemas from
packages/api-contracts. - Operations consumes typed dashboard events and invalidates affected query keys; cached content remains visible if the stream reconnects.
- Vite writes production assets to
apps/dashboard/dist.
The asset resolver requires the production React build. Express serves those assets, API/SSE routes, and private media behind one authentication boundary.
Data Flow
Post Processing Pipeline
1. MonitorService polls subreddits
│
├─► RedditApi.fetchPosts()
│ └─► GET /r/{subreddit}/new.json
│ └─► Filter: image posts only, valid author
│
├─► Filter: skip processed posts (DB check)
├─► Filter: skip previously messaged users
│
└─► Return newest valid post
│
2. ProcessorService.processPost(post)
│
├─► ImageService.downloadImage(url)
│ └─► Validate MIME type, file size
│
├─► OpenRouter.detectGenderOnly()
│ └─► Quick pre-filter (cost-saving)
│ └─► Skip if gender doesn't match target
│
├─► StrategySelector.selectStrategy(subreddit, author)
│ └─► Explore/exploit algorithm
│
├─► OpenRouter.analyzeImage(image)
│ └─► Multi-model fallback chain
│ └─► Detect gender, describe image
│
├─► OpenRouter.generateMessage(analysis, title, examples, strategy)
│ └─► Few-shot learning from successful_messages
│
└─► Mark post as processed in DB
│
3. MessagePage.sendDM(author, subject, message)
│
├─► Navigate to /compose?to={author}
├─► Fill fields (with shadow DOM handling)
├─► Submit message
└─► Verify success
│
└─► Record sent_messages in DB
Response Monitoring Flow
1. ResponseChecker.checkResponses()
│
├─► Navigate to /message/inbox
├─► Parse response items
│
├─► For each response:
│ ├─► Match to sent_messages by recipient + timestamp
│ ├─► Update got_response = 1
│ ├─► Store message/response pair
│ └─► Add to successful_messages (for few-shot learning)
│
└─► Update strategy stats in DB
Chat Interaction Flow
1. ChatService.fetchChats()
│
├─► Navigate to chat.reddit.com
├─► Traverse shadow DOM via JS injection
├─► Extract active conversations
└─► Cache in memory + disk JSON
│
2. ChatService.loadConversation(chatId)
│
├─► Load from cache
├─► Fetch full history (pagination)
└─► Return conversation object
│
3. ChatService.sendMessage(chatId, text)
│
├─► Navigate to chat view
├─► Fill message input (shadow DOM)
├─► Apply typing delay (human-like)
├─► Submit
└─► Verify delivery
Database Schema
Table: processed_posts
Tracks all posts that have been discovered and processed.
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
TEXT | PRIMARY KEY | Reddit post ID |
subreddit |
TEXT | NOT NULL | Source subreddit |
author |
TEXT | NOT NULL | Post author username |
title |
TEXT | Post title | |
image_url |
TEXT | Original image URL | |
image_analysis |
TEXT | AI-generated image description | |
generated_message |
TEXT | AI-generated DM text | |
processed_at |
INTEGER | NOT NULL | Unix timestamp |
message_sent |
INTEGER | DEFAULT 0 | 0 = not sent, 1 = sent |
error |
TEXT | Error message if processing failed |
Indexes: idx_posts_subreddit, idx_posts_author
Table: sent_messages
Tracks all DMs sent by the bot.
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
INTEGER | PRIMARY KEY AUTOINCREMENT | Internal ID |
post_id |
TEXT | FK → processed_posts.id | Source post |
recipient |
TEXT | NOT NULL | Recipient username |
subject |
TEXT | DEFAULT 'Stunning' | DM subject line |
message |
TEXT | NOT NULL | DM body |
sent_at |
INTEGER | NOT NULL | Unix timestamp |
strategy_id |
TEXT | A/B test variant identifier | |
got_response |
INTEGER | DEFAULT 0 | 0 = no response, 1 = responded |
response_checked_at |
INTEGER | Timestamp of last response check |
Indexes: idx_messages_recipient, idx_messages_strategy, idx_messages_response
Table: successful_messages
Stores successful message/response pairs for few-shot learning.
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
INTEGER | PRIMARY KEY AUTOINCREMENT | Internal ID |
sent_message_id |
INTEGER | FK → sent_messages.id (nullable) | Related message |
original_message |
TEXT | NOT NULL | The DM that got a response |
image_analysis |
TEXT | Context from image analysis | |
post_title |
TEXT | Original post title | |
response_snippet |
TEXT | First reply text | |
subreddit |
TEXT | For weighted example selection | |
created_at |
INTEGER | NOT NULL | Unix timestamp |
Indexes: idx_success_subreddit, idx_success_message
Migration Strategy
The database uses progressive migrations that:
- Run on every startup
- Use
ALTER TABLE ADD COLUMN IF NOT EXISTSfor safe schema evolution - Create indexes with
IF NOT EXISTSfor idempotency - Enable WAL mode and foreign keys for performance and integrity
API Reference
Web Dashboard Endpoints
All endpoints are served by the Express server at http://localhost:{port}.
Bot Status & Control
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/stats |
Bot statistics (running state, post/message/response counts, response rate) |
| POST | /api/start |
Start the bot |
| POST | /api/stop |
Stop the bot |
Activity & Messages
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/activity |
Recent activity log entries |
| GET | /api/posts/by-author/:username |
Look up processed posts by user |
| GET | /api/messages |
Recent sent messages |
| GET | /api/successful |
Successful message/response pairs |
Chat & Conversations
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/chats |
Active cached conversations |
| POST | /api/chats/fetch-all |
Fetch all chats from Reddit |
| GET | /api/conversations |
All cached conversations |
| GET | /api/conversations/:id |
Single conversation detail |
| POST | /api/conversations/:id/suggest-reply |
AI suggest reply for conversation |
| POST | /api/conversations/:id/send-reply |
Send chat reply |
| POST | /api/conversations/:id/approve-reply |
Approve pending auto-reply |
| POST | /api/conversations/:id/reject-reply |
Reject pending auto-reply |
| POST | /api/conversations/:id/dismiss-upsell |
Dismiss upsell flag |
Media
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/media |
Downloaded media gallery |
| GET | /api/media/sync |
Media sync status |
| POST | /api/media/sync |
Trigger incremental sync (top 10 chats) |
| POST | /api/media/sync/full |
Trigger full sync (all conversations) |
Analytics
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/analytics/by-subreddit |
Response rate by subreddit |
| GET | /api/analytics/by-strategy |
Response rate by strategy variant |
| GET | /api/analytics/trend |
Daily response rate trend |
Configuration
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/config |
Current runtime configuration |
| PUT | /api/config |
Update runtime config (rate limits, dry run, poll interval) |
Logs & Debugging
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/events/stream |
Typed resumable SSE event stream |
| GET | /api/logs/stream |
Temporary legacy SSE log stream |
| GET | /api/logs |
Recent logs (HTTP) |
| GET | /api/timers |
Rate limiter timer status |
| POST | /api/debug/inspect-dom |
Debug: inspect chat DOM structure |
| POST | /api/debug/check-responses |
Debug: manual response check |
Configuration
Configuration Sources (Priority Order)
config.yaml- Primary configuration file (project root)- Environment Variables - Override specific fields:
OPENROUTER_API_KEY- OpenRouter API keyREDDIT_USERNAME- Reddit usernameREDDIT_PASSWORD- Reddit passwordBROWSER_HEADLESS- Browser headless mode (true/false)
Configuration Schema (Zod-Validated)
{
reddit: {
subreddits: string[] // Subreddits to monitor
pollInterval: number // Minutes between polls
targetGender: string // Target gender filter
sellerKeywords: string[] // Keywords for spam/seller detection
}
openrouter: {
apiKey: string // OpenRouter API key
model: string // Primary AI model
fallbackModels: string[] // Fallback models on refusal
systemPrompt: string // System prompt for analysis
messagePrompt: string // System prompt for message generation
}
rateLimit: {
minDelayMs: number // Minimum delay between messages
maxMessagesPerHour: number // Hourly message cap
jitterMs: number // Random jitter range
}
browser: {
headless: boolean // Headless mode
userDataDir: string // Persistent session directory
}
database: {
path: string // SQLite database path
}
mediaSync: {
enabled: boolean // Enable media sync
intervalMinutes: number // Sync interval
}
autoReply: {
enabled: boolean // Enable auto-reply
autoSend: boolean // Auto-send or pending approval
checkInterval: number // Check interval in minutes
}
strategyVariants: {
name: string // Variant name
prompt: string // Message style prompt
enabled: boolean // Enable/disable variant
}[]
web: {
port: number // Dashboard port
}
}
Runtime Configuration
The following settings can be modified via the web dashboard without restarting:
rateLimit.minDelayMs- Message delayrateLimit.maxMessagesPerHour- Hourly caprateLimit.jitterMs- Jitter rangereddit.pollInterval- Poll frequencydryRun- Dry run mode (log only, no actual sends)
Design Patterns
Repository Pattern
All database operations are abstracted behind three repository classes:
PostsRepository- Post lifecycle managementMessagesRepository- Message tracking and analyticsSuccessfulMessagesRepository- Few-shot learning data
Benefits:
- Single source of truth for data access
- Easy to swap implementations (e.g., different databases)
- Consistent error handling and query patterns
Dependency Injection
Services receive dependencies via constructor parameters:
class ProcessorService {
constructor(
private config: Config,
private redditApi: RedditApi,
private openrouter: OpenRouterService,
private strategySelector: StrategySelector,
private messageSender: MessagePage,
private postsRepo: PostsRepository,
private messagesRepo: MessagesRepository,
private successfulRepo: SuccessfulMessagesRepository,
private logger: Logger
) {}
}
Benefits:
- Loose coupling between components
- Easy to mock dependencies for testing
- Clear dependency graph
Strategy Pattern
StrategySelector implements A/B testing with multiple message variants:
class StrategySelector {
selectStrategy(subreddit: string, author: string): string {
// Explore/exploit algorithm
// Returns variant name based on response rates
}
}
Benefits:
- Swappable message strategies
- Measurable performance per variant
- Configurable exploration rate
Factory Pattern
Configuration and database creation use factory functions:
loadConfig()- Loads, merges env vars, validates with ZodcreateDatabase()- Initializes SQLite, runs migrations
Observer Pattern
SSE (Server-Sent Events) carries validated dashboard events:
// The transport-agnostic hub assigns IDs and retains a bounded replay window.
eventHub.publish({ type: "message.sent", payload: { /* validated fields */ } });
// Express adapts the hub to resumable SSE.
router.get("/events/stream", broadcaster.events);
Circuit Breaker / Retry
withRetry() wrapper for resilient external calls:
await withRetry(
() => redditApi.fetchPosts(subreddit),
{ retries: 3, logger }
);
Multiple fallback chains:
- AI model fallback (primary → fallback models)
- Image URL fallback (preview → i.redd.it → imgur → thumbnail)
Rate Limiter Pattern
Token-bucket-like implementation:
class RateLimiter {
async waitForSlot(): Promise<void> {
// Enforce minimum delay + jitter
// Track hourly counts
// Block if hourly cap reached
}
}
External Integrations
Reddit API
- Authentication: Playwright browser cookies (persistent session)
- Endpoints Used:
GET /r/{name}/new.json- Fetch new postsGET /user/{username}/profile- Check for seller/spam indicatorsGET /message/inbox- Check for responsesGET chat.reddit.com- Chat scraping
- Rate Limits: Enforced by
RateLimiterutility
OpenRouter AI
- SDK: OpenAI-compatible (
openainpm package) - Base URL:
https://openrouter.ai/api/v1 - Endpoints Used:
POST /chat/completions- Image analysis, message generation, replies
- Model Fallback: Primary model → fallback models on refusal
- Cost Optimization: Quick gender detection before full analysis
Playwright Browser
- Context: Persistent (cookies/session storage saved to disk)
- Usage:
- Automated Reddit login
- DM compose page automation
- Inbox navigation for response checking
- Chat page scraping (shadow DOM traversal)
- Media file downloads
- Shadow DOM: JavaScript injection for
faceplate-*component traversal
Deployment
Docker
The project includes full Docker support:
Dockerfile- Playwright image with browser, VNC, and frontend/server build toolingdocker-compose.yml- Service orchestration.dockerignore- Excludes unnecessary files
Dokploy
Deployment documentation is provided in DOKPLOY_DEPLOY.md.
Required Environment Variables
| Variable | Required | Description |
|---|---|---|
OPENROUTER_API_KEY |
Yes | OpenRouter API key for AI |
REDDIT_USERNAME |
Yes | Reddit username |
REDDIT_PASSWORD |
Yes | Reddit password |
BROWSER_HEADLESS |
No | Browser headless mode (default: true) |
Pre-Deployment Checklist
- Set up
config.yamlwith target subreddits and settings - Set environment variables (or use
.envfile) - Run
npm installto install dependencies - Run
npm run buildto compile the server and Vite dashboard - Run
scripts/login.tsonce to establish browser session - Start the bot (via
npm startor Docker)
Technical Debt & Recommendations
Critical
-
Dashboard security cutover - Wire authenticated React asset, API, SSE, debug, and private-media hosting before exposing the dashboard externally.
-
No README.md - No project documentation for new developers.
-
Hardcoded Username -
ChatServicehasmyUsername = 'Upset_Marsupial1006'hardcoded in JS evaluation scripts. Should be parameterized from config.
High Priority
-
God Class -
main.ts(391 lines) creates all dependencies and wires everything. Consider extracting anAppContainerorDependencyContainer. -
ChatService Size - 1123+ lines. Consider splitting into:
ChatScraper(navigation + DOM traversal)ConversationCache(cache management)ChatMessenger(sending + verification)
-
No .env.example - No template showing required environment variables.
Medium Priority
-
Config Mutability - Runtime config updates via
PUT /api/configmodify the in-memory config object. Consider a proper config service with validation on updates. -
Error Handling - Some services have basic try/catch. Consider structured error types with retry logic.
Low Priority
-
Logging - Winston is well-configured, but consider adding request IDs for tracing.
-
Type Safety - Some
page.evaluate()calls useanytypes. Consider stricter typing for DOM structures. -
Media Storage - Media files stored in
data/media/with no cleanup policy. Consider retention policies.