UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

626 lines (625 loc) • 26.5 kB
import { S as parseStrictInteger, w as parseStrictPositiveInteger } from "./number-coercion-CkhMiRw8.mjs"; import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.mjs"; import { t as formatErrorMessage } from "./errors-BvOkmQM5.mjs"; import { r as logVerbose, t as danger } from "./globals-yEiTitH8.mjs"; import { n as getGlobalHookRunner } from "./hook-runner-global-Cn3xHNvt.mjs"; import { p as fireAndForgetHook } from "./hooks-CgZHhfMh.mjs"; import { f as normalizeMessagePresentation } from "./payload-DD64v4IB.mjs"; import "./channel-outbound-2iQdEUqa.mjs"; import { r as probeVideoDimensions } from "./media-probe-DIdEcKUL.mjs"; import "./text-utility-runtime-BB0hWeEg.mjs"; import { t as createMessageReceiptFromOutboundResults } from "./receipt-DD2wc-6s.mjs"; import { a as projectOutboundPayloadPlanForDelivery, n as createStructuredOutboundPayloadPlan, t as createOutboundPayloadPlan } from "./payloads-k3x9706z.mjs"; import { d as toPluginMessageSentEvent, l as toPluginMessageContext, o as toInternalMessageSentContext, t as buildCanonicalSentMessageHookContext } from "./message-hook-mappers-4B6b-i9y.mjs"; import { n as isSingleUseReplyToMode } from "./reply-reference-CLjC4hNl.mjs"; import { t as buildOutboundMediaLoadOptions } from "./load-options-gEuoEu4c.mjs"; import { n as loadWebMedia } from "./web-media-BoSxnw3q.mjs"; import { n as createInternalHookEvent, u as triggerInternalHook } from "./internal-hooks-eyE9vom5.mjs"; import { f as isChannelPartialDeliveryError } from "./live-BtWpKyHV.mjs"; import "./runtime-env-CySdLHPX.mjs"; import "./number-runtime-D3v2w3D4.mjs"; import "./reply-reference-D95Ecb-U.mjs"; import "./channel-inbound-BOhgTVYr.mjs"; import "./web-media-D1Dv73pC.mjs"; import "./ssrf-runtime-CR-jTHeC.mjs"; import "./media-runtime-Bn5-G6uL.mjs"; import "./plugin-runtime-B_bvZi7t.mjs"; import "./hook-runtime-CKjASQDc.mjs"; import { u as resolveTelegramTargetChatType } from "./topic-conversation-S0gZB4iY.mjs"; import { C as buildInlineKeyboard, d as isTelegramEmptyContentError, r as TELEGRAM_RICH_TEXT_LIMIT } from "./text-chunk-limit-plWH6e89.mjs"; import "./helpers-ODclOX0q.mjs"; import { o as resolveTelegramInlineButtons } from "./button-types-DEBkvVXS.mjs"; import { n as canonicalizeTelegramPresentationPayload, o as resolveTelegramInteractiveTextFallback } from "./interactive-fallback-B2WQWbwQ.mjs"; import { E as planTelegramTextDeliveryPages, J as recordSentMessage, K as buildTelegramProviderDeliveryResult, L as reactMessageTelegram, _ as isTelegramPhotoLimitError, b as resolveTelegramOutboundMediaSenders, dt as buildTelegramSendParams, f as createTelegramPreparedSender, g as isTelegramCaptionTooLongError, p as createTelegramReplyRequest, q as reportTelegramProviderDelivery, v as isTelegramVoiceMessagesForbiddenError, x as planTelegramMediaBatches, y as prepareTelegramOutboundMedia } from "./send-CpiI0BKu.mjs"; //#region extensions/telegram/src/bot/delivery.hooks.ts function emitTelegramMessageSentHooks(params) { const hookRunner = getGlobalHookRunner(); const enabled = hookRunner?.hasHooks("message_sent") ?? false; if (!enabled && !params.sessionKeyForInternalHooks) return; const canonical = buildCanonicalSentMessageHookContext({ to: params.chatId, content: params.content, success: params.success, error: params.error, channelId: "telegram", accountId: params.accountId, conversationId: params.chatId, messageId: typeof params.messageId === "number" ? String(params.messageId) : void 0, isGroup: params.isGroup, groupId: params.groupId }); if (enabled) fireAndForgetHook(Promise.resolve(hookRunner.runMessageSent(toPluginMessageSentEvent(canonical), toPluginMessageContext(canonical))), "telegram: message_sent plugin hook failed"); if (params.sessionKeyForInternalHooks) fireAndForgetHook(triggerInternalHook(createInternalHookEvent("message", "sent", params.sessionKeyForInternalHooks, toInternalMessageSentContext(canonical))), "telegram: message:sent internal hook failed"); } //#endregion //#region extensions/telegram/src/bot/native-quote.ts const TELEGRAM_NATIVE_QUOTE_MAX_LENGTH = 1024; function sliceTelegramEntitiesForQuote(entities, quoteLength) { if (!entities?.length || quoteLength <= 0) return; const sliced = []; for (const entity of entities) { const offset = Number.isFinite(entity.offset) ? Math.trunc(entity.offset) : 0; const length = Number.isFinite(entity.length) ? Math.trunc(entity.length) : 0; const start = Math.max(0, offset); const end = Math.min(quoteLength, offset + length); if (end <= start) continue; sliced.push({ ...entity, offset: start, length: end - start }); } return sliced.length > 0 ? sliced : void 0; } function buildTelegramNativeQuoteCandidate(params) { const source = params.text; if (!source?.trim()) return; const maxLength = params.maxLength ?? TELEGRAM_NATIVE_QUOTE_MAX_LENGTH; const text = truncateUtf16Safe(source, maxLength); if (!text.trim()) return; const entities = sliceTelegramEntitiesForQuote(params.entities, text.length); return { text, position: 0, ...entities ? { entities } : {} }; } function addTelegramNativeQuoteCandidate(target, messageId, candidate) { if (messageId == null || !candidate) return; const key = String(messageId).trim(); if (!key || target[key]) return; target[key] = candidate; } function resolveReplyQuoteForSend(params) { if (params.replyToId != null) { const mapped = params.replyQuoteByMessageId?.[String(params.replyToId)]; if (mapped?.text) return { messageId: params.replyToId, text: mapped.text, ...typeof mapped.position === "number" ? { position: mapped.position } : {}, ...mapped.entities ? { entities: mapped.entities } : {} }; } return { ...params.replyQuoteMessageId != null ? { messageId: params.replyQuoteMessageId } : {}, ...params.replyQuoteText != null ? { text: params.replyQuoteText } : {}, ...params.replyQuotePosition != null ? { position: params.replyQuotePosition } : {}, ...params.replyQuoteEntities != null ? { entities: params.replyQuoteEntities } : {} }; } //#endregion //#region extensions/telegram/src/bot/delivery.replies.ts function resolveReplyToForSend(params, progress) { return params.replyToId && (params.replyToMode === "all" || !progress.hasReplied) ? params.replyToId : void 0; } function markReplyApplied(progress, replyToId) { if (replyToId && !progress.hasReplied) progress.hasReplied = true; } function resolveVoiceFallbackText(reply) { return [reply.text, reply.spokenText].find((text) => text?.trim()); } function createReplyDeliverer(config, sender, progress) { const reportAcceptedPart = (part, kind) => reportTelegramProviderDelivery({ message: part.result, messageId: part.messageId, fallbackChatId: config.chatId, successfulSendThread: config.thread ?? void 0, ...kind ? { kind } : {} }); const recordMessageId = (messageId) => recordSentMessage(config.chatId, messageId, config.cfg, { accountId: config.accountId, agentId: config.ownerAgentId }); const mediaLoader = config.mediaLoader ?? loadWebMedia; async function deliverTextReply(params) { const replyProgress = params.progress ?? progress; const chunks = planTelegramTextDeliveryPages({ text: params.text, maxChars: config.richMessages === true ? Math.min(config.textLimit, TELEGRAM_RICH_TEXT_LIMIT) : Math.min(config.textLimit, 4e3), chunkMode: config.chunkMode ?? "length", tableMode: config.tableMode, richMessages: config.richMessages, skipEntityDetection: config.linkPreview === false, ...config.textMode ? { textMode: config.textMode } : {} }).filter((chunk) => chunk.richMessage?.blocks.length || chunk.htmlText?.trim() || chunk.plainText.trim()); const suppressReply = chunks.length > 1 && isSingleUseReplyToMode(params.replyToMode); return sender.sendText({ pages: chunks, context: config.richMessages ? "sendRichMessage" : "sendMessage", tracking: { invalidate: () => replyProgress.promptContext?.invalidate(), onRejected: (error) => config.runtime.error?.(danger(`telegram reply chunk rejected; continuing: ${formatErrorMessage(error)}`)), onSilentSkip: (error) => config.runtime.log?.(`telegram reply chunk rendered empty; skipping: ${formatErrorMessage(error)}`) }, preparePage: (_index, acceptedPages) => { const first = acceptedPages === 0; const replyToMessageId = suppressReply ? void 0 : resolveReplyToForSend(params, replyProgress); const quote = params.quoteOnlyOnFirstChunk === true && !first ? void 0 : params.quote; const base = buildTelegramSendParams({ replyToMessageId, replyQuoteMessageId: quote?.messageId, replyQuoteText: replyToMessageId ? quote?.text : void 0, replyQuotePosition: quote?.position, replyQuoteEntities: quote?.entities, thread: config.thread, silent: config.silent }); return { requestParams: (fallback) => { const requestParams = { ...base }; if (fallback?.index) { delete requestParams.reply_parameters; delete requestParams.reply_to_message_id; } return { ...requestParams, ...config.linkPreview === false ? { link_preview_options: { is_disabled: true } } : {}, ...first && params.replyMarkup && (!fallback || fallback.index === fallback.count - 1) ? { reply_markup: params.replyMarkup } : {} }; }, delivered: () => { markReplyApplied(replyProgress, suppressReply && first ? params.replyToId : replyToMessageId); } }; }, observe: async (part) => { const { messageId, plainText } = part; if (config.thread?.id !== void 0) await reportAcceptedPart(part); config.runtime.log?.(`telegram text delivery ok chat=${config.chatId} message=${messageId}`); await recordMessageId(messageId); await replyProgress.promptContext?.accept({ messageId, text: plainText }); } }); } async function deliverMediaReply(params) { let firstDeliveredMessageId; let visibleFallbackText; let firstDeliveredCaption; const mediaUrls = []; const observeMedia = async (part, captionRemoved) => { const { result: message, messageId, plainText } = part; if (config.thread?.id !== void 0) await reportAcceptedPart(part, "media"); firstDeliveredMessageId ??= messageId; firstDeliveredCaption ??= plainText || void 0; if (captionRemoved) visibleFallbackText = ""; await recordMessageId(messageId); await progress.promptContext?.accept({ messageId, message, ...plainText ? { text: plainText } : {} }); }; const deliverAcceptedMedia = async (options) => { const delivery = await sender.sendMedia(options); await sender.accept(delivery, (part) => observeMedia(part, delivery.captionRemoved), { mediaUrls: [options.mediaUrl] }); mediaUrls.push(options.mediaUrl); }; const prepareMedia = async (mediaUrl, index) => { const isFirstMedia = index === 0; const media = await mediaLoader(mediaUrl, buildOutboundMediaLoadOptions({ mediaLocalRoots: config.mediaLocalRoots, maxBytes: config.mediaMaxBytes })); const mediaPlan = prepareTelegramOutboundMedia({ media, text: isFirstMedia ? params.reply.text ?? void 0 : void 0, textMode: config.textMode, tableMode: config.tableMode, preparedHtml: true }); const { sender: mediaSender, documentSender } = resolveTelegramOutboundMediaSenders({ api: config.bot.api, chatId: config.chatId, media, plan: mediaPlan, asVoice: params.reply.audioAsVoice }); return { index, mediaUrl, media, mediaPlan, mediaSender, documentSender }; }; const deliverMediaBatch = async (batch) => { const { index, mediaUrl, media, mediaPlan, mediaSender, documentSender } = batch[0]; const isFirstMedia = index === 0; const { htmlCaption, plainCaption, followUpText } = mediaPlan; const replyToMessageId = resolveReplyToForSend(params, progress); const shouldAttachButtonsToMedia = isFirstMedia && params.replyMarkup && !followUpText; const videoDimensions = mediaPlan.kind === "video" ? await probeVideoDimensions(media.buffer) : void 0; const mediaParams = { caption: htmlCaption, ...htmlCaption ? { parse_mode: "HTML" } : {}, ...shouldAttachButtonsToMedia ? { reply_markup: params.replyMarkup } : {}, ...videoDimensions ? { width: videoDimensions.width, height: videoDimensions.height } : {}, ...buildTelegramSendParams({ replyToMessageId, replyQuoteMessageId: params.quote?.messageId, replyQuoteText: params.quote?.text, replyQuotePosition: params.quote?.position, replyQuoteEntities: params.quote?.entities, thread: config.thread, silent: config.silent }) }; if (batch.length > 1) { let album; try { album = await sender.sendPhotoAlbum({ files: batch.map((item) => item.mediaPlan.file), requestParams: mediaParams, plainCaption }); } catch (error) { if (!isTelegramPhotoLimitError(error)) throw error; mediaPlan.followUpText = void 0; batch.reduce((_previous, item) => item).mediaPlan.followUpText = followUpText; for (const item of batch) await deliverMediaBatch([item]); return; } await sender.acceptMany(album.parts, (part) => observeMedia(part, album.captionRemoved), { mediaUrls: batch.map((item) => item.mediaUrl), partialDeliveryResult: () => ({ receipt: createMessageReceiptFromOutboundResults({ results: album.parts.map((part) => buildTelegramProviderDeliveryResult({ message: part.result, messageId: part.result.message_id, fallbackChatId: config.chatId, ...config.thread ? { successfulSendThread: config.thread } : {}, kind: "media" })), kind: "media" }) }) }); mediaUrls.push(...batch.map((item) => item.mediaUrl)); } else if (mediaSender.label === "voice") { const sendVoiceMedia = async (requestParams) => { const hasCaption = typeof requestParams.caption === "string"; await deliverAcceptedMedia({ sender: mediaSender, mediaUrl, requestParams, plainCaption: hasCaption ? plainCaption : void 0 }); }; await config.onVoiceRecording?.(); try { await sendVoiceMedia(mediaParams); } catch (voiceErr) { if (isChannelPartialDeliveryError(voiceErr)) throw voiceErr; const voiceForbidden = isTelegramVoiceMessagesForbiddenError(voiceErr); let fallbackText; let fallbackReplyToId; if (voiceForbidden) { fallbackText = resolveVoiceFallbackText(params.reply); if (!fallbackText) throw voiceErr; logVerbose("telegram sendVoice forbidden (recipient has voice messages blocked in privacy settings); falling back to text"); fallbackReplyToId = resolveReplyToForSend(params, progress); } else if (isTelegramCaptionTooLongError(voiceErr)) { logVerbose("telegram sendVoice caption too long; resending voice without caption + text separately"); const noCaptionParams = { ...mediaParams }; delete noCaptionParams.caption; delete noCaptionParams.parse_mode; await sendVoiceMedia(noCaptionParams); fallbackText = resolveVoiceFallbackText(params.reply); } else throw voiceErr; if (fallbackText) try { const fallbackMessageId = await deliverTextReply({ ...params, text: fallbackText, replyToId: fallbackReplyToId, quote: voiceForbidden ? params.quote : void 0, replyToMode: voiceForbidden ? params.replyToMode : "first", progress: { ...progress, hasReplied: false }, quoteOnlyOnFirstChunk: true }); if (voiceForbidden) { if (fallbackMessageId === void 0) throw voiceErr; firstDeliveredMessageId ??= fallbackMessageId; } if (fallbackMessageId !== void 0) visibleFallbackText = fallbackText; } catch (fallbackError) { if (voiceForbidden || isChannelPartialDeliveryError(fallbackError) || !isTelegramEmptyContentError(fallbackError)) throw fallbackError; visibleFallbackText = ""; } markReplyApplied(progress, voiceForbidden ? fallbackReplyToId : replyToMessageId); return; } } else await deliverAcceptedMedia({ sender: mediaSender, documentSender, mediaUrl, requestParams: mediaParams, plainCaption }); markReplyApplied(progress, replyToMessageId); if (followUpText) try { if (await deliverTextReply({ ...params, text: followUpText, quote: void 0 }) === void 0) visibleFallbackText = firstDeliveredCaption ?? ""; else visibleFallbackText = void 0; } catch (error) { if (isChannelPartialDeliveryError(error) || !isTelegramEmptyContentError(error)) throw error; visibleFallbackText = firstDeliveredCaption ?? ""; if (params.replyMarkup && firstDeliveredMessageId !== void 0) await config.bot.api.editMessageReplyMarkup(config.chatId, firstDeliveredMessageId, { reply_markup: params.replyMarkup }); } }; for await (const batch of planTelegramMediaBatches({ mediaUrls: params.mediaList, prepare: prepareMedia, canGroup: (item) => item.mediaSender.label === "photo" && !params.replyMarkup })) await deliverMediaBatch(batch); return { firstDeliveredMessageId, visibleFallbackText, mediaUrls }; } return { deliverTextReply, deliverMediaReply }; } async function maybePinFirstDeliveredMessage(params) { if (!(params.pin === true || typeof params.pin === "object" && params.pin.enabled) || typeof params.firstDeliveredMessageId !== "number") return; const notify = typeof params.pin === "object" && params.pin.notify === true; try { await params.bot.api.pinChatMessage(params.chatId, params.firstDeliveredMessageId, { disable_notification: !notify }); } catch (err) { if (typeof params.pin === "object" && params.pin.required === true) throw err; logVerbose(`telegram pinChatMessage failed chat=${params.chatId} message=${params.firstDeliveredMessageId}: ${formatErrorMessage(err)}`); } } async function deliverReplies(params) { return deliverReplyPlan(params, (replies) => createOutboundPayloadPlan(replies, { cfg: params.cfg, sessionKey: params.policySessionKey ?? params.sessionKeyForInternalHooks, surface: "telegram" })); } async function deliverStructuredReplies(params) { return deliverReplyPlan(params, createStructuredOutboundPayloadPlan); } async function deliverReplyPlan(params, createPlan) { const progress = { hasReplied: false, ...params.promptContextSequence ? { promptContext: params.promptContextSequence } : {} }; const transcriptMirror = params.transcriptMirror; const deliveredContents = []; const hookRunner = getGlobalHookRunner(); const hasMessageSendingHooks = hookRunner?.hasHooks("message_sending") ?? false; const candidateReplies = []; for (const reply of params.replies) { if (!reply || typeof reply !== "object") { params.runtime.error?.(danger("reply missing text/media")); continue; } candidateReplies.push(reply); } const normalizedReplies = projectOutboundPayloadPlanForDelivery(createPlan(candidateReplies)); const sender = createTelegramPreparedSender({ api: params.bot.api, chatId: params.chatId, request: createTelegramReplyRequest(params.runtime), warn: (message) => params.runtime.log?.(message), beforeSend: params.onPlatformSendDispatch, assertPlatformSendAuthorized: params.assertPlatformSendAuthorized, onMediaAccepted: params.onMediaAccepted }); const { deliverTextReply, deliverMediaReply } = createReplyDeliverer(params, sender, progress); let deliveredReactions = 0; const deliveredCount = () => sender.parts.length + deliveredReactions; const buildDeliveryReceipt = () => { const receipt = createMessageReceiptFromOutboundResults({ results: sender.parts.map((part) => buildTelegramProviderDeliveryResult({ message: part.result, messageId: part.messageId, fallbackChatId: params.chatId, ...params.thread ? { successfulSendThread: params.thread } : {} })) }); for (const [index, part] of receipt.parts.entries()) part.index = index; return receipt; }; for (const originalReply of normalizedReplies) { let reply = canonicalizeTelegramPresentationPayload(originalReply, { allowWebAppButtons: resolveTelegramTargetChatType(params.chatId) === "direct", richTables: params.richMessages === true && params.textMode !== "html" }); const mediaList = reply.mediaUrls?.length ? reply.mediaUrls : reply.mediaUrl ? [reply.mediaUrl] : []; const hasMedia = mediaList.length > 0; const presentation = normalizeMessagePresentation(reply.presentation); const interactive = reply.interactive; const resolvedReplyText = resolveTelegramInteractiveTextFallback({ text: reply.text, interactive, presentation }) ?? reply.text ?? ""; if (resolvedReplyText !== (reply.text ?? "")) reply = { ...reply, text: resolvedReplyText }; const telegramData = reply.channelData?.telegram; const reactionEmoji = typeof telegramData?.reaction?.emoji === "string" ? telegramData.reaction.emoji : void 0; const replyToMode = params.replyToMode === "off" && (reply.replyToTag === true || reply.replyToCurrent === true) ? "all" : params.replyToMode; const replyToId = replyToMode === "off" ? void 0 : parseStrictInteger(reply.replyToId); const targetId = parseStrictPositiveInteger(telegramData?.reaction?.replyToId ?? replyToId); if (reactionEmoji && typeof targetId !== "number") { params.runtime.error?.(danger("Telegram reaction requires a reply target")); continue; } if (!resolvedReplyText && !hasMedia && !reactionEmoji) { if (reply.audioAsVoice) { logVerbose("telegram reply has audioAsVoice without media/text; skipping"); continue; } params.runtime.error?.(danger("reply missing text/media")); continue; } const rawContent = resolvedReplyText; const spokenHookContent = !rawContent && reply.audioAsVoice === true && reply.spokenText?.trim() ? reply.spokenText : void 0; const hookContent = spokenHookContent ?? rawContent; const replyQuote = resolveReplyQuoteForSend({ replyToId, replyQuoteByMessageId: params.replyQuoteByMessageId, replyQuoteMessageId: params.replyQuoteMessageId, replyQuoteText: params.replyQuoteText, replyQuotePosition: params.replyQuotePosition, replyQuoteEntities: params.replyQuoteEntities }); if (hasMessageSendingHooks) { const hookResult = await hookRunner?.runMessageSending({ to: params.chatId, content: hookContent, replyToId, threadId: params.thread?.id, metadata: { channel: "telegram", mediaUrls: mediaList, threadId: params.thread?.id } }, { channelId: "telegram", accountId: params.accountId, conversationId: params.chatId }); if (hookResult?.cancel) continue; if (typeof hookResult?.content === "string" && hookResult.content !== hookContent) { progress.promptContext?.detach(); reply = spokenHookContent ? { ...reply, spokenText: hookResult.content } : { ...reply, text: hookResult.content }; } } let contentForSentHook = reply.text || (reply.audioAsVoice === true ? resolveVoiceFallbackText(reply) : "") || ""; const sentHookContext = { sessionKeyForInternalHooks: params.sessionKeyForInternalHooks, chatId: params.chatId, accountId: params.accountId, isGroup: params.mirrorIsGroup, groupId: params.mirrorGroupId }; try { const deliveredCountBeforeReply = deliveredCount(); const replyMarkup = buildInlineKeyboard(resolveTelegramInlineButtons({ buttons: telegramData?.buttons, presentation, interactive })); let firstDeliveredMessageId; let deliveredMediaUrls = []; if (reactionEmoji && typeof targetId === "number") { await params.onPlatformSendDispatch?.(); params.assertPlatformSendAuthorized?.(); const reactionResult = await reactMessageTelegram(params.chatId, targetId, reactionEmoji, { cfg: params.cfg ?? { channels: { telegram: { botToken: params.token } } }, token: params.token, accountId: params.accountId, api: params.bot.api, verbose: false }); if (reactionResult.ok) deliveredReactions += 1; else { params.runtime.error?.(danger(reactionResult.warning)); continue; } } const textReply = { text: reply.text || "", replyMarkup, quote: replyQuote, replyToId, replyToMode }; if (mediaList.length === 0 && resolvedReplyText) firstDeliveredMessageId = await deliverTextReply(textReply); else if (mediaList.length > 0) { const mediaDelivery = await deliverMediaReply({ ...textReply, reply, mediaList }); firstDeliveredMessageId = mediaDelivery.firstDeliveredMessageId; deliveredMediaUrls = mediaDelivery.mediaUrls; if (mediaDelivery.visibleFallbackText !== void 0) contentForSentHook = mediaDelivery.visibleFallbackText; } await maybePinFirstDeliveredMessage({ pin: reply.delivery?.pin, bot: params.bot, chatId: params.chatId, firstDeliveredMessageId }); if (deliveredCount() > deliveredCountBeforeReply && transcriptMirror) deliveredContents.push({ text: contentForSentHook, mediaUrls: deliveredMediaUrls }); emitTelegramMessageSentHooks({ ...sentHookContext, content: contentForSentHook, success: deliveredCount() > deliveredCountBeforeReply, messageId: firstDeliveredMessageId }); } catch (error) { emitTelegramMessageSentHooks({ ...sentHookContext, content: contentForSentHook, success: false, error: formatErrorMessage(error) }); sender.fail(error, 0, sender.parts.length ? { receipt: buildDeliveryReceipt() } : void 0); } } if (deliveredCount() > 0 && transcriptMirror) { const text = deliveredContents.map((content) => content.text).filter(Boolean).join("\n\n"); const mediaUrls = deliveredContents.flatMap((content) => content.mediaUrls); if (text || mediaUrls.length > 0) try { await transcriptMirror({ text: text || void 0, mediaUrls: mediaUrls.length > 0 ? mediaUrls : void 0 }); } catch (mirrorErr) { logVerbose(`telegram transcriptMirror failed: ${formatErrorMessage(mirrorErr)}`); } } return { delivered: deliveredCount() > 0, ...sender.parts.length ? { receipt: buildDeliveryReceipt() } : {} }; } //#endregion export { emitTelegramMessageSentHooks as a, buildTelegramNativeQuoteCandidate as i, deliverStructuredReplies as n, addTelegramNativeQuoteCandidate as r, deliverReplies as t };