refactor: share outbound media payload sequencing

This commit is contained in:
Peter Steinberger 2026-03-13 16:26:42 +00:00
parent a37e25fa21
commit 501837058c
2 changed files with 52 additions and 31 deletions

View File

@ -28,34 +28,58 @@ type SendPayloadAdapter = Pick<
"sendMedia" | "sendText" | "chunker" | "textChunkLimit"
>;
export function resolvePayloadMediaUrls(payload: SendPayloadContext["payload"]): string[] {
return payload.mediaUrls?.length ? payload.mediaUrls : payload.mediaUrl ? [payload.mediaUrl] : [];
}
export async function sendPayloadMediaSequence<TResult>(params: {
text: string;
mediaUrls: readonly string[];
send: (input: {
text: string;
mediaUrl: string;
index: number;
isFirst: boolean;
}) => Promise<TResult>;
}): Promise<TResult | undefined> {
let lastResult: TResult | undefined;
for (let i = 0; i < params.mediaUrls.length; i += 1) {
const mediaUrl = params.mediaUrls[i];
if (!mediaUrl) {
continue;
}
lastResult = await params.send({
text: i === 0 ? params.text : "",
mediaUrl,
index: i,
isFirst: i === 0,
});
}
return lastResult;
}
export async function sendTextMediaPayload(params: {
channel: string;
ctx: SendPayloadContext;
adapter: SendPayloadAdapter;
}): Promise<SendPayloadResult> {
const text = params.ctx.payload.text ?? "";
const urls = params.ctx.payload.mediaUrls?.length
? params.ctx.payload.mediaUrls
: params.ctx.payload.mediaUrl
? [params.ctx.payload.mediaUrl]
: [];
const urls = resolvePayloadMediaUrls(params.ctx.payload);
if (!text && urls.length === 0) {
return { channel: params.channel, messageId: "" };
}
if (urls.length > 0) {
let lastResult = await params.adapter.sendMedia!({
const lastResult = await sendPayloadMediaSequence({
text,
mediaUrls: urls,
send: async ({ text, mediaUrl }) =>
await params.adapter.sendMedia!({
...params.ctx,
text,
mediaUrl: urls[0],
mediaUrl,
}),
});
for (let i = 1; i < urls.length; i++) {
lastResult = await params.adapter.sendMedia!({
...params.ctx,
text: "",
mediaUrl: urls[i],
});
}
return lastResult;
return lastResult ?? { channel: params.channel, messageId: "" };
}
const limit = params.adapter.textChunkLimit;
const chunks = limit && params.adapter.chunker ? params.adapter.chunker(text, limit) : [text];

View File

@ -8,6 +8,7 @@ import {
} from "../../../telegram/outbound-params.js";
import { sendMessageTelegram } from "../../../telegram/send.js";
import type { ChannelOutboundAdapter } from "../types.js";
import { resolvePayloadMediaUrls, sendPayloadMediaSequence } from "./direct-text-media.js";
type TelegramSendFn = typeof sendMessageTelegram;
type TelegramSendOpts = Parameters<TelegramSendFn>[2];
@ -55,11 +56,7 @@ export async function sendTelegramPayloadMessages(params: {
const quoteText =
typeof telegramData?.quoteText === "string" ? telegramData.quoteText : undefined;
const text = params.payload.text ?? "";
const mediaUrls = params.payload.mediaUrls?.length
? params.payload.mediaUrls
: params.payload.mediaUrl
? [params.payload.mediaUrl]
: [];
const mediaUrls = resolvePayloadMediaUrls(params.payload);
const payloadOpts = {
...params.baseOpts,
quoteText,
@ -73,16 +70,16 @@ export async function sendTelegramPayloadMessages(params: {
}
// Telegram allows reply_markup on media; attach buttons only to the first send.
let finalResult: Awaited<ReturnType<TelegramSendFn>> | undefined;
for (let i = 0; i < mediaUrls.length; i += 1) {
const mediaUrl = mediaUrls[i];
const isFirst = i === 0;
finalResult = await params.send(params.to, isFirst ? text : "", {
const finalResult = await sendPayloadMediaSequence({
text,
mediaUrls,
send: async ({ text, mediaUrl, isFirst }) =>
await params.send(params.to, text, {
...payloadOpts,
mediaUrl,
...(isFirst ? { buttons: telegramData?.buttons } : {}),
}),
});
}
return finalResult ?? { messageId: "unknown", chatId: params.to };
}