Files
clawdbot/src/auto-reply/reply/reply-payloads.ts
2026-01-17 06:45:11 +00:00

105 lines
3.7 KiB
TypeScript

import { isMessagingToolDuplicate } from "../../agents/pi-embedded-helpers.js";
import { normalizeTargetForProvider } from "../../infra/outbound/target-normalization.js";
import type { MessagingToolSend } from "../../agents/pi-embedded-runner.js";
import type { ReplyToMode } from "../../config/types.js";
import type { OriginatingChannelType } from "../templating.js";
import type { ReplyPayload } from "../types.js";
import { extractReplyToTag } from "./reply-tags.js";
import { createReplyToModeFilterForChannel } from "./reply-threading.js";
export function applyReplyTagsToPayload(
payload: ReplyPayload,
currentMessageId?: string,
): ReplyPayload {
if (typeof payload.text !== "string") {
if (!payload.replyToCurrent || payload.replyToId) return payload;
return {
...payload,
replyToId: currentMessageId?.trim() || undefined,
};
}
const shouldParseTags = payload.text.includes("[[");
if (!shouldParseTags) {
if (!payload.replyToCurrent || payload.replyToId) return payload;
return {
...payload,
replyToId: currentMessageId?.trim() || undefined,
replyToTag: payload.replyToTag ?? true,
};
}
const { cleaned, replyToId, replyToCurrent, hasTag } = extractReplyToTag(
payload.text,
currentMessageId,
);
return {
...payload,
text: cleaned ? cleaned : undefined,
replyToId: replyToId ?? payload.replyToId,
replyToTag: hasTag || payload.replyToTag,
replyToCurrent: replyToCurrent || payload.replyToCurrent,
};
}
export function isRenderablePayload(payload: ReplyPayload): boolean {
return Boolean(
payload.text ||
payload.mediaUrl ||
(payload.mediaUrls && payload.mediaUrls.length > 0) ||
payload.audioAsVoice,
);
}
export function applyReplyThreading(params: {
payloads: ReplyPayload[];
replyToMode: ReplyToMode;
replyToChannel?: OriginatingChannelType;
currentMessageId?: string;
}): ReplyPayload[] {
const { payloads, replyToMode, replyToChannel, currentMessageId } = params;
const applyReplyToMode = createReplyToModeFilterForChannel(replyToMode, replyToChannel);
return payloads
.map((payload) => applyReplyTagsToPayload(payload, currentMessageId))
.filter(isRenderablePayload)
.map(applyReplyToMode);
}
export function filterMessagingToolDuplicates(params: {
payloads: ReplyPayload[];
sentTexts: string[];
}): ReplyPayload[] {
const { payloads, sentTexts } = params;
if (sentTexts.length === 0) return payloads;
return payloads.filter((payload) => !isMessagingToolDuplicate(payload.text ?? "", sentTexts));
}
function normalizeAccountId(value?: string): string | undefined {
const trimmed = value?.trim();
return trimmed ? trimmed.toLowerCase() : undefined;
}
export function shouldSuppressMessagingToolReplies(params: {
messageProvider?: string;
messagingToolSentTargets?: MessagingToolSend[];
originatingTo?: string;
accountId?: string;
}): boolean {
const provider = params.messageProvider?.trim().toLowerCase();
if (!provider) return false;
const originTarget = normalizeTargetForProvider(provider, params.originatingTo);
if (!originTarget) return false;
const originAccount = normalizeAccountId(params.accountId);
const sentTargets = params.messagingToolSentTargets ?? [];
if (sentTargets.length === 0) return false;
return sentTargets.some((target) => {
if (!target?.provider) return false;
if (target.provider.trim().toLowerCase() !== provider) return false;
const targetKey = normalizeTargetForProvider(provider, target.to);
if (!targetKey) return false;
const targetAccount = normalizeAccountId(target.accountId);
if (originAccount && targetAccount && originAccount !== targetAccount) {
return false;
}
return targetKey === originTarget;
});
}