import type { OpenClawPluginApi } from "openclaw/plugin-sdk"; import type { ChannelStore } from "../core/channel-store.js"; type Deps = { api: OpenClawPluginApi; channelStore: ChannelStore; moderatorBotUserId: string | undefined; scheduleIdentifier: string; moderatorServiceUrl: string | undefined; moderatorServiceToken: string | undefined; debugMode: boolean; onNewMessage: (event: { channelId: string; messageId: string; senderId: string; guildId?: string; }) => Promise; }; function sendJson(res: import("node:http").ServerResponse, status: number, payload: unknown): void { res.writeHead(status, { "Content-Type": "application/json; charset=utf-8" }); res.end(JSON.stringify(payload)); } function readBody(req: import("node:http").IncomingMessage): Promise> { return new Promise((resolve, reject) => { let body = ""; req.on("data", (chunk: Buffer) => { body += chunk.toString(); if (body.length > 1_000_000) { req.destroy(); reject(new Error("body too large")); } }); req.on("end", () => { try { resolve(body ? (JSON.parse(body) as Record) : {}); } catch { reject(new Error("invalid_json")); } }); req.on("error", reject); }); } /** * Register Dirigent plugin HTTP routes that the moderator service calls back into. * * Routes: * POST /dirigent/api/moderator/message — inbound message notification from moderator service * GET /dirigent/api/moderator/status — health/status check */ export function registerDirigentApi(deps: Deps): void { const { api, moderatorServiceUrl, onNewMessage } = deps; // ── POST /dirigent/api/moderator/message ───────────────────────────────────── // Called by the moderator service on every Discord MESSAGE_CREATE event. api.registerHttpRoute({ path: "/dirigent/api/moderator/message", auth: "plugin", match: "exact", handler: async (req, res) => { if (req.method !== "POST") { res.writeHead(405); res.end(); return; } let body: Record; try { body = await readBody(req); } catch (err) { return sendJson(res, 400, { ok: false, error: String(err) }); } const channelId = typeof body.channelId === "string" ? body.channelId : undefined; const messageId = typeof body.messageId === "string" ? body.messageId : undefined; const senderId = typeof body.senderId === "string" ? body.senderId : undefined; const guildId = typeof body.guildId === "string" ? body.guildId : undefined; if (!channelId || !senderId) { return sendJson(res, 400, { ok: false, error: "channelId and senderId required" }); } try { await onNewMessage({ channelId, messageId: messageId ?? "", senderId, guildId, }); return sendJson(res, 200, { ok: true }); } catch (err) { api.logger.warn(`dirigent: moderator/message handler error: ${String(err)}`); return sendJson(res, 500, { ok: false, error: String(err) }); } }, }); // ── GET /dirigent/api/moderator/status ─────────────────────────────────────── api.registerHttpRoute({ path: "/dirigent/api/moderator/status", auth: "plugin", match: "exact", handler: (_req, res) => { return sendJson(res, 200, { ok: true, moderatorServiceUrl: moderatorServiceUrl ?? null, }); }, }); }