This commit is contained in:
jjbrunton committed 2026-01-04 19:24:56 +00:00
1 parent c552956516
commit 0c5ffc5001
5 files changed
+88 -7

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
node_modules
dist
.git
.env
data/browser-session
data/reddit-bot.db
data/reddit-bot.db-journal
test-results
playwright-report
blob-report
+30
View File
@@ -0,0 +1,30 @@
# Use the official Playwright image which includes all browser dependencies
FROM mcr.microsoft.com/playwright:v1.57.0-jammy
# Set working directory
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies (including devDependencies for building)
RUN npm ci
# Copy source code
COPY . .
# Build the TypeScript project
RUN npm run build
# Prune dev dependencies to reduce image size (optional, but good practice)
# Note: we need to keep some deps if they are runtime requirements
# npm prune --production
# Create data directory
RUN mkdir -p data
# Expose the port defined in config (default 3000)
EXPOSE 3000
# Start the application using the compiled code
CMD ["node", "dist/index.js"]
+17
View File
@@ -0,0 +1,17 @@
version: '3.8'
services:
reddit-bot:
build: .
container_name: reddit-bot
restart: unless-stopped
# Mount the data directory to persist the database and browser session
volumes:
- ./data:/app/data
# Environment variables
environment:
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
# You can add other overrides if supported by the app or if you modify config loading
# - HEADLESS=true
ports:
- "3000:3000"
+6
View File
@@ -56,6 +56,12 @@ export function loadConfig(): Config {
} }
rawConfig.openrouter.apiKey = process.env.OPENROUTER_API_KEY || rawConfig.openrouter.apiKey; rawConfig.openrouter.apiKey = process.env.OPENROUTER_API_KEY || rawConfig.openrouter.apiKey;
// Override browser settings from environment
if (process.env.BROWSER_HEADLESS !== undefined) {
if (!rawConfig.browser) rawConfig.browser = {};
rawConfig.browser.headless = process.env.BROWSER_HEADLESS === 'true';
}
if (!rawConfig.openrouter.apiKey) { if (!rawConfig.openrouter.apiKey) {
throw new Error('OPENROUTER_API_KEY environment variable is required'); throw new Error('OPENROUTER_API_KEY environment variable is required');
} }
+25 -7
View File
@@ -30,10 +30,8 @@ export class WebServer {
private app: express.Application; private app: express.Application;
private server: Server | null = null; private server: Server | null = null;
private activityLog: ActivityItem[] = []; private activityLog: ActivityItem[] = [];
private botStats = { private botRunning = false; // Start stopped
isRunning: true, private messagesThisHour = 0;
messagesThisHour: 0,
};
constructor( constructor(
private port: number, private port: number,
@@ -75,25 +73,45 @@ export class WebServer {
res.json(successful); res.json(successful);
}); });
// API: Start bot
this.app.post('/api/start', (req, res) => {
this.botRunning = true;
this.logActivity('sent', 'Bot started by user');
this.logger.info('Bot started via web UI');
res.json({ success: true, running: true });
});
// API: Stop bot
this.app.post('/api/stop', (req, res) => {
this.botRunning = false;
this.logActivity('skipped', 'Bot stopped by user');
this.logger.info('Bot stopped via web UI');
res.json({ success: true, running: false });
});
// Serve dashboard for all other routes // Serve dashboard for all other routes
this.app.get('*', (req, res) => { this.app.get('*', (req, res) => {
res.sendFile(path.join(process.cwd(), 'public', 'index.html')); res.sendFile(path.join(process.cwd(), 'public', 'index.html'));
}); });
} }
isRunning(): boolean {
return this.botRunning;
}
private getStats(): BotStats { private getStats(): BotStats {
const messages = this.messagesRepo.getRecent(1000); const messages = this.messagesRepo.getRecent(1000);
const totalSent = messages.length; const totalSent = messages.length;
const totalResponses = messages.filter((m) => m.gotResponse).length; const totalResponses = messages.filter((m) => m.gotResponse).length;
return { return {
isRunning: this.botStats.isRunning, isRunning: this.botRunning,
dryRunMode: this.config.browser.dryRun, dryRunMode: this.config.browser.dryRun,
totalProcessed: this.getTotalProcessed(), totalProcessed: this.getTotalProcessed(),
totalMessagesSent: totalSent, totalMessagesSent: totalSent,
totalResponses, totalResponses,
responseRate: totalSent > 0 ? Math.round((totalResponses / totalSent) * 100) : 0, responseRate: totalSent > 0 ? Math.round((totalResponses / totalSent) * 100) : 0,
messagesThisHour: this.botStats.messagesThisHour, messagesThisHour: this.messagesThisHour,
maxMessagesPerHour: this.config.rateLimit.maxMessagesPerHour, maxMessagesPerHour: this.config.rateLimit.maxMessagesPerHour,
subreddits: this.config.reddit.subreddits, subreddits: this.config.reddit.subreddits,
targetGenders: this.config.reddit.targetGenders, targetGenders: this.config.reddit.targetGenders,
@@ -122,7 +140,7 @@ export class WebServer {
} }
updateStats(messagesThisHour: number): void { updateStats(messagesThisHour: number): void {
this.botStats.messagesThisHour = messagesThisHour; this.messagesThisHour = messagesThisHour;
} }
start(): void { start(): void {