fix: resolve architecture disconnects and codebase weaknesses

- Add TEXT_CHANNEL_ID and TEXT_GUILD_ID to config schema (fix silent channel monitoring)
- Remove dead files: message-capture/broadcaster.ts, voice-recording/index.ts
- Fix WebSocket voice_command payload to forward from frontend
- Implement moderation:action handler in commandHandler
- Fix useMascotChat to use canonical request() wrapper
- Fix useAudioPlayback userId hash collision (use string not parseInt)
- Add catch blocks to useMediaControl.skip/stop
- Add typed broadcast functions (messageAnalyzed, voicePcmData, voiceActiveUser)
- Apply Biome formatting and lint fixes

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MythEclipse
2026-06-09 16:36:23 +07:00
co-authored by Claude Opus 4.8
parent 30c9e86edc
commit 3a7b005d95
13 changed files with 171 additions and 109 deletions
@@ -52,6 +52,10 @@ export function useMediaControl() {
const state = await skipMedia();
setMediaState(state);
return state;
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
setError(message);
throw err;
} finally {
setLoading(false);
}
@@ -64,6 +68,10 @@ export function useMediaControl() {
const state = await stopMedia();
setMediaState(state);
return state;
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
setError(message);
throw err;
} finally {
setLoading(false);
}
@@ -124,6 +124,10 @@ export interface AppConfig {
monitorGuildId: string | null;
}
export interface ChatResponse {
response?: string;
}
export type DashboardTab = "live" | "messages";
// ─── Messages ────────────────────────────────────────────────────────────────
@@ -10,7 +10,7 @@ export function useAudioPlayback() {
Array.from({ length: 32 }, () => 0.04),
);
const audioContextRef = useRef<AudioContext | null>(null);
const userTimelinesRef = useRef(new Map<number, number>());
const userTimelinesRef = useRef(new Map<string, number>());
const handleIncomingPcm = useCallback(
(data: { userId: string; pcm: string }) => {
@@ -57,13 +57,12 @@ export function useAudioPlayback() {
source.connect(audioContext.destination);
// Schedule playback per user to avoid overlaps
const userIdHash = parseInt(data.userId, 10);
const currentTime = audioContext.currentTime;
let nextStart = userTimelinesRef.current.get(userIdHash) || 0;
let nextStart = userTimelinesRef.current.get(data.userId) || 0;
if (nextStart < currentTime) nextStart = currentTime + 0.05;
source.start(nextStart);
userTimelinesRef.current.set(
userIdHash,
data.userId,
nextStart + audioBuffer.duration,
);
},
@@ -1,4 +1,6 @@
import { useCallback, useState } from "react";
import type { ChatResponse } from "../api/client";
import { request } from "../api/client";
import { createChildLogger } from "../logger";
const logger = createChildLogger("useMascotChat");
@@ -18,17 +20,10 @@ export function useMascotChat(context?: ChatContext) {
const handleSendMessage = useCallback(
async (message: string): Promise<string> => {
try {
const response = await fetch("/api/mascot/chat", {
const data = await request<ChatResponse>("/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message, context }),
});
if (!response.ok) {
throw new Error(`Mascot backend responded with ${response.status}`);
}
const data = (await response.json()) as { response?: string };
return data.response || fallbackResponse(message, context);
} catch (error) {
logger.warn("Mascot backend unavailable, using fallback", { error });