feat: implement all real backend endpoints, Redis pub/sub bridge, and gateway command handler
Backend (real implementations, no more stubs):
- Redis pub/sub bridge: subscribes to discord-gateway events (message/attachment/voice) and broadcasts to WS clients
- Redis command channel: backend publishes voice/media commands, discord-gateway executes and replies
- Voice service: connectVoice/disconnectVoice/getVoiceStatus via Redis commands with graceful fallback
- Media service: queue/skip/stop/volume via Redis commands, reads status from Redis cache
- Messages repository: ALL 7 methods now use real PostgreSQL queries (findMany, findById, findByChannel, create, update, delete, getAttachmentsByChannel)
- Analytics: period returns {start,end} epoch millis, overview includes hourly/topics/top_users, worst_flags as string[]
- Health check: actually queries SELECT 1 against database
- VoiceStatus type fixed: {connected, activeGuildId, activeChannelId, activeChannelName}
- Guild type: includes icon: string | null
- asyncHandler: accepts Promise<unknown> instead of Promise<void>
Discord Gateway:
- CommandHandler: subscribes to 'backend:command' Redis channel, executes voice/media commands, publishes replies
- Publishes voice:status and media:status to Redis for backend caching
- Shutdown handler updated to close command handler
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
9b41eb9c12
commit
3b2709455e
@@ -35,6 +35,8 @@ export class AnalyticsRepository {
|
||||
async getOverview(guildId: string, channelId?: string, hours = 24) {
|
||||
logger.debug({ guildId, channelId, hours }, "Getting analytics overview");
|
||||
const pool = getPool();
|
||||
const now = Date.now();
|
||||
const start = now - hours * 3_600_000;
|
||||
const filter = buildTimeFilter(guildId, channelId, hours);
|
||||
|
||||
const { rows } = await pool.query(
|
||||
@@ -57,8 +59,13 @@ export class AnalyticsRepository {
|
||||
|
||||
const row = rows[0] as Record<string, unknown> | undefined;
|
||||
|
||||
// Fetch hourly stats, topics, and top violators to include in overview
|
||||
const hourly = await this.getHourlyStats(guildId, channelId, hours);
|
||||
const topics = await this.getTopics(guildId, channelId, hours);
|
||||
const topUsers = await this.getTopViolators(guildId, channelId, hours, 5);
|
||||
|
||||
return {
|
||||
period: { hours },
|
||||
period: { start, end: now },
|
||||
messages: {
|
||||
total: Number(row?.total_messages ?? 0),
|
||||
clean: Number(row?.clean ?? 0),
|
||||
@@ -68,9 +75,9 @@ export class AnalyticsRepository {
|
||||
pending: Number(row?.pending ?? 0),
|
||||
average_score: Number(row?.average_score ?? 0),
|
||||
},
|
||||
hourly: [],
|
||||
topics: [],
|
||||
top_users: [],
|
||||
hourly,
|
||||
topics,
|
||||
top_users: topUsers,
|
||||
active_users_count: Number(row?.active_users_count ?? 0),
|
||||
total_channels: Number(row?.total_channels ?? 0),
|
||||
};
|
||||
@@ -185,7 +192,9 @@ export class AnalyticsRepository {
|
||||
flagged_count: Number(r.flagged_count ?? 0),
|
||||
warned_count: Number(r.warned_count ?? 0),
|
||||
violation_score: Number(r.violation_score ?? 0),
|
||||
worst_flags: (r.worst_flags as string | null) ?? null,
|
||||
worst_flags: (r.worst_flags as string | null)
|
||||
? (r.worst_flags as string).split(",").map((s) => s.trim()).filter(Boolean)
|
||||
: [],
|
||||
last_violation: Number(r.last_violation ?? 0),
|
||||
}));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user