Replace the corrections/adaptive-prompt-tuner feature with a new dashboard module providing server stats and user profile overview. Backend: - Add dashboard module (routes, service, repository) with stats + user list + user detail endpoints - Remove corrections module entirely - Wire dashboard router in app.ts Frontend: - Add dashboard feature (DashboardStats, UserSummaryList, UserProfileDetail components + useDashboard hook) - Remove tuner feature (CorrectionStats, CorrectionHistory, SubmitCorrection, useCorrections) - Update API client from corrections → dashboard types/fns - Rename tab 'tuner' → 'dashboard' - Update MobileTabBar, Header, Sidebar links Tests: - Expand backend placeholder test with dashboard assertions - Expand discord-gateway placeholder test with config/channel assertions AI moderation: - llmModerationClient: improve status/reply detection, expand safety categories, fix timer reset - userProfileLearner: fix isReply refinement - userProfileStore: add pending cache check - messageMetadata: add crosspost type mapping - migrate.ts: improve partial-index safety in schema push Co-Authored-By: Claude <noreply@anthropic.com>
Discord Gateway Service - Extraction Complete
Overview
Successfully extracted Discord Gateway service with Modular MVC + Event-Driven Architecture using Redis pub/sub for inter-service communication.
Directory Structure
services/discord-gateway/
├── src/
│ ├── app/
│ │ ├── bootstrap.ts # Service initialization (Discord client, DB, Redis)
│ │ └── shutdown.ts # Graceful shutdown handler
│ ├── shared/ # Shared infrastructure layer
│ │ ├── config/
│ │ │ └── config.ts # Zod-validated environment config
│ │ ├── database/
│ │ │ ├── schema.ts # Drizzle ORM schema
│ │ │ ├── drizzle.ts # PostgreSQL connection
│ │ │ ├── migrate.ts # Migration runner
│ │ │ └── voiceRecordingRepo.ts
│ │ ├── errors/
│ │ │ └── errors.ts # Custom error classes
│ │ ├── logger/
│ │ │ ├── logger.ts # Winston logger wrapper
│ │ │ └── serialization.ts # Log serialization
│ │ ├── utils/
│ │ │ └── retry.ts # Retry with exponential backoff
│ │ └── discord/
│ │ └── clientOptions.ts # Discord.js client config
│ ├── modules/ # Feature modules (Modular MVC)
│ │ ├── message-capture/ # Controller-Service-Repository
│ │ │ ├── messageCapture.ts # Controller: Discord event listeners
│ │ │ ├── messageStore.ts # Repository: DB operations
│ │ │ ├── messageMetadata.ts # Service: Metadata extraction
│ │ │ ├── types.ts # Domain types
│ │ │ └── index.ts # Module exports
│ │ ├── ai-moderation/ # Controller-Service-Repository
│ │ │ ├── aiAnalyzer.ts # Controller: Analysis orchestration
│ │ │ ├── llmModerationClient.ts # Service: LLM API client
│ │ │ ├── aiAnalysisWorker.ts # Service: Worker pool
│ │ │ ├── indonesianTextNormalizer.ts # Service: Text normalization
│ │ │ ├── moderationPrompt.ts # Service: Prompt generation
│ │ │ └── index.ts # Module exports
│ │ ├── voice-recording/ # Controller-Service-Repository
│ │ │ ├── voiceController.ts # Controller: Voice connection mgmt
│ │ │ ├── recorder.ts # Service: Recording orchestration
│ │ │ ├── recorder/ # Sub-services
│ │ │ │ ├── audioStream.ts # Audio stream subscription
│ │ │ │ ├── decoder.ts # Opus decoding
│ │ │ │ ├── segment.ts # OGG segment rotation
│ │ │ │ ├── metadata.ts # Segment metadata
│ │ │ │ ├── sessionRecording.ts # Session management
│ │ │ │ └── uploader.ts # Segment upload
│ │ │ └── index.ts # Module exports
│ │ ├── attachment-upload/ # Controller-Service-Repository
│ │ │ ├── attachmentUploader.ts # Service: Upload orchestration
│ │ │ ├── imageResizer.ts # Service: Image resizing
│ │ │ └── index.ts # Module exports
│ │ └── event-broadcaster/ # Event-driven layer
│ │ ├── eventBroadcaster.ts # Service: Redis pub/sub publisher
│ │ ├── eventTypes.ts # Domain: Event type definitions
│ │ └── index.ts # Module exports
│ ├── mock-crc.ts # CRC polyfill for discord.js
│ └── index.ts # Service entry point
├── ARCHITECTURE.md # Detailed architecture documentation
├── package.json # Service dependencies
└── tsconfig.json # TypeScript configuration (inherited)
Architecture Patterns
1. Modular MVC Structure
Each feature module follows Controller-Service-Repository pattern:
Message Capture Module:
- Controller (
messageCapture.ts): Listens to Discord events (messageCreate, messageUpdate, messageDelete) - Service (
messageMetadata.ts): Extracts and normalizes message metadata - Repository (
messageStore.ts): Database CRUD operations
AI Moderation Module:
- Controller (
aiAnalyzer.ts): Orchestrates analysis workflow - Service (
llmModerationClient.ts): LLM API integration - Service (
aiAnalysisWorker.ts): Worker pool management - Service (
indonesianTextNormalizer.ts): Text preprocessing
Voice Recording Module:
- Controller (
voiceController.ts): Voice channel connection management - Service (
recorder.ts): Recording orchestration - Sub-services (
recorder/*): Audio stream, decoding, segmentation, upload
Attachment Upload Module:
- Service (
attachmentUploader.ts): Upload orchestration - Service (
imageResizer.ts): Image processing
2. Event-Driven Architecture
Redis Pub/Sub replaces WebSocket broadcaster:
Discord Events → Discord Gateway Service → Redis Pub/Sub → Backend Service
↓
Event Channels:
- discord:message:created
- discord:message:updated
- discord:message:deleted
- discord:message:analyzed
- discord:attachment:created
- discord:attachment:uploaded
- discord:voice:started
- discord:voice:stopped
- discord:voice:uploaded
- discord:analysis:queue_status
3. Shared Infrastructure Layer
Centralized, reusable components:
- Config: Zod-validated environment variables
- Logger: Winston logger with context support
- Database: Drizzle ORM with PostgreSQL
- Errors: Custom error classes with codes and HTTP status codes
- Utils: Retry logic with exponential backoff
- Discord: Client configuration and options
4. No HTTP Server
- Event-driven only: No Express, WebSocket, or HTTP routes
- Redis pub/sub: All inter-service communication via Redis
- Backend service: Consumes events and serves HTTP API
- Frontend: Continues to use Backend HTTP API
Key Features
Message Capture
- Discord emits
messageCreate,messageUpdate,messageDeleteevents messageCapture.tslistener receives and validates event- Extract metadata: user, channel, content, timestamp, attachments
messageStore.tsinserts into PostgreSQLeventBroadcaster.messageCreated()publishes to Redis- Backend service subscribes and processes
AI Moderation
aiAnalyzer.tsqueues messages for analysisllmModerationClient.tscalls LLM API with contextindonesianTextNormalizer.tspreprocesses text- Results stored in database
eventBroadcaster.messageAnalyzed()publishes results- Backend service receives and updates UI
Voice Recording
voiceController.connect()joins voice channelrecorder.tssubscribes to user audio streams- For each speaking user:
audioStream.tssubscribes to Opus packetsdecoder.tsdecodes Opus to PCMsegment.tsrotates OGG files (5s default)metadata.tscollects user info
- On silence (3s):
sessionRecording.tsfinalizes segmentuploader.tsuploads to storageeventBroadcaster.voiceRecordingUploaded()publishes
- Backend service indexes recording
Attachment Upload
messageCapture.tsdetects attachmentsattachmentUploader.tsdownloads from DiscordimageResizer.tsresizes images if needed- Upload to external storage with retry logic
eventBroadcaster.attachmentUploaded()publishes- Backend service stores metadata
Initialization Flow
1. Load environment config (Zod validation)
↓
2. Initialize PostgreSQL connection
↓
3. Run pending database migrations
↓
4. Create Discord client with optimized cache
↓
5. Initialize Redis event broadcaster
↓
6. Register Discord event listeners
- messageCapture (message events)
- aiAnalyzer (analysis worker)
↓
7. Login to Discord
↓
8. Listen for graceful shutdown signals
Graceful Shutdown
On SIGINT/SIGTERM/uncaughtException/unhandledRejection:
- Close PostgreSQL connection
- Disconnect from voice channels
- Close Redis connection
- Destroy Discord client
- Exit process (code 0 for clean, 1 for error)
Dependencies
Core Discord:
discord.js-selfbot-v13— Discord client (selfbot variant)@discordjs/voice— Voice connection management@discordjs/opus— Native Opus codec
Audio Processing:
prism-media— Opus encoding/decodingopusscript— Opus fallback for Node v26+sharp— Image resizing
Data & Config:
drizzle-orm— Type-safe ORMpg— PostgreSQL driverzod— Config validationioredis— Redis client
Logging & Utilities:
winston— Structured loggingp-retry— Retry with backoffp-limit— Concurrency limitingpiscina— Worker pool
No Breaking Changes
- Original
src/remains untouched - Discord Gateway is a new service in
services/discord-gateway/ - Can run alongside existing monolith during transition
- Backend service will consume Redis events
- Frontend continues to use Backend HTTP API
Next Steps
-
Create Backend service (
services/backend/)- HTTP API endpoints
- Redis event subscribers
- Database models
- WebSocket broadcaster
-
Update Frontend (
frontend/)- Connect to Backend HTTP API
- Subscribe to WebSocket events
-
Docker & CI/CD
- Dockerfile for Discord Gateway
- Docker Compose for multi-service setup
- GitHub Actions for build/deploy
-
Documentation
- API documentation
- Event schema documentation
- Deployment guide
Files Created
Total: 43 files
Shared Infrastructure (9 files)
src/shared/config/config.tssrc/shared/database/(5 files)@bete/shared/errors(shared package)src/shared/logger/logger.tssrc/shared/logger/serialization.tssrc/shared/utils/retry.tssrc/shared/discord/clientOptions.ts
Modules (28 files)
src/modules/message-capture/(5 files)src/modules/ai-moderation/(6 files)src/modules/voice-recording/(9 files)src/modules/attachment-upload/(3 files)src/modules/event-broadcaster/(3 files)
App & Entry (4 files)
src/app/bootstrap.tssrc/app/shutdown.tssrc/index.tssrc/mock-crc.ts
Configuration (2 files)
package.jsonARCHITECTURE.md
Verification Checklist
✅ Directory structure created ✅ Shared infrastructure migrated ✅ Message capture module migrated ✅ AI moderation module migrated ✅ Voice recording module migrated ✅ Attachment upload module migrated ✅ Event broadcaster module created (Redis pub/sub) ✅ Bootstrap and entry point created ✅ Package.json with dependencies ✅ No HTTP server code (Express, WebSocket removed) ✅ Event-driven architecture implemented ✅ Graceful shutdown handler ✅ Module index files for clean exports ✅ Architecture documentation
Event Flow Diagram
┌─────────────────────────────────────────────────────────────────┐
│ Discord Gateway Service │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────┐ │
│ │ Message Capture │ │ AI Moderation │ │ Voice Record │ │
│ │ (Controller) │ │ (Controller) │ │ (Controller) │ │
│ └────────┬─────────┘ └────────┬─────────┘ └──────┬───────┘ │
│ │ │ │ │
│ ├─────────────────────┼───────────────────┤ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Event Broadcaster (Redis Pub/Sub) │ │
│ │ - discord:message:created │ │
│ │ - discord:message:updated │ │
│ │ - discord:message:deleted │ │
│ │ - discord:message:analyzed │ │
│ │ - discord:attachment:created │ │
│ │ - discord:attachment:uploaded │ │
│ │ - discord:voice:started │ │
│ │ - discord:voice:stopped │ │
│ │ - discord:voice:uploaded │ │
│ │ - discord:analysis:queue_status │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
└───────────┼────────────────────────────────────────────────────┘
│
│ Redis Pub/Sub
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Backend Service │
│ (Subscribes to events, serves HTTP API, manages WebSocket) │
└─────────────────────────────────────────────────────────────────┘
│
│ HTTP API
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Frontend Application │
│ (React SPA, real-time updates via WebSocket) │
└─────────────────────────────────────────────────────────────────┘
Summary
The Discord Gateway service has been successfully extracted with:
- Modular MVC architecture for clean separation of concerns
- Event-driven design using Redis pub/sub for inter-service communication
- Shared infrastructure layer for reusable components
- No HTTP server — pure event-driven service
- Graceful shutdown handling
- Type-safe configuration with Zod validation
- Structured logging with Winston
- PostgreSQL integration with Drizzle ORM
The service is ready for integration with the Backend service, which will consume Redis events and serve the HTTP API to the Frontend.