Files
dc-recorder/src/moderation/broadcaster.ts

79 lines
2.2 KiB
TypeScript
Raw Normal View History

import type { WebSocket } from "ws";
import { createChildLogger } from "../logger";
import type {
AnalysisQueueStatus,
AttachmentRecord,
MessageRecord,
ModerationWsEvent,
} from "./types";
export type BroadcasterClient = Pick<WebSocket, "readyState" | "send">;
2026-05-14 18:44:47 +07:00
const log = createChildLogger("broadcaster");
function sendJson(
clients: Set<BroadcasterClient>,
event: ModerationWsEvent,
): void {
const payload = JSON.stringify({ ...event, timestamp: Date.now() });
for (const client of clients) {
2026-05-14 18:44:47 +07:00
if (client.readyState === 1) {
try {
client.send(payload);
} catch (error) {
log.warn(
{ error, eventType: event.type },
2026-05-14 19:46:47 +07:00
"Failed to send event to client",
2026-05-14 18:44:47 +07:00
);
}
}
}
}
export function createBroadcaster() {
const clients = new Set<BroadcasterClient>();
return {
addClient(client: BroadcasterClient) {
clients.add(client);
2026-05-14 18:44:47 +07:00
log.debug({ clientCount: clients.size }, "Client added");
},
removeClient(client: BroadcasterClient) {
clients.delete(client);
2026-05-14 18:44:47 +07:00
log.debug({ clientCount: clients.size }, "Client removed");
},
clientCount() {
return clients.size;
},
2026-05-14 19:46:47 +07:00
getClients() {
return Array.from(clients);
},
uiState(state: unknown) {
sendJson(clients, { type: "ui_state", state });
},
userState(users: unknown[]) {
sendJson(clients, { type: "user_state", users });
},
messageCreated(data: MessageRecord) {
sendJson(clients, { type: "message_created", data });
},
messageUpdated(data: Partial<MessageRecord> & { id: string }) {
sendJson(clients, { type: "message_updated", data });
},
messageDeleted(data: { id: string; deleted_at: number }) {
sendJson(clients, { type: "message_deleted", data });
},
messageAnalyzed(data: MessageRecord) {
sendJson(clients, { type: "message_analyzed", data });
},
attachmentCreated(data: AttachmentRecord) {
sendJson(clients, { type: "attachment_created", data });
},
analysisQueueStatus(data: AnalysisQueueStatus) {
sendJson(clients, { type: "analysis_queue_status", data });
},
};
}
export type ModerationBroadcaster = ReturnType<typeof createBroadcaster>;