- Sprint 1 (Quick Wins): Remove dead analytics modules, fix 4 unresolved imports, replace 3 console.warn with logger, remove mock-crc import - Sprint 2 (Architecture): Create MascotChatRepository, AnalysisRepository, 3 Zod schemas (mascot-chat, analysis, voice), deduplicate error classes, move 3 SQL queries from routes to repository - Sprint 3 (Complexity): Replace 7 any types with proper interfaces, extract 6 helpers from prepareMediaMessage (CC 85 -> ~15) - Sprint 4 (Config): Remove 22 dead env vars from .env, add 30 missing vars to .env.example, standardize naming Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
364 lines
16 KiB
Markdown
364 lines
16 KiB
Markdown
# 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
|
|
1. Discord emits `messageCreate`, `messageUpdate`, `messageDelete` events
|
|
2. `messageCapture.ts` listener receives and validates event
|
|
3. Extract metadata: user, channel, content, timestamp, attachments
|
|
4. `messageStore.ts` inserts into PostgreSQL
|
|
5. `eventBroadcaster.messageCreated()` publishes to Redis
|
|
6. Backend service subscribes and processes
|
|
|
|
### AI Moderation
|
|
1. `aiAnalyzer.ts` queues messages for analysis
|
|
2. `llmModerationClient.ts` calls LLM API with context
|
|
3. `indonesianTextNormalizer.ts` preprocesses text
|
|
4. Results stored in database
|
|
5. `eventBroadcaster.messageAnalyzed()` publishes results
|
|
6. Backend service receives and updates UI
|
|
|
|
### Voice Recording
|
|
1. `voiceController.connect()` joins voice channel
|
|
2. `recorder.ts` subscribes to user audio streams
|
|
3. For each speaking user:
|
|
- `audioStream.ts` subscribes to Opus packets
|
|
- `decoder.ts` decodes Opus to PCM
|
|
- `segment.ts` rotates OGG files (5s default)
|
|
- `metadata.ts` collects user info
|
|
4. On silence (3s):
|
|
- `sessionRecording.ts` finalizes segment
|
|
- `uploader.ts` uploads to storage
|
|
- `eventBroadcaster.voiceRecordingUploaded()` publishes
|
|
5. Backend service indexes recording
|
|
|
|
### Attachment Upload
|
|
1. `messageCapture.ts` detects attachments
|
|
2. `attachmentUploader.ts` downloads from Discord
|
|
3. `imageResizer.ts` resizes images if needed
|
|
4. Upload to external storage with retry logic
|
|
5. `eventBroadcaster.attachmentUploaded()` publishes
|
|
6. 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:
|
|
1. Close PostgreSQL connection
|
|
2. Disconnect from voice channels
|
|
3. Close Redis connection
|
|
4. Destroy Discord client
|
|
5. 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/decoding
|
|
- `opusscript` — Opus fallback for Node v26+
|
|
- `sharp` — Image resizing
|
|
|
|
**Data & Config**:
|
|
- `drizzle-orm` — Type-safe ORM
|
|
- `pg` — PostgreSQL driver
|
|
- `zod` — Config validation
|
|
- `ioredis` — Redis client
|
|
|
|
**Logging & Utilities**:
|
|
- `winston` — Structured logging
|
|
- `p-retry` — Retry with backoff
|
|
- `p-limit` — Concurrency limiting
|
|
- `piscina` — 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
|
|
|
|
1. **Create Backend service** (`services/backend/`)
|
|
- HTTP API endpoints
|
|
- Redis event subscribers
|
|
- Database models
|
|
- WebSocket broadcaster
|
|
|
|
2. **Update Frontend** (`frontend/`)
|
|
- Connect to Backend HTTP API
|
|
- Subscribe to WebSocket events
|
|
|
|
3. **Docker & CI/CD**
|
|
- Dockerfile for Discord Gateway
|
|
- Docker Compose for multi-service setup
|
|
- GitHub Actions for build/deploy
|
|
|
|
4. **Documentation**
|
|
- API documentation
|
|
- Event schema documentation
|
|
- Deployment guide
|
|
|
|
## Files Created
|
|
|
|
**Total: 43 files**
|
|
|
|
### Shared Infrastructure (9 files)
|
|
- `src/shared/config/config.ts`
|
|
- `src/shared/database/` (5 files)
|
|
- `@bete/shared/errors` (shared package)
|
|
- `src/shared/logger/logger.ts`
|
|
- `src/shared/logger/serialization.ts`
|
|
- `src/shared/utils/retry.ts`
|
|
- `src/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.ts`
|
|
- `src/app/shutdown.ts`
|
|
- `src/index.ts`
|
|
- `src/mock-crc.ts`
|
|
|
|
### Configuration (2 files)
|
|
- `package.json`
|
|
- `ARCHITECTURE.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.
|