import type { OpenClawPluginApi } from "openclaw/plugin-sdk"; import { onNewMessage, setMentionOverride, getTurnDebugInfo } from "../turn-manager.js"; import { extractDiscordChannelId } from "../channel-resolver.js"; import type { DirigentConfig } from "../rules.js"; type DebugConfig = { enableDebugLogs?: boolean; debugLogChannelIds?: string[]; }; type MessageReceivedDeps = { api: OpenClawPluginApi; baseConfig: DirigentConfig; getLivePluginConfig: (api: OpenClawPluginApi, fallback: DirigentConfig) => DirigentConfig; shouldDebugLog: (config: DirigentConfig & DebugConfig, channelId?: string) => boolean; debugCtxSummary: (ctx: Record, event: Record) => Record; ensureTurnOrder: (api: OpenClawPluginApi, channelId: string) => Promise | void; getModeratorUserId: (cfg: DirigentConfig) => string | undefined; recordChannelAccount: (api: OpenClawPluginApi, channelId: string, accountId: string) => boolean; extractMentionedUserIds: (content: string) => string[]; buildUserIdToAccountIdMap: (api: OpenClawPluginApi) => Map; }; export function registerMessageReceivedHook(deps: MessageReceivedDeps): void { const { api, baseConfig, getLivePluginConfig, shouldDebugLog, debugCtxSummary, ensureTurnOrder, getModeratorUserId, recordChannelAccount, extractMentionedUserIds, buildUserIdToAccountIdMap, } = deps; api.on("message_received", async (event, ctx) => { try { const c = (ctx || {}) as Record; const e = (event || {}) as Record; const preChannelId = extractDiscordChannelId(c, e); const livePre = getLivePluginConfig(api, baseConfig as DirigentConfig) as DirigentConfig & DebugConfig; if (shouldDebugLog(livePre, preChannelId)) { api.logger.info(`dirigent: debug message_received preflight ctx=${JSON.stringify(debugCtxSummary(c, e))}`); } if (preChannelId) { await ensureTurnOrder(api, preChannelId); const metadata = (e as Record).metadata as Record | undefined; const from = (typeof metadata?.senderId === "string" && metadata.senderId) || (typeof (e as Record).from === "string" ? ((e as Record).from as string) : ""); const moderatorUserId = getModeratorUserId(livePre); if (moderatorUserId && from === moderatorUserId) { if (shouldDebugLog(livePre, preChannelId)) { api.logger.info(`dirigent: ignoring moderator message in channel=${preChannelId}`); } } else { const humanList = livePre.humanList || livePre.bypassUserIds || []; const isHuman = humanList.includes(from); const senderAccountId = typeof c.accountId === "string" ? c.accountId : undefined; if (senderAccountId && senderAccountId !== "default") { const isNew = recordChannelAccount(api, preChannelId, senderAccountId); if (isNew) { await ensureTurnOrder(api, preChannelId); api.logger.info(`dirigent: new account ${senderAccountId} seen in channel=${preChannelId}, turn order updated`); } } if (isHuman) { const messageContent = ((e as Record).content as string) || ((e as Record).text as string) || ""; const mentionedUserIds = extractMentionedUserIds(messageContent); if (mentionedUserIds.length > 0) { const userIdMap = buildUserIdToAccountIdMap(api); const mentionedAccountIds = mentionedUserIds.map((uid) => userIdMap.get(uid)).filter((aid): aid is string => !!aid); if (mentionedAccountIds.length > 0) { await ensureTurnOrder(api, preChannelId); const overrideSet = setMentionOverride(preChannelId, mentionedAccountIds); if (overrideSet) { api.logger.info( `dirigent: mention override set channel=${preChannelId} mentionedAgents=${JSON.stringify(mentionedAccountIds)}`, ); if (shouldDebugLog(livePre, preChannelId)) { api.logger.info(`dirigent: turn state after override: ${JSON.stringify(getTurnDebugInfo(preChannelId))}`); } } else { onNewMessage(preChannelId, senderAccountId, isHuman); } } else { onNewMessage(preChannelId, senderAccountId, isHuman); } } else { onNewMessage(preChannelId, senderAccountId, isHuman); } } else { onNewMessage(preChannelId, senderAccountId, isHuman); } if (shouldDebugLog(livePre, preChannelId)) { api.logger.info( `dirigent: turn onNewMessage channel=${preChannelId} from=${from} isHuman=${isHuman} accountId=${senderAccountId ?? "unknown"}`, ); } } } } catch (err) { api.logger.warn(`dirigent: message hook failed: ${String(err)}`); } }); }