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
@@ -14,8 +14,13 @@ export function createRecordingsRouter(): Router {
"/recordings",
asyncHandler(async (req: Request, res: Response) => {
const limit = Number(req.query.limit) || 50;
logger.debug({ limit }, "Fetching recordings");
const result = await recordingsService.getRecent(limit);
const channelId = req.query.channelId as string | undefined;
const userId = req.query.userId as string | undefined;
logger.debug({ limit, channelId, userId }, "Fetching recordings");
const result = await recordingsService.getRecent(limit, {
channelId,
userId,
});
res.json(result);
}),
);
@@ -5,17 +5,37 @@ import { getDatabase } from "../../shared/database/index.js";
const logger = createChildLogger("recordings.service");
export class RecordingsService {
async getRecent(limit = 50) {
async getRecent(
limit = 50,
filters?: { channelId?: string; userId?: string },
) {
logger.info({ limit }, "getRecent called");
const db = getDatabase();
logger.debug({ limit }, "Fetching recent voice recordings");
const conditions: string[] = [];
const params: unknown[] = [];
if (filters?.channelId) {
params.push(filters.channelId);
conditions.push(`channel_id = $${params.length}`);
}
if (filters?.userId) {
params.push(filters.userId);
conditions.push(`user_id = $${params.length}`);
}
const whereClause =
conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
const { rows } = await db.execute(sql`
SELECT
id, user_id, username, avatar_url, guild_id, channel_id,
channel_name, filename, size_bytes, download_url,
upload_status, upload_error, created_at, uploaded_at
upload_status, upload_error, created_at, uploaded_at,
COALESCE(size_bytes, 0) AS duration_bytes
FROM voice_recordings
${sql.raw(whereClause)}
ORDER BY created_at DESC
LIMIT ${limit}
`);