fix(voice): resolve Redis subscriber corruption, transmitter races, and DB error logging
Redis root cause: VoiceHandler passed redisPub to transmitter.start(), which called .subscribe() on it — permanently converting the publish connection to subscriber mode. Every subsequent command reply and status update failed. Fixes: 1. Transmitter now creates its own Redis client via new IORedis() 2. Mutual exclusion gate serialises start/stop to prevent null-deref races 3. PassThrough drain listeners cleaned up to stop MaxListenersExceeded 4. FFmpeg SIGTERM flagged as expected exit (no more false level-50 errors) 5. Voice recording repo captures PG error code/detail for diagnostics Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -81,7 +81,6 @@ export class CommandHandler {
|
|||||||
this.voiceHandler = new VoiceHandler(
|
this.voiceHandler = new VoiceHandler(
|
||||||
client,
|
client,
|
||||||
voiceController,
|
voiceController,
|
||||||
this.redisPub,
|
|
||||||
);
|
);
|
||||||
this.mediaHandler = new MediaHandler();
|
this.mediaHandler = new MediaHandler();
|
||||||
this.guildHandler = new GuildHandler(client);
|
this.guildHandler = new GuildHandler(client);
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
} from "@bete/shared";
|
} from "@bete/shared";
|
||||||
import { createChildLogger } from "@bete/shared/logger";
|
import { createChildLogger } from "@bete/shared/logger";
|
||||||
import type { Client } from "discord.js-selfbot-v13";
|
import type { Client } from "discord.js-selfbot-v13";
|
||||||
import type Redis from "ioredis";
|
|
||||||
import { config } from "../../shared/config/config.js";
|
import { config } from "../../shared/config/config.js";
|
||||||
import { discordPlayer } from "../voice-recording/player.js";
|
import { discordPlayer } from "../voice-recording/player.js";
|
||||||
import { voiceTransmitter } from "../voice-recording/transmitter.js";
|
import { voiceTransmitter } from "../voice-recording/transmitter.js";
|
||||||
@@ -21,7 +20,6 @@ export class VoiceHandler {
|
|||||||
constructor(
|
constructor(
|
||||||
private client: Client | null,
|
private client: Client | null,
|
||||||
private voiceController: VoiceController | null,
|
private voiceController: VoiceController | null,
|
||||||
private sharedRedis: Redis | null = null,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
setClient(client: Client): void {
|
setClient(client: Client): void {
|
||||||
|
|||||||
@@ -40,19 +40,13 @@ export async function insertVoiceRecording(
|
|||||||
.values(recording)
|
.values(recording)
|
||||||
.onConflictDoNothing();
|
.onConflictDoNothing();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const detail =
|
const err = error as Record<string, unknown>;
|
||||||
error instanceof Error
|
const detail: Record<string, unknown> = {
|
||||||
? {
|
message: error instanceof Error ? error.message : String(error),
|
||||||
message: error.message,
|
name: error instanceof Error ? error.name : undefined,
|
||||||
name: error.name,
|
};
|
||||||
...((error as Record<string, unknown>).code !== undefined
|
if (err.code !== undefined) detail.code = err.code;
|
||||||
? { code: (error as Record<string, unknown>).code }
|
if (err.detail !== undefined) detail.detail = err.detail;
|
||||||
: {}),
|
|
||||||
...((error as Record<string, unknown>).detail !== undefined
|
|
||||||
? { detail: (error as Record<string, unknown>).detail }
|
|
||||||
: {}),
|
|
||||||
}
|
|
||||||
: String(error);
|
|
||||||
logger.error(
|
logger.error(
|
||||||
{ id: recording.id, error: detail },
|
{ id: recording.id, error: detail },
|
||||||
"Failed to insert voice recording",
|
"Failed to insert voice recording",
|
||||||
|
|||||||
Reference in New Issue
Block a user