Files
RedditBot/docs/api.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

1003 lines
15 KiB
Markdown

# API Reference
Complete reference for the RedditBot web dashboard API.
---
## Table of Contents
1. [Overview](#overview)
2. [Authentication](#authentication)
3. [Base URL](#base-url)
4. [Bot Status & Control](#bot-status--control)
5. [Activity & Messages](#activity--messages)
6. [Chat & Conversations](#chat--conversations)
7. [Media](#media)
8. [Analytics](#analytics)
9. [Configuration](#configuration)
10. [Logs & Debugging](#logs--debugging)
11. [Error Responses](#error-responses)
12. [SSE Events](#sse-events)
---
## Overview
The RedditBot web dashboard exposes a REST API for monitoring and controlling the bot. All endpoints are served by the Express server.
**Content-Type**: `application/json` (except SSE)
**Response Format**: JSON
**Authentication**: None (local network only)
---
## Authentication
No authentication is required. The dashboard is intended for local network access only.
> **Security Note**: If exposing the dashboard externally, add authentication (e.g., basic auth, reverse proxy with auth).
---
## Base URL
```
http://localhost:3000/api
```
The port is configured via `config.web.port` (default: 3000).
---
## Bot Status & Control
### Get Bot Statistics
Retrieve current bot statistics including running state, counts, and response rate.
```
GET /api/stats
```
**Response**:
```json
{
"running": true,
"processed": 1250,
"sent": 980,
"responded": 312,
"responseRate": 0.318,
"hourlyCount": 15,
"timeUntilSlot": 45
}
```
**Fields**:
| Field | Type | Description |
|---|---|---|
| `running` | boolean | Whether the bot is currently running |
| `processed` | number | Total posts processed |
| `sent` | number | Total messages sent |
| `responded` | number | Total messages that received responses |
| `responseRate` | number | Response rate (0.0 to 1.0) |
| `hourlyCount` | number | Messages sent this hour |
| `timeUntilSlot` | number | Seconds until next message slot |
---
### Start Bot
Start the bot's main processing loop.
```
POST /api/start
```
**Response**:
```json
{
"success": true,
"message": "Bot started"
}
```
**Errors**:
```json
{
"error": "Bot is already running"
}
```
---
### Stop Bot
Stop the bot's main processing loop gracefully.
```
POST /api/stop
```
**Response**:
```json
{
"success": true,
"message": "Bot stopping..."
}
```
---
## Activity & Messages
### Get Activity Log
Retrieve recent activity entries from the bot's log.
```
GET /api/activity
```
**Query Parameters**:
| Parameter | Type | Default | Description |
|---|---|---|---|
| `limit` | number | 50 | Number of entries to return |
**Response**:
```json
[
{
"level": "info",
"message": "Processed post abc123 from r/gonewild",
"timestamp": "2024-01-15T10:30:00.000Z"
},
{
"level": "warn",
"message": "AI model refused to analyze post def456",
"timestamp": "2024-01-15T10:28:00.000Z"
},
{
"level": "error",
"message": "Failed to send DM to user789: timeout",
"timestamp": "2024-01-15T10:25:00.000Z"
}
]
```
---
### Get Posts by Author
Look up processed posts for a specific Reddit user.
```
GET /api/posts/by-author/:username
```
**Path Parameters**:
| Parameter | Type | Description |
|---|---|---|
| `username` | string | Reddit username |
**Response**:
```json
[
{
"id": "abc123",
"subreddit": "gonewild",
"author": "user123",
"title": "Summer vibes",
"image_url": "https://...",
"image_analysis": "{\"gender\":\"F\",\"description\":\"...\"}",
"generated_message": "Hey, you look amazing...",
"processed_at": 1716234567,
"message_sent": 1,
"error": null
}
]
```
---
### Get Sent Messages
Retrieve recent sent messages.
```
GET /api/messages
```
**Query Parameters**:
| Parameter | Type | Default | Description |
|---|---|---|---|
| `limit` | number | 50 | Number of messages to return |
**Response**:
```json
[
{
"id": 1,
"post_id": "abc123",
"recipient": "user123",
"subject": "Stunning",
"message": "Hey, you look amazing...",
"sent_at": "2024-01-15T10:30:00.000Z",
"strategy_id": "compliment",
"got_response": 1,
"response_checked_at": "2024-01-15T11:00:00.000Z"
}
]
```
---
### Get Successful Messages
Retrieve successful message/response pairs used for few-shot learning.
```
GET /api/successful
```
**Query Parameters**:
| Parameter | Type | Default | Description |
|---|---|---|---|
| `limit` | number | 50 | Number of entries to return |
**Response**:
```json
[
{
"id": 1,
"sent_message_id": 1,
"original_message": "Hey, you look amazing...",
"image_analysis": "{\"gender\":\"F\",\"description\":\"...\"}",
"post_title": "Summer vibes",
"response_snippet": "Thank you so much! 😊",
"subreddit": "gonewild",
"created_at": "2024-01-15T10:30:00.000Z"
}
]
```
---
## Chat & Conversations
### Get Active Chats
Retrieve cached active conversations.
```
GET /api/chats
```
**Response**:
```json
[
{
"chatId": "chat_abc123",
"participant": "user456",
"lastMessage": "Thanks for the compliment!",
"timestamp": "2024-01-15T10:30:00.000Z",
"hasNewMessages": false
}
]
```
---
### Fetch All Chats
Force-fetch all chats from Reddit (refreshes cache).
```
POST /api/chats/fetch-all
```
**Response**:
```json
{
"success": true,
"count": 15
}
```
---
### Get Conversations
Retrieve all cached conversations.
```
GET /api/conversations
```
**Response**:
```json
[
{
"chatId": "chat_abc123",
"participants": ["user456", "bot"],
"messageCount": 24,
"lastUpdated": "2024-01-15T10:30:00.000Z",
"hasNewMessages": false,
"pendingReply": false,
"isUpsell": false
}
]
```
---
### Get Conversation Detail
Retrieve full details of a specific conversation.
```
GET /api/conversations/:id
```
**Path Parameters**:
| Parameter | Type | Description |
|---|---|---|
| `id` | string | Chat ID |
**Response**:
```json
{
"chatId": "chat_abc123",
"participants": ["user456", "bot"],
"messages": [
{
"author": "user456",
"text": "Hey there!",
"timestamp": "2024-01-15T10:00:00.000Z"
},
{
"author": "bot",
"text": "Hi! Thanks for the compliment!",
"timestamp": "2024-01-15T10:01:00.000Z"
},
{
"author": "user456",
"text": "Thanks for the compliment!",
"timestamp": "2024-01-15T10:02:00.000Z"
}
],
"lastUpdated": "2024-01-15T10:30:00.000Z",
"hasNewMessages": false,
"pendingReply": false,
"isUpsell": false
}
```
---
### Suggest Reply
Generate an AI reply suggestion for a conversation.
```
POST /api/conversations/:id/suggest-reply
```
**Path Parameters**:
| Parameter | Type | Description |
|---|---|---|
| `id` | string | Chat ID |
**Response**:
```json
{
"success": true,
"reply": "Thanks for reaching out! 😊 How's your day going?"
}
```
---
### Send Reply
Send a manual reply in a conversation.
```
POST /api/conversations/:id/send-reply
```
**Path Parameters**:
| Parameter | Type | Description |
|---|---|---|
| `id` | string | Chat ID |
**Request Body**:
```json
{
"text": "Thanks for reaching out! 😊"
}
```
**Response**:
```json
{
"success": true,
"delivered": true
}
```
---
### Approve Pending Reply
Approve an auto-reply that is pending manual approval.
```
POST /api/conversations/:id/approve-reply
```
**Path Parameters**:
| Parameter | Type | Description |
|---|---|---|
| `id` | string | Chat ID |
**Response**:
```json
{
"success": true,
"message": "Reply sent"
}
```
---
### Reject Pending Reply
Reject an auto-reply that is pending manual approval.
```
POST /api/conversations/:id/reject-reply
```
**Path Parameters**:
| Parameter | Type | Description |
|---|---|---|
| `id` | string | Chat ID |
**Response**:
```json
{
"success": true,
"message": "Reply rejected"
}
```
---
### Dismiss Upsell Flag
Clear the upsell/spam flag on a conversation.
```
POST /api/conversations/:id/dismiss-upsell
```
**Path Parameters**:
| Parameter | Type | Description |
|---|---|---|
| `id` | string | Chat ID |
**Response**:
```json
{
"success": true,
"message": "Upsell flag dismissed"
}
```
---
## Media
### Get Media Gallery
Retrieve downloaded media files.
```
GET /api/media
```
**Query Parameters**:
| Parameter | Type | Default | Description |
|---|---|---|---|
| `limit` | number | 100 | Number of files to return |
**Response**:
```json
[
{
"hash": "abc123def456",
"filename": "abc123def456.jpg",
"size": 245678,
"conversationId": "chat_abc123",
"downloadedAt": "2024-01-15T10:30:00.000Z"
}
]
```
---
### Get Media Sync Status
Retrieve current media sync status.
```
GET /api/media/sync
```
**Response**:
```json
{
"running": false,
"mode": null,
"progress": 0,
"total": 0,
"completed": 0,
"lastSync": "2024-01-15T09:00:00.000Z"
}
```
---
### Trigger Incremental Sync
Start an incremental media sync (top 10 conversations).
```
POST /api/media/sync
```
**Response**:
```json
{
"success": true,
"message": "Incremental sync started"
}
```
---
### Trigger Full Sync
Start a full media sync (all conversations).
```
POST /api/media/sync/full
```
**Response**:
```json
{
"success": true,
"message": "Full sync started"
}
```
---
## Analytics
### Response Rate by Subreddit
Get response rates broken down by subreddit.
```
GET /api/analytics/by-subreddit
```
**Response**:
```json
[
{
"subreddit": "gonewild",
"sent": 500,
"responded": 175,
"rate": 0.35
},
{
"subreddit": "ratemyboobs",
"sent": 300,
"responded": 90,
"rate": 0.30
},
{
"subreddit": "HomemadeNsfw",
"sent": 180,
"responded": 72,
"rate": 0.40
}
]
```
---
### Response Rate by Strategy
Get response rates broken down by A/B test variant.
```
GET /api/analytics/by-strategy
```
**Response**:
```json
[
{
"strategy_id": "compliment",
"sent": 400,
"responded": 140,
"rate": 0.35
},
{
"strategy_id": "question",
"sent": 350,
"responded": 147,
"rate": 0.42
},
{
"strategy_id": "observation",
"sent": 230,
"responded": 69,
"rate": 0.30
}
]
```
---
### Daily Response Trend
Get daily response rates over time.
```
GET /api/analytics/trend
```
**Response**:
```json
[
{
"date": "2024-01-01",
"sent": 18,
"responded": 6,
"rate": 0.33
},
{
"date": "2024-01-02",
"sent": 22,
"responded": 8,
"rate": 0.36
},
{
"date": "2024-01-03",
"sent": 15,
"responded": 4,
"rate": 0.27
}
]
```
---
## Configuration
### Get Runtime Configuration
Get current runtime configuration.
```
GET /api/config
```
**Response**:
```json
{
"reddit": {
"subreddits": ["gonewild", "ratemyboobs"],
"pollInterval": 15,
"targetGender": "F",
"sellerKeywords": ["seller", "onlyfans"]
},
"openrouter": {
"model": "meta-llama/llama-3.2-90b-vision-preview",
"fallbackModels": ["google/gemini-2.0-flash-001"]
},
"rateLimit": {
"minDelayMs": 30000,
"maxMessagesPerHour": 20,
"jitterMs": 15000
},
"browser": {
"headless": true,
"userDataDir": "./data/browser"
},
"database": {
"path": "./data/reddit-bot.db"
},
"mediaSync": {
"enabled": true,
"intervalMinutes": 30
},
"autoReply": {
"enabled": true,
"autoSend": false,
"checkInterval": 10
},
"strategyVariants": [
{
"name": "compliment",
"enabled": true
}
],
"web": {
"port": 3000
},
"dryRun": false,
"fewShotExamples": 3,
"minStrategySamples": 10,
"explorationRate": 0.15
}
```
---
### Update Runtime Configuration
Update runtime configuration values without restarting.
```
PUT /api/config
```
**Request Body** (partial updates supported):
```json
{
"rateLimit": {
"minDelayMs": 45000,
"maxMessagesPerHour": 15
},
"dryRun": true,
"reddit": {
"pollInterval": 20
}
}
```
**Response**:
```json
{
"success": true,
"config": { ... }
}
```
**Errors**:
```json
{
"error": "Invalid config: rateLimit.minDelayMs must be a positive integer"
}
```
---
## Logs & Debugging
### SSE Log Stream
Connect to the realtime log stream via Server-Sent Events.
```
GET /api/logs/stream
```
**Headers**:
| Header | Value |
|---|---|
| Accept | `text/event-stream` |
**Response** (streaming):
```
event: log
data: {"level":"info","message":"Bot started","timestamp":"2024-01-15T10:30:00.000Z"}
event: log
data: {"level":"info","message":"Processed post abc123","timestamp":"2024-01-15T10:30:05.000Z"}
event: log
data: {"level":"warn","message":"AI refusal detected","timestamp":"2024-01-15T10:30:10.000Z"}
```
**Client usage (JavaScript)**:
```javascript
const eventSource = new EventSource('/api/logs/stream');
eventSource.onmessage = (event) => {
const log = JSON.parse(event.data);
console.log(log.level, log.message);
};
// Close connection
eventSource.close();
```
---
### Get Recent Logs
Retrieve recent log entries via HTTP (non-streaming).
```
GET /api/logs
```
**Query Parameters**:
| Parameter | Type | Default | Description |
|---|---|---|---|
| `limit` | number | 100 | Number of entries to return |
| `level` | string | all | Filter by level: `info`, `warn`, `error` |
**Response**: Same format as `/api/activity`.
---
### Get Rate Limiter Timers
Get current rate limiter status.
```
GET /api/timers
```
**Response**:
```json
{
"timeUntilSlot": 45,
"hourlyCount": 15,
"maxMessagesPerHour": 20,
"isThrottled": false
}
```
---
### Debug: Inspect DOM
Inspect the current chat DOM structure (for debugging shadow DOM issues).
```
POST /api/debug/inspect-dom
```
**Response**:
```json
{
"success": true,
"domStructure": { ... }
}
```
---
### Debug: Manual Response Check
Trigger a manual response check (bypasses scheduled interval).
```
POST /api/debug/check-responses
```
**Response**:
```json
{
"success": true,
"responsesFound": 3,
"updated": 2
}
```
---
## Error Responses
All error responses follow this format:
```json
{
"error": "Error message describing what went wrong"
}
```
### Common Error Codes
| HTTP Status | Description | Example |
|---|---|---|
| 400 | Bad Request | Invalid query parameter |
| 404 | Not Found | Resource not found |
| 500 | Internal Server Error | Unexpected error |
---
## SSE Events
The SSE log stream (`/api/logs/stream`) emits the following events:
### `log` Event
Emitted for every log entry.
**Data Format**:
```json
{
"level": "info",
"message": "Processed post abc123 from r/gonewild",
"timestamp": "2024-01-15T10:30:00.000Z"
}
```
**Log Levels**:
| Level | Description |
|---|---|
| `info` | Normal operation |
| `warn` | Warning (recoverable issue) |
| `error` | Error (action failed) |
| `debug` | Debug information |