refactor: share telegram caption splitting
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { type Bot, InputFile } from "grammy";
|
||||
import { markdownToTelegramChunks, markdownToTelegramHtml } from "../format.js";
|
||||
import { splitTelegramCaption } from "../caption.js";
|
||||
import type { ReplyPayload } from "../../auto-reply/types.js";
|
||||
import type { ReplyToMode } from "../../config/config.js";
|
||||
import { danger, logVerbose } from "../../globals.js";
|
||||
@@ -16,10 +17,6 @@ import type { TelegramContext } from "./types.js";
|
||||
|
||||
const PARSE_ERR_RE = /can't parse entities|parse entities|find end of the entity/i;
|
||||
|
||||
// Telegram limits media captions to 1024 characters.
|
||||
// Text beyond this must be sent as a separate follow-up message.
|
||||
const TELEGRAM_MAX_CAPTION_LENGTH = 1024;
|
||||
|
||||
export async function deliverReplies(params: {
|
||||
replies: ReplyPayload[];
|
||||
chatId: string;
|
||||
@@ -73,6 +70,7 @@ export async function deliverReplies(params: {
|
||||
// (when caption exceeds Telegram's 1024-char limit)
|
||||
let pendingFollowUpText: string | undefined;
|
||||
for (const mediaUrl of mediaList) {
|
||||
const isFirstMedia = first;
|
||||
const media = await loadWebMedia(mediaUrl);
|
||||
const kind = mediaKindFromMime(media.contentType ?? undefined);
|
||||
const isGif = isGifMedia({
|
||||
@@ -82,11 +80,11 @@ export async function deliverReplies(params: {
|
||||
const fileName = media.fileName ?? (isGif ? "animation.gif" : "file");
|
||||
const file = new InputFile(media.buffer, fileName);
|
||||
// Caption only on first item; if text exceeds limit, defer to follow-up message.
|
||||
const rawCaption = first ? (reply.text ?? undefined) : undefined;
|
||||
const captionTooLong = rawCaption != null && rawCaption.length > TELEGRAM_MAX_CAPTION_LENGTH;
|
||||
const caption = captionTooLong ? undefined : rawCaption;
|
||||
if (captionTooLong && rawCaption) {
|
||||
pendingFollowUpText = rawCaption;
|
||||
const { caption, followUpText } = splitTelegramCaption(
|
||||
isFirstMedia ? (reply.text ?? undefined) : undefined,
|
||||
);
|
||||
if (followUpText) {
|
||||
pendingFollowUpText = followUpText;
|
||||
}
|
||||
first = false;
|
||||
const replyToMessageId =
|
||||
@@ -138,22 +136,26 @@ export async function deliverReplies(params: {
|
||||
if (replyToId && !hasReplied) {
|
||||
hasReplied = true;
|
||||
}
|
||||
}
|
||||
// Send deferred follow-up text when caption was too long for media.
|
||||
// Chunk it in case it's extremely long (same logic as text-only replies).
|
||||
if (pendingFollowUpText) {
|
||||
const chunks = markdownToTelegramChunks(pendingFollowUpText, textLimit);
|
||||
for (const chunk of chunks) {
|
||||
await sendTelegramText(bot, chatId, chunk.html, runtime, {
|
||||
replyToMessageId:
|
||||
replyToId && (replyToMode === "all" || !hasReplied) ? replyToId : undefined,
|
||||
messageThreadId,
|
||||
textMode: "html",
|
||||
plainText: chunk.text,
|
||||
});
|
||||
if (replyToId && !hasReplied) {
|
||||
hasReplied = true;
|
||||
// Send deferred follow-up text right after the first media item.
|
||||
// Chunk it in case it's extremely long (same logic as text-only replies).
|
||||
if (pendingFollowUpText && isFirstMedia) {
|
||||
const chunks = markdownToTelegramChunks(pendingFollowUpText, textLimit);
|
||||
for (const chunk of chunks) {
|
||||
const replyToMessageIdFollowup =
|
||||
replyToId && (replyToMode === "all" || !hasReplied) ? replyToId : undefined;
|
||||
await bot.api.sendMessage(
|
||||
chatId,
|
||||
chunk.text,
|
||||
buildTelegramSendParams({
|
||||
replyToMessageId: replyToMessageIdFollowup,
|
||||
messageThreadId,
|
||||
}),
|
||||
);
|
||||
if (replyToId && !hasReplied) {
|
||||
hasReplied = true;
|
||||
}
|
||||
}
|
||||
pendingFollowUpText = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,6 +193,21 @@ export async function resolveMedia(
|
||||
return { path: saved.path, contentType: saved.contentType, placeholder };
|
||||
}
|
||||
|
||||
function buildTelegramSendParams(opts?: {
|
||||
replyToMessageId?: number;
|
||||
messageThreadId?: number;
|
||||
}): Record<string, unknown> {
|
||||
const threadParams = buildTelegramThreadParams(opts?.messageThreadId);
|
||||
const params: Record<string, unknown> = {};
|
||||
if (opts?.replyToMessageId) {
|
||||
params.reply_to_message_id = opts.replyToMessageId;
|
||||
}
|
||||
if (threadParams) {
|
||||
params.message_thread_id = threadParams.message_thread_id;
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
async function sendTelegramText(
|
||||
bot: Bot,
|
||||
chatId: string,
|
||||
@@ -203,13 +220,10 @@ async function sendTelegramText(
|
||||
plainText?: string;
|
||||
},
|
||||
): Promise<number | undefined> {
|
||||
const threadParams = buildTelegramThreadParams(opts?.messageThreadId);
|
||||
const baseParams: Record<string, unknown> = {
|
||||
reply_to_message_id: opts?.replyToMessageId,
|
||||
};
|
||||
if (threadParams) {
|
||||
baseParams.message_thread_id = threadParams.message_thread_id;
|
||||
}
|
||||
const baseParams = buildTelegramSendParams({
|
||||
replyToMessageId: opts?.replyToMessageId,
|
||||
messageThreadId: opts?.messageThreadId,
|
||||
});
|
||||
const textMode = opts?.textMode ?? "markdown";
|
||||
const htmlText = textMode === "html" ? text : markdownToTelegramHtml(text);
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user