Files
openclaw/src/hooks/message-hook-mappers.ts
2026-03-07 10:41:05 +00:00

274 lines
7.6 KiB
TypeScript

import type { FinalizedMsgContext } from "../auto-reply/templating.js";
import type { OpenClawConfig } from "../config/config.js";
import type {
PluginHookMessageContext,
PluginHookMessageReceivedEvent,
PluginHookMessageSentEvent,
} from "../plugins/types.js";
import type {
MessagePreprocessedHookContext,
MessageReceivedHookContext,
MessageSentHookContext,
MessageTranscribedHookContext,
} from "./internal-hooks.js";
export type CanonicalInboundMessageHookContext = {
from: string;
to?: string;
content: string;
body?: string;
bodyForAgent?: string;
transcript?: string;
timestamp?: number;
channelId: string;
accountId?: string;
conversationId?: string;
messageId?: string;
senderId?: string;
senderName?: string;
senderUsername?: string;
senderE164?: string;
provider?: string;
surface?: string;
threadId?: string | number;
mediaPath?: string;
mediaType?: string;
originatingChannel?: string;
originatingTo?: string;
guildId?: string;
channelName?: string;
isGroup: boolean;
groupId?: string;
};
export type CanonicalSentMessageHookContext = {
to: string;
content: string;
success: boolean;
error?: string;
channelId: string;
accountId?: string;
conversationId?: string;
messageId?: string;
isGroup?: boolean;
groupId?: string;
};
export function deriveInboundMessageHookContext(
ctx: FinalizedMsgContext,
overrides?: {
content?: string;
messageId?: string;
},
): CanonicalInboundMessageHookContext {
const content =
overrides?.content ??
(typeof ctx.BodyForCommands === "string"
? ctx.BodyForCommands
: typeof ctx.RawBody === "string"
? ctx.RawBody
: typeof ctx.Body === "string"
? ctx.Body
: "");
const channelId = (ctx.OriginatingChannel ?? ctx.Surface ?? ctx.Provider ?? "").toLowerCase();
const conversationId = ctx.OriginatingTo ?? ctx.To ?? ctx.From ?? undefined;
const isGroup = Boolean(ctx.GroupSubject || ctx.GroupChannel);
return {
from: ctx.From ?? "",
to: ctx.To,
content,
body: ctx.Body,
bodyForAgent: ctx.BodyForAgent,
transcript: ctx.Transcript,
timestamp:
typeof ctx.Timestamp === "number" && Number.isFinite(ctx.Timestamp)
? ctx.Timestamp
: undefined,
channelId,
accountId: ctx.AccountId,
conversationId,
messageId:
overrides?.messageId ??
ctx.MessageSidFull ??
ctx.MessageSid ??
ctx.MessageSidFirst ??
ctx.MessageSidLast,
senderId: ctx.SenderId,
senderName: ctx.SenderName,
senderUsername: ctx.SenderUsername,
senderE164: ctx.SenderE164,
provider: ctx.Provider,
surface: ctx.Surface,
threadId: ctx.MessageThreadId,
mediaPath: ctx.MediaPath,
mediaType: ctx.MediaType,
originatingChannel: ctx.OriginatingChannel,
originatingTo: ctx.OriginatingTo,
guildId: ctx.GroupSpace,
channelName: ctx.GroupChannel,
isGroup,
groupId: isGroup ? conversationId : undefined,
};
}
export function buildCanonicalSentMessageHookContext(params: {
to: string;
content: string;
success: boolean;
error?: string;
channelId: string;
accountId?: string;
conversationId?: string;
messageId?: string;
isGroup?: boolean;
groupId?: string;
}): CanonicalSentMessageHookContext {
return {
to: params.to,
content: params.content,
success: params.success,
error: params.error,
channelId: params.channelId,
accountId: params.accountId,
conversationId: params.conversationId ?? params.to,
messageId: params.messageId,
isGroup: params.isGroup,
groupId: params.groupId,
};
}
export function toPluginMessageContext(
canonical: CanonicalInboundMessageHookContext | CanonicalSentMessageHookContext,
): PluginHookMessageContext {
return {
channelId: canonical.channelId,
accountId: canonical.accountId,
conversationId: canonical.conversationId,
};
}
export function toPluginMessageReceivedEvent(
canonical: CanonicalInboundMessageHookContext,
): PluginHookMessageReceivedEvent {
return {
from: canonical.from,
content: canonical.content,
timestamp: canonical.timestamp,
metadata: {
to: canonical.to,
provider: canonical.provider,
surface: canonical.surface,
threadId: canonical.threadId,
originatingChannel: canonical.originatingChannel,
originatingTo: canonical.originatingTo,
messageId: canonical.messageId,
senderId: canonical.senderId,
senderName: canonical.senderName,
senderUsername: canonical.senderUsername,
senderE164: canonical.senderE164,
guildId: canonical.guildId,
channelName: canonical.channelName,
},
};
}
export function toPluginMessageSentEvent(
canonical: CanonicalSentMessageHookContext,
): PluginHookMessageSentEvent {
return {
to: canonical.to,
content: canonical.content,
success: canonical.success,
...(canonical.error ? { error: canonical.error } : {}),
};
}
export function toInternalMessageReceivedContext(
canonical: CanonicalInboundMessageHookContext,
): MessageReceivedHookContext {
return {
from: canonical.from,
content: canonical.content,
timestamp: canonical.timestamp,
channelId: canonical.channelId,
accountId: canonical.accountId,
conversationId: canonical.conversationId,
messageId: canonical.messageId,
metadata: {
to: canonical.to,
provider: canonical.provider,
surface: canonical.surface,
threadId: canonical.threadId,
senderId: canonical.senderId,
senderName: canonical.senderName,
senderUsername: canonical.senderUsername,
senderE164: canonical.senderE164,
guildId: canonical.guildId,
channelName: canonical.channelName,
},
};
}
export function toInternalMessageTranscribedContext(
canonical: CanonicalInboundMessageHookContext,
cfg: OpenClawConfig,
): MessageTranscribedHookContext & { cfg: OpenClawConfig } {
const shared = toInternalInboundMessageHookContextBase(canonical);
return {
...shared,
transcript: canonical.transcript ?? "",
cfg,
};
}
export function toInternalMessagePreprocessedContext(
canonical: CanonicalInboundMessageHookContext,
cfg: OpenClawConfig,
): MessagePreprocessedHookContext & { cfg: OpenClawConfig } {
const shared = toInternalInboundMessageHookContextBase(canonical);
return {
...shared,
transcript: canonical.transcript,
isGroup: canonical.isGroup,
groupId: canonical.groupId,
cfg,
};
}
function toInternalInboundMessageHookContextBase(canonical: CanonicalInboundMessageHookContext) {
return {
from: canonical.from,
to: canonical.to,
body: canonical.body,
bodyForAgent: canonical.bodyForAgent,
timestamp: canonical.timestamp,
channelId: canonical.channelId,
conversationId: canonical.conversationId,
messageId: canonical.messageId,
senderId: canonical.senderId,
senderName: canonical.senderName,
senderUsername: canonical.senderUsername,
provider: canonical.provider,
surface: canonical.surface,
mediaPath: canonical.mediaPath,
mediaType: canonical.mediaType,
};
}
export function toInternalMessageSentContext(
canonical: CanonicalSentMessageHookContext,
): MessageSentHookContext {
return {
to: canonical.to,
content: canonical.content,
success: canonical.success,
...(canonical.error ? { error: canonical.error } : {}),
channelId: canonical.channelId,
accountId: canonical.accountId,
conversationId: canonical.conversationId,
messageId: canonical.messageId,
...(canonical.isGroup != null ? { isGroup: canonical.isGroup } : {}),
...(canonical.groupId ? { groupId: canonical.groupId } : {}),
};
}