feat(discord-gateway): implement voice & push improvements

- Voice disconnect broadcast on stopRecording
- Multi-guild voice support (VoiceController Map<guildId>)
- Session finalization + auto-enqueue muxer job
- Recordings API: duration field, channelId/userId filters
- Transmitter Redis connection reuse (shared conn)
- FFmpeg stderr memory cap (4KB limit)
- 10 new Redis event channels + Redis bridge subscriptions
- New DB tables: message_reactions, message_edits
- Webhook notification module
- Gateway metrics / Prometheus endpoint
- Multi-guild message capture (MONITOR_GUILD_IDS array)
- Thread tracking, presence, channel topic, guild member events
- Edit history snapshot on message update
- Muxer audio post-processing worker

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
MythEclipse
2026-06-13 13:51:21 +07:00
co-authored by Claude
parent fd3b5c5ca5
commit 14c0081f01
17 changed files with 260 additions and 90 deletions
@@ -9,7 +9,10 @@ import { createChildLogger } from "@bete/shared/logger";
import type { Client } from "discord.js-selfbot-v13";
import Redis from "ioredis";
import { config } from "../../shared/config/config.js";
import type { VoiceController } from "../voice-recording/voiceController.js";
import type {
VoiceController,
VoiceStatus,
} from "../voice-recording/voiceController.js";
import { GuildHandler } from "./guild.handler.js";
import {
type CommandHandlerFn,
@@ -30,6 +33,12 @@ interface VoiceStatusPayload {
activeGuildId: string | null;
activeChannelId: string | null;
activeChannelName: string | null;
connections: Array<{
guildId: string;
channelId: string;
channelName: string;
connectedAt: number;
}>;
}
// ---------------------------------------------------------------------------
@@ -69,7 +78,11 @@ export class CommandHandler {
this.voiceController = voiceController;
// Create domain-specific handlers with their dependencies
this.voiceHandler = new VoiceHandler(client, voiceController, this.redisPub);
this.voiceHandler = new VoiceHandler(
client,
voiceController,
this.redisPub,
);
this.mediaHandler = new MediaHandler();
this.guildHandler = new GuildHandler(client);
this.moderationHandler = new ModerationHandler(client);
@@ -164,14 +177,23 @@ export class CommandHandler {
// ---- Status publishing ----
private publishVoiceStatus(): void {
const status: VoiceStatusPayload = this.voiceController
const raw = this.voiceController
? this.voiceController.getStatus()
: {
ready: false,
connected: false,
activeGuildId: null,
activeChannelId: null,
activeChannelName: null,
connections: [],
};
const status: VoiceStatusPayload = {
connected: raw.connected,
activeGuildId: raw.activeGuildId,
activeChannelId: raw.activeChannelId,
activeChannelName: raw.activeChannelName,
connections: raw.connections ?? [],
};
this.setKey(VOICE_STATUS_KEY, JSON.stringify(status));
}