83 lines
2.3 KiB
TypeScript
83 lines
2.3 KiB
TypeScript
import type { WebSocket } from "ws";
|
|
import { createChildLogger } from "../logger";
|
|
import type {
|
|
AnalysisQueueStatus,
|
|
AttachmentRecord,
|
|
MediaState,
|
|
MessageRecord,
|
|
ModerationWsEvent,
|
|
} from "./types";
|
|
|
|
export type BroadcasterClient = Pick<WebSocket, "readyState" | "send">;
|
|
|
|
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) {
|
|
if (client.readyState === 1) {
|
|
try {
|
|
client.send(payload);
|
|
} catch (error) {
|
|
log.warn(
|
|
{ error, eventType: event.type },
|
|
"Failed to send event to client",
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export function createBroadcaster() {
|
|
const clients = new Set<BroadcasterClient>();
|
|
|
|
return {
|
|
addClient(client: BroadcasterClient) {
|
|
clients.add(client);
|
|
log.debug({ clientCount: clients.size }, "Client added");
|
|
},
|
|
removeClient(client: BroadcasterClient) {
|
|
clients.delete(client);
|
|
log.debug({ clientCount: clients.size }, "Client removed");
|
|
},
|
|
clientCount() {
|
|
return clients.size;
|
|
},
|
|
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 });
|
|
},
|
|
mediaState(state: MediaState) {
|
|
sendJson(clients, { type: "media_state", state });
|
|
},
|
|
};
|
|
}
|
|
|
|
export type ModerationBroadcaster = ReturnType<typeof createBroadcaster>;
|