refactor: de-duplicate channel runtime and payload helpers

This commit is contained in:
Peter Steinberger
2026-02-23 21:25:20 +00:00
parent 0ae7f470a2
commit 0183610db3
44 changed files with 775 additions and 698 deletions

View File

@@ -10,9 +10,8 @@ import { resolveSignalAccount } from "../signal/accounts.js";
import { resolveSlackAccount, resolveSlackReplyToMode } from "../slack/accounts.js";
import { buildSlackThreadingToolContext } from "../slack/threading-tool-context.js";
import { resolveTelegramAccount } from "../telegram/accounts.js";
import { escapeRegExp, normalizeE164 } from "../utils.js";
import { normalizeE164 } from "../utils.js";
import { resolveWhatsAppAccount } from "../web/accounts.js";
import { normalizeWhatsAppTarget } from "../whatsapp/normalize.js";
import {
resolveDiscordGroupRequireMention,
resolveDiscordGroupToolPolicy,
@@ -28,6 +27,7 @@ import {
resolveWhatsAppGroupToolPolicy,
} from "./plugins/group-mentions.js";
import { normalizeSignalMessagingTarget } from "./plugins/normalize/signal.js";
import { normalizeWhatsAppAllowFromEntries } from "./plugins/normalize/whatsapp.js";
import type {
ChannelCapabilities,
ChannelCommandAdapter,
@@ -42,6 +42,10 @@ import type {
ChannelThreadingAdapter,
ChannelThreadingToolContext,
} from "./plugins/types.js";
import {
resolveWhatsAppGroupIntroHint,
resolveWhatsAppMentionStripPatterns,
} from "./plugins/whatsapp-shared.js";
import { CHAT_CHANNEL_ORDER, type ChatChannelId, getChatChannelMeta } from "./registry.js";
export type ChannelDock = {
@@ -287,12 +291,7 @@ const DOCKS: Record<ChatChannelId, ChannelDock> = {
config: {
resolveAllowFrom: ({ cfg, accountId }) =>
resolveWhatsAppAccount({ cfg, accountId }).allowFrom ?? [],
formatAllowFrom: ({ allowFrom }) =>
allowFrom
.map((entry) => String(entry).trim())
.filter((entry): entry is string => Boolean(entry))
.map((entry) => (entry === "*" ? entry : normalizeWhatsAppTarget(entry)))
.filter((entry): entry is string => Boolean(entry)),
formatAllowFrom: ({ allowFrom }) => normalizeWhatsAppAllowFromEntries(allowFrom),
resolveDefaultTo: ({ cfg, accountId }) => {
const root = cfg.channels?.whatsapp;
const normalized = normalizeAccountId(accountId);
@@ -303,18 +302,10 @@ const DOCKS: Record<ChatChannelId, ChannelDock> = {
groups: {
resolveRequireMention: resolveWhatsAppGroupRequireMention,
resolveToolPolicy: resolveWhatsAppGroupToolPolicy,
resolveGroupIntroHint: () =>
"WhatsApp IDs: SenderId is the participant JID (group participant id).",
resolveGroupIntroHint: resolveWhatsAppGroupIntroHint,
},
mentions: {
stripPatterns: ({ ctx }) => {
const selfE164 = (ctx.To ?? "").replace(/^whatsapp:/, "");
if (!selfE164) {
return [];
}
const escaped = escapeRegExp(selfE164);
return [escaped, `@${escaped}`];
},
stripPatterns: ({ ctx }) => resolveWhatsAppMentionStripPatterns(ctx),
},
threading: {
buildToolContext: ({ context, hasRepliedRef }) => {

View File

@@ -0,0 +1,33 @@
export type MediaPayloadInput = {
path: string;
contentType?: string;
};
export type MediaPayload = {
MediaPath?: string;
MediaType?: string;
MediaUrl?: string;
MediaPaths?: string[];
MediaUrls?: string[];
MediaTypes?: string[];
};
export function buildMediaPayload(
mediaList: MediaPayloadInput[],
opts?: { preserveMediaTypeCardinality?: boolean },
): MediaPayload {
const first = mediaList[0];
const mediaPaths = mediaList.map((media) => media.path);
const rawMediaTypes = mediaList.map((media) => media.contentType ?? "");
const mediaTypes = opts?.preserveMediaTypeCardinality
? rawMediaTypes
: rawMediaTypes.filter((value): value is string => Boolean(value));
return {
MediaPath: first?.path,
MediaType: first?.contentType,
MediaUrl: first?.path,
MediaPaths: mediaPaths.length > 0 ? mediaPaths : undefined,
MediaUrls: mediaPaths.length > 0 ? mediaPaths : undefined,
MediaTypes: mediaTypes.length > 0 ? mediaTypes : undefined,
};
}

View File

@@ -9,6 +9,14 @@ export function normalizeWhatsAppMessagingTarget(raw: string): string | undefine
return normalizeWhatsAppTarget(trimmed) ?? undefined;
}
export function normalizeWhatsAppAllowFromEntries(allowFrom: Array<string | number>): string[] {
return allowFrom
.map((entry) => String(entry).trim())
.filter((entry): entry is string => Boolean(entry))
.map((entry) => (entry === "*" ? entry : normalizeWhatsAppTarget(entry)))
.filter((entry): entry is string => Boolean(entry));
}
export function looksLikeWhatsAppTargetId(raw: string): boolean {
return looksLikeHandleOrPhoneTarget({
raw,

View File

@@ -0,0 +1,17 @@
import { escapeRegExp } from "../../utils.js";
export const WHATSAPP_GROUP_INTRO_HINT =
"WhatsApp IDs: SenderId is the participant JID (group participant id).";
export function resolveWhatsAppGroupIntroHint(): string {
return WHATSAPP_GROUP_INTRO_HINT;
}
export function resolveWhatsAppMentionStripPatterns(ctx: { To?: string | null }): string[] {
const selfE164 = (ctx.To ?? "").replace(/^whatsapp:/, "");
if (!selfE164) {
return [];
}
const escaped = escapeRegExp(selfE164);
return [escaped, `@${escaped}`];
}