# Development Guide Guide for contributing to RedditBot development. --- ## Table of Contents 1. [Project Structure](#project-structure) 2. [Development Setup](#development-setup) 3. [Coding Standards](#coding-standards) 4. [Adding a New Service](#adding-a-new-service) 5. [Adding a New Strategy Variant](#adding-a-new-strategy-variant) 6. [Debugging](#debugging) 7. [Common Tasks](#common-tasks) 8. [Known Issues](#known-issues) --- ## Project Structure ``` RedditBot/ ├── config.yaml # Main configuration ├── package.json # Dependencies and scripts ├── tsconfig.json # TypeScript config ├── Dockerfile # Container build ├── docker-compose.yml # Docker Compose ├── apps/ │ └── dashboard/ # React + Vite operator console ├── packages/ │ └── api-contracts/ # Shared Zod transport contracts ├── scripts/ │ ├── login.ts # Manual login helper │ └── inspect-chat-dom.ts # DOM inspection debug script └── src/ ├── index.ts # Entry point / orchestrator ├── config/ │ └── index.ts # Config loader + Zod schema ├── database/ │ ├── client.ts # SQLite setup + migrations │ └── repositories.ts # Repository classes ├── reddit/ │ └── api.ts # Reddit API client ├── ai/ │ └── openrouter.ts # OpenRouter AI client ├── browser/ │ ├── client.ts # Playwright browser manager │ ├── message-page.ts # DM compose automation │ └── media-downloader.ts # Media download ├── services/ │ ├── monitor.ts # Post discovery │ ├── processor.ts # Processing pipeline │ ├── response-checker.ts # Response monitoring │ ├── strategy-selector.ts # A/B testing │ ├── chat.ts # Chat scraping + messaging │ ├── media.ts # Media management │ ├── media-sync.ts # Media sync scheduler │ └── auto-reply.ts # Auto-reply service ├── utils/ │ ├── logger.ts # Winston logger │ ├── rate-limiter.ts # Rate limiting │ ├── retry.ts # Retry wrapper │ └── image.ts # Image utilities └── web/ # Express API, events, and dashboard hosting ``` --- ## Development Setup ### 1. Install Dependencies ```bash npm install ``` ### 2. Configure Copy and edit `config.yaml`: ```bash cp config.yaml config.yaml.backup # Backup existing # Edit config.yaml with your settings ``` ### 3. Set Environment Variables ```bash export REDDIT_USERNAME="your-username" export REDDIT_PASSWORD="your-password" export OPENROUTER_API_KEY="sk-or-v1-your-key" ``` ### 4. Initial Login ```bash # Open browser for manual login npm run login ``` ### 5. Start Development Server ```bash # Terminal 1: backend reload with tsx npm run dev # Terminal 2: Vite frontend with /api proxied to port 3000 npm run dashboard:dev # Production build: server plus apps/dashboard/dist npm run build && npm start ``` ### 6. Access Dashboard During frontend development, open the URL printed by Vite (normally http://localhost:5173). The Express dashboard remains on http://localhost:3000 for production/fallback verification. Useful frontend checks: ```bash npm run dashboard:typecheck npm run dashboard:build npx vitest run tests/dashboard ``` --- ## Coding Standards ### TypeScript - **Strict mode**: No `any` types (use `unknown` with type guards) - **Interfaces**: Define interfaces for all data structures - **Null checks**: Use optional chaining (`?.`) and nullish coalescing (`??`) - **Async/Await**: Use async/await, not `.then()` chains ```typescript // Good interface PostData { id: string; subreddit: string; author: string; } async function fetchPost(id: string): Promise { const response = await api.get(`/posts/${id}`); return response.data; } // Bad async function fetchPost(id: any): Promise { return api.get(`/posts/${id}`).then(r => r.data); } ``` ### Logging Use `winston` logger, never `console.log`: ```typescript // Good this.logger.info("Processed post {id}", { id: post.id }); this.logger.warn("AI refusal detected", { postId: post.id }); this.logger.error("Failed to send DM", { error: err.message }); // Bad console.log("Processed post", post.id); ``` ### Error Handling Use try/catch with structured error handling: ```typescript try { await this.processPost(post); } catch (err) { this.logger.error("Post processing failed", { postId: post.id, error: err instanceof Error ? err.message : String(err) }); // Don't throw - allow other posts to be processed } ``` ### Service Structure Follow single responsibility: ```typescript class NewService { constructor( private config: Config, private dependency: ExistingService, private logger: Logger ) {} async doSomething(input: string): Promise { // Validate input if (!input) { throw new Error("Input is required"); } // Do work const result = await this.dependency.process(input); // Return result return result; } } ``` ### File Organization - One class per file (except repositories which share one file) - Services in `src/services/` - Utilities in `src/utils/` - External integrations in their own folders (`src/ai/`, `src/reddit/`, `src/browser/`) --- ## Adding a New Service ### 1. Create the Service File ```typescript // src/services/new-service.ts import { Config } from '../config'; import { Logger } from '../utils/logger'; interface NewServiceDeps { config: Config; logger: Logger; // other dependencies } interface Result { success: boolean; data?: any; error?: string; } export class NewService { constructor(private deps: NewServiceDeps) {} async execute(input: string): Promise { const { config, logger } = this.deps; logger.info("NewService executing", { input }); // Implementation here return { success: true }; } } ``` ### 2. Add to Dependencies Interface If the service needs other services, add them to the interface: ```typescript interface NewServiceDeps { config: Config; logger: Logger; redditApi: RedditApi; openrouter: OpenRouterService; } ``` ### 3. Wire Up in main() In `src/index.ts`, add initialization: ```typescript const newService = new NewService({ config, logger, redditApi, openrouter }); // If it needs to run periodically: setInterval(() => { newService.execute(someInput).catch(err => { logger.error("NewService error", { error: err.message }); }); }, intervalMs); ``` ### 4. Add to the Web API and Dashboard (Optional) If the service needs an API, add a focused router under `src/web/routes/` and mount it from `src/web/server.ts`. Define its transport schema in `packages/api-contracts`; do not expose repository rows directly. ```typescript router.get('/new-service', async (req, res) => { try { const result = await newService.execute(req.query.input as string); res.json(result); } catch (err) { res.status(500).json({ error: (err as Error).message }); } }); ``` Add frontend behavior under `apps/dashboard/src/features//` and drive it with Testing Library and MSW tests under `tests/dashboard/`. ### 5. Update Config (If Needed) Add new configuration options to: - `config.yaml` - `src/config/index.ts` Zod schema - Configuration documentation --- ## Adding a New Strategy Variant ### 1. Add to config.yaml ```yaml strategyVariants: - name: "existing_variant" prompt: "Existing prompt..." enabled: true - name: "new_variant" prompt: | Write a friendly message that references the image content. Be specific and genuine. Keep it under 2 sentences. enabled: true ``` ### 2. No Code Changes Needed The `StrategySelector` automatically picks up new variants from the config. The algorithm will: 1. Include the new variant in random exploration 2. Track its response rate once messages are sent 3. Promote it to exploitation if it has good stats ### 3. Monitor Performance Check performance via the dashboard or API: ```bash curl http://localhost:3000/api/analytics/by-strategy ``` --- ## Debugging ### Browser Debugging Run with visible browser: ```yaml # config.yaml browser: headless: false ``` Or set environment variable: ```bash BROWSER_HEADLESS=false npm run dev ``` ### DOM Inspection Use the debug endpoint to inspect Reddit's shadow DOM: ```bash curl -X POST http://localhost:3000/api/debug/inspect-dom ``` Or run the standalone script: ```bash npx tsx scripts/inspect-chat-dom.ts ``` ### Logging Adjust log level by modifying `src/utils/logger.ts`: ```typescript const logger = winston.createLogger({ level: 'debug', // Change from 'info' to 'debug' // ... }); ``` ### Database Inspection Open the database with SQLite viewer: ```bash # Install sqlite3 if needed brew install sqlite3 # Open database sqlite3 data/reddit-bot.db # Query SELECT * FROM sent_messages LIMIT 5; ``` ### Network Debugging Enable axios debug: ```typescript // In src/reddit/api.ts import axios from 'axios'; axios.defaults.debug = true; ``` --- ## Common Tasks ### Add a New Subreddit ```yaml # config.yaml reddit: subreddits: - gonewild - ratemyboobs - new_subreddit_name # Add here ``` ### Change AI Model ```yaml # config.yaml openrouter: model: "new-model-name" fallbackModels: - "fallback-model-1" - "fallback-model-2" ``` ### Adjust Rate Limits ```yaml # config.yaml rateLimit: minDelayMs: 45000 # Increase delay maxMessagesPerHour: 15 # Reduce cap jitterMs: 20000 # More jitter ``` ### Enable Dry Run ```yaml # config.yaml dryRun: true # Logs all actions without sending ``` Or via dashboard: `PUT /api/config` with `{"dryRun": true}` ### Add Database Column ```typescript // In src/database/client.ts function runMigrations(db: Database) { // ... existing migrations ... // Check if column exists const tableInfo = db.prepare(`PRAGMA table_info(sent_messages)`).all(); const hasNewColumn = tableInfo.some((col: any) => col.name === 'new_column'); if (!hasNewColumn) { db.exec("ALTER TABLE sent_messages ADD COLUMN new_column TEXT"); } } ``` ### Add New API Endpoint ```typescript // In src/web/server.ts import { Request, Response } from 'express'; server.app.get('/api/new-endpoint', async (req: Request, res: Response) => { try { const result = await someService.doSomething(); res.json({ success: true, data: result }); } catch (err) { res.status(500).json({ error: (err as Error).message }); } }); ``` --- ## Known Issues ### Hardcoded Username in ChatService `ChatService` has `myUsername = 'Upset_Marsupial1006'` hardcoded in JavaScript evaluation scripts. This should be parameterized from config. **Fix needed**: Pass username to `page.evaluate()` calls. ### Shadow DOM Fragility Reddit frequently updates their `faceplate-*` components. Shadow DOM traversal code may break without warning. **Mitigation**: Use `scripts/inspect-chat-dom.ts` to debug when selectors change. ### Dashboard Migration Boundary The React application lives in `apps/dashboard`. A production build is required before the Express server starts; there is no legacy HTML fallback. The repository has automated service, contract, component, and transport tests. Manual browser verification is still required for Reddit DOM changes and the final dashboard cutover.