telegram: centralize api error logging
This commit is contained in:
41
src/telegram/api-logging.ts
Normal file
41
src/telegram/api-logging.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { danger } from "../globals.js";
|
||||
import { formatErrorMessage } from "../infra/errors.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
|
||||
export type TelegramApiLogger = (message: string) => void;
|
||||
|
||||
type TelegramApiLoggingParams<T> = {
|
||||
operation: string;
|
||||
fn: () => Promise<T>;
|
||||
runtime?: RuntimeEnv;
|
||||
logger?: TelegramApiLogger;
|
||||
shouldLog?: (err: unknown) => boolean;
|
||||
};
|
||||
|
||||
const fallbackLogger = createSubsystemLogger("telegram/api");
|
||||
|
||||
function resolveTelegramApiLogger(runtime?: RuntimeEnv, logger?: TelegramApiLogger) {
|
||||
if (logger) return logger;
|
||||
if (runtime?.error) return runtime.error;
|
||||
return (message: string) => fallbackLogger.error(message);
|
||||
}
|
||||
|
||||
export async function withTelegramApiErrorLogging<T>({
|
||||
operation,
|
||||
fn,
|
||||
runtime,
|
||||
logger,
|
||||
shouldLog,
|
||||
}: TelegramApiLoggingParams<T>): Promise<T> {
|
||||
try {
|
||||
return await fn();
|
||||
} catch (err) {
|
||||
if (!shouldLog || shouldLog(err)) {
|
||||
const errText = formatErrorMessage(err);
|
||||
const log = resolveTelegramApiLogger(runtime, logger);
|
||||
log(danger(`telegram ${operation} failed: ${errText}`));
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import { loadConfig } from "../config/config.js";
|
||||
import { writeConfigFile } from "../config/io.js";
|
||||
import { danger, logVerbose, warn } from "../globals.js";
|
||||
import { resolveMedia } from "./bot/delivery.js";
|
||||
import { withTelegramApiErrorLogging } from "./api-logging.js";
|
||||
import { resolveTelegramForumThreadId } from "./bot/helpers.js";
|
||||
import type { TelegramMessage } from "./bot/types.js";
|
||||
import { firstDefined, isSenderAllowed, normalizeAllowFromWithStore } from "./bot-access.js";
|
||||
@@ -180,7 +181,11 @@ export const registerTelegramHandlers = ({
|
||||
if (!callback) return;
|
||||
if (shouldSkipUpdate(ctx)) return;
|
||||
// Answer immediately to prevent Telegram from retrying while we process
|
||||
await bot.api.answerCallbackQuery(callback.id).catch(() => {});
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "answerCallbackQuery",
|
||||
runtime,
|
||||
fn: () => bot.api.answerCallbackQuery(callback.id),
|
||||
}).catch(() => {});
|
||||
try {
|
||||
const data = (callback.data ?? "").trim();
|
||||
const callbackMessage = callback.message;
|
||||
@@ -577,11 +582,14 @@ export const registerTelegramHandlers = ({
|
||||
const errMsg = String(mediaErr);
|
||||
if (errMsg.includes("exceeds") && errMsg.includes("MB limit")) {
|
||||
const limitMb = Math.round(mediaMaxBytes / (1024 * 1024));
|
||||
await bot.api
|
||||
.sendMessage(chatId, `⚠️ File too large. Maximum size is ${limitMb}MB.`, {
|
||||
reply_to_message_id: msg.message_id,
|
||||
})
|
||||
.catch(() => {});
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendMessage",
|
||||
runtime,
|
||||
fn: () =>
|
||||
bot.api.sendMessage(chatId, `⚠️ File too large. Maximum size is ${limitMb}MB.`, {
|
||||
reply_to_message_id: msg.message_id,
|
||||
}),
|
||||
}).catch(() => {});
|
||||
logger.warn({ chatId, error: errMsg }, "media exceeds size limit");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import { shouldAckReaction as shouldAckReactionGate } from "../channels/ack-reac
|
||||
import { resolveMentionGatingWithBypass } from "../channels/mention-gating.js";
|
||||
import { resolveControlCommandGate } from "../channels/command-gating.js";
|
||||
import { logInboundDrop } from "../channels/logging.js";
|
||||
import { withTelegramApiErrorLogging } from "./api-logging.js";
|
||||
import {
|
||||
buildGroupLabel,
|
||||
buildSenderLabel,
|
||||
@@ -165,16 +166,19 @@ export const buildTelegramMessageContext = async ({
|
||||
}
|
||||
|
||||
const sendTyping = async () => {
|
||||
await bot.api.sendChatAction(chatId, "typing", buildTypingThreadParams(resolvedThreadId));
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendChatAction",
|
||||
fn: () => bot.api.sendChatAction(chatId, "typing", buildTypingThreadParams(resolvedThreadId)),
|
||||
});
|
||||
};
|
||||
|
||||
const sendRecordVoice = async () => {
|
||||
try {
|
||||
await bot.api.sendChatAction(
|
||||
chatId,
|
||||
"record_voice",
|
||||
buildTypingThreadParams(resolvedThreadId),
|
||||
);
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendChatAction",
|
||||
fn: () =>
|
||||
bot.api.sendChatAction(chatId, "record_voice", buildTypingThreadParams(resolvedThreadId)),
|
||||
});
|
||||
} catch (err) {
|
||||
logVerbose(`telegram record_voice cue failed for chat ${chatId}: ${String(err)}`);
|
||||
}
|
||||
@@ -227,19 +231,23 @@ export const buildTelegramMessageContext = async ({
|
||||
},
|
||||
"telegram pairing request",
|
||||
);
|
||||
await bot.api.sendMessage(
|
||||
chatId,
|
||||
[
|
||||
"Clawdbot: access not configured.",
|
||||
"",
|
||||
`Your Telegram user id: ${telegramUserId}`,
|
||||
"",
|
||||
`Pairing code: ${code}`,
|
||||
"",
|
||||
"Ask the bot owner to approve with:",
|
||||
formatCliCommand("clawdbot pairing approve telegram <code>"),
|
||||
].join("\n"),
|
||||
);
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendMessage",
|
||||
fn: () =>
|
||||
bot.api.sendMessage(
|
||||
chatId,
|
||||
[
|
||||
"Clawdbot: access not configured.",
|
||||
"",
|
||||
`Your Telegram user id: ${telegramUserId}`,
|
||||
"",
|
||||
`Pairing code: ${code}`,
|
||||
"",
|
||||
"Ask the bot owner to approve with:",
|
||||
formatCliCommand("clawdbot pairing approve telegram <code>"),
|
||||
].join("\n"),
|
||||
),
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
logVerbose(`telegram pairing reply failed for chat ${chatId}: ${String(err)}`);
|
||||
@@ -408,7 +416,10 @@ export const buildTelegramMessageContext = async ({
|
||||
typeof api.setMessageReaction === "function" ? api.setMessageReaction.bind(api) : null;
|
||||
const ackReactionPromise =
|
||||
shouldAckReaction() && msg.message_id && reactionApi
|
||||
? reactionApi(chatId, msg.message_id, [{ type: "emoji", emoji: ackReaction }]).then(
|
||||
? withTelegramApiErrorLogging({
|
||||
operation: "setMessageReaction",
|
||||
fn: () => reactionApi(chatId, msg.message_id, [{ type: "emoji", emoji: ackReaction }]),
|
||||
}).then(
|
||||
() => true,
|
||||
(err) => {
|
||||
logVerbose(`telegram react failed for chat ${chatId}: ${String(err)}`);
|
||||
|
||||
@@ -17,6 +17,7 @@ import { dispatchReplyWithBufferedBlockDispatcher } from "../auto-reply/reply/pr
|
||||
import { finalizeInboundContext } from "../auto-reply/reply/inbound-context.js";
|
||||
import { danger, logVerbose } from "../globals.js";
|
||||
import { resolveMarkdownTableMode } from "../config/markdown-tables.js";
|
||||
import { withTelegramApiErrorLogging } from "./api-logging.js";
|
||||
import {
|
||||
normalizeTelegramCommandName,
|
||||
TELEGRAM_COMMAND_NAME_PATTERN,
|
||||
@@ -134,11 +135,17 @@ async function resolveTelegramCommandAuth(params: {
|
||||
const senderUsername = msg.from?.username ?? "";
|
||||
|
||||
if (isGroup && groupConfig?.enabled === false) {
|
||||
await bot.api.sendMessage(chatId, "This group is disabled.");
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendMessage",
|
||||
fn: () => bot.api.sendMessage(chatId, "This group is disabled."),
|
||||
});
|
||||
return null;
|
||||
}
|
||||
if (isGroup && topicConfig?.enabled === false) {
|
||||
await bot.api.sendMessage(chatId, "This topic is disabled.");
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendMessage",
|
||||
fn: () => bot.api.sendMessage(chatId, "This topic is disabled."),
|
||||
});
|
||||
return null;
|
||||
}
|
||||
if (requireAuth && isGroup && hasGroupAllowOverride) {
|
||||
@@ -150,7 +157,10 @@ async function resolveTelegramCommandAuth(params: {
|
||||
senderUsername,
|
||||
})
|
||||
) {
|
||||
await bot.api.sendMessage(chatId, "You are not authorized to use this command.");
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendMessage",
|
||||
fn: () => bot.api.sendMessage(chatId, "You are not authorized to use this command."),
|
||||
});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -159,7 +169,10 @@ async function resolveTelegramCommandAuth(params: {
|
||||
const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy;
|
||||
const groupPolicy = telegramCfg.groupPolicy ?? defaultGroupPolicy ?? "open";
|
||||
if (groupPolicy === "disabled") {
|
||||
await bot.api.sendMessage(chatId, "Telegram group commands are disabled.");
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendMessage",
|
||||
fn: () => bot.api.sendMessage(chatId, "Telegram group commands are disabled."),
|
||||
});
|
||||
return null;
|
||||
}
|
||||
if (groupPolicy === "allowlist" && requireAuth) {
|
||||
@@ -171,13 +184,19 @@ async function resolveTelegramCommandAuth(params: {
|
||||
senderUsername,
|
||||
})
|
||||
) {
|
||||
await bot.api.sendMessage(chatId, "You are not authorized to use this command.");
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendMessage",
|
||||
fn: () => bot.api.sendMessage(chatId, "You are not authorized to use this command."),
|
||||
});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
const groupAllowlist = resolveGroupPolicy(chatId);
|
||||
if (groupAllowlist.allowlistEnabled && !groupAllowlist.allowed) {
|
||||
await bot.api.sendMessage(chatId, "This group is not allowed.");
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendMessage",
|
||||
fn: () => bot.api.sendMessage(chatId, "This group is not allowed."),
|
||||
});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -197,7 +216,10 @@ async function resolveTelegramCommandAuth(params: {
|
||||
modeWhenAccessGroupsOff: "configured",
|
||||
});
|
||||
if (requireAuth && !commandAuthorized) {
|
||||
await bot.api.sendMessage(chatId, "You are not authorized to use this command.");
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendMessage",
|
||||
fn: () => bot.api.sendMessage(chatId, "You are not authorized to use this command."),
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -300,9 +322,11 @@ export const registerTelegramNativeCommands = ({
|
||||
];
|
||||
|
||||
if (allCommands.length > 0) {
|
||||
bot.api.setMyCommands(allCommands).catch((err) => {
|
||||
runtime.error?.(danger(`telegram setMyCommands failed: ${String(err)}`));
|
||||
});
|
||||
void withTelegramApiErrorLogging({
|
||||
operation: "setMyCommands",
|
||||
runtime,
|
||||
fn: () => bot.api.setMyCommands(allCommands),
|
||||
}).catch(() => {});
|
||||
|
||||
if (typeof (bot as unknown as { command?: unknown }).command !== "function") {
|
||||
logVerbose("telegram: bot.command unavailable; skipping native handlers");
|
||||
@@ -376,9 +400,14 @@ export const registerTelegramNativeCommands = ({
|
||||
);
|
||||
}
|
||||
const replyMarkup = buildInlineKeyboard(rows);
|
||||
await bot.api.sendMessage(chatId, title, {
|
||||
...(replyMarkup ? { reply_markup: replyMarkup } : {}),
|
||||
...(resolvedThreadId != null ? { message_thread_id: resolvedThreadId } : {}),
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendMessage",
|
||||
runtime,
|
||||
fn: () =>
|
||||
bot.api.sendMessage(chatId, title, {
|
||||
...(replyMarkup ? { reply_markup: replyMarkup } : {}),
|
||||
...(resolvedThreadId != null ? { message_thread_id: resolvedThreadId } : {}),
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -492,7 +521,11 @@ export const registerTelegramNativeCommands = ({
|
||||
const commandBody = `/${pluginCommand.command}${rawText ? ` ${rawText}` : ""}`;
|
||||
const match = matchPluginCommand(commandBody);
|
||||
if (!match) {
|
||||
await bot.api.sendMessage(chatId, "Command not found.");
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendMessage",
|
||||
runtime,
|
||||
fn: () => bot.api.sendMessage(chatId, "Command not found."),
|
||||
});
|
||||
return;
|
||||
}
|
||||
const auth = await resolveTelegramCommandAuth({
|
||||
@@ -543,8 +576,10 @@ export const registerTelegramNativeCommands = ({
|
||||
}
|
||||
}
|
||||
} else if (nativeDisabledExplicit) {
|
||||
bot.api.setMyCommands([]).catch((err) => {
|
||||
runtime.error?.(danger(`telegram clear commands failed: ${String(err)}`));
|
||||
});
|
||||
void withTelegramApiErrorLogging({
|
||||
operation: "setMyCommands",
|
||||
runtime,
|
||||
fn: () => bot.api.setMyCommands([]),
|
||||
}).catch(() => {});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -24,6 +24,7 @@ import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import { formatUncaughtError } from "../infra/errors.js";
|
||||
import { enqueueSystemEvent } from "../infra/system-events.js";
|
||||
import { getChildLogger } from "../logging.js";
|
||||
import { withTelegramApiErrorLogging } from "./api-logging.js";
|
||||
import { resolveAgentRoute } from "../routing/resolve-route.js";
|
||||
import { resolveThreadSessionKeys } from "../routing/session-key.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
@@ -261,7 +262,11 @@ export function createTelegramBot(opts: TelegramBotOptions) {
|
||||
}
|
||||
if (typeof botHasTopicsEnabled === "boolean") return botHasTopicsEnabled;
|
||||
try {
|
||||
const me = (await bot.api.getMe()) as { has_topics_enabled?: boolean };
|
||||
const me = (await withTelegramApiErrorLogging({
|
||||
operation: "getMe",
|
||||
runtime,
|
||||
fn: () => bot.api.getMe(),
|
||||
})) as { has_topics_enabled?: boolean };
|
||||
botHasTopicsEnabled = Boolean(me?.has_topics_enabled);
|
||||
} catch (err) {
|
||||
logVerbose(`telegram getMe failed: ${String(err)}`);
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
markdownToTelegramHtml,
|
||||
renderTelegramHtmlText,
|
||||
} from "../format.js";
|
||||
import { withTelegramApiErrorLogging } from "../api-logging.js";
|
||||
import { chunkMarkdownTextWithMode, type ChunkMode } from "../../auto-reply/chunk.js";
|
||||
import { splitTelegramCaption } from "../caption.js";
|
||||
import type { ReplyPayload } from "../../auto-reply/types.js";
|
||||
@@ -25,24 +26,6 @@ import type { TelegramContext } from "./types.js";
|
||||
const PARSE_ERR_RE = /can't parse entities|parse entities|find end of the entity/i;
|
||||
const VOICE_FORBIDDEN_RE = /VOICE_MESSAGES_FORBIDDEN/;
|
||||
|
||||
/**
|
||||
* Wraps a Telegram API call with error logging. Ensures network failures are
|
||||
* logged with context before propagating, preventing silent unhandled rejections.
|
||||
*/
|
||||
async function withMediaErrorHandler<T>(
|
||||
operation: string,
|
||||
runtime: RuntimeEnv,
|
||||
fn: () => Promise<T>,
|
||||
): Promise<T> {
|
||||
try {
|
||||
return await fn();
|
||||
} catch (err) {
|
||||
const errText = formatErrorMessage(err);
|
||||
runtime.error?.(danger(`telegram ${operation} failed: ${errText}`));
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function deliverReplies(params: {
|
||||
replies: ReplyPayload[];
|
||||
chatId: string;
|
||||
@@ -164,17 +147,23 @@ export async function deliverReplies(params: {
|
||||
mediaParams.message_thread_id = threadParams.message_thread_id;
|
||||
}
|
||||
if (isGif) {
|
||||
await withMediaErrorHandler("sendAnimation", runtime, () =>
|
||||
bot.api.sendAnimation(chatId, file, { ...mediaParams }),
|
||||
);
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendAnimation",
|
||||
runtime,
|
||||
fn: () => bot.api.sendAnimation(chatId, file, { ...mediaParams }),
|
||||
});
|
||||
} else if (kind === "image") {
|
||||
await withMediaErrorHandler("sendPhoto", runtime, () =>
|
||||
bot.api.sendPhoto(chatId, file, { ...mediaParams }),
|
||||
);
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendPhoto",
|
||||
runtime,
|
||||
fn: () => bot.api.sendPhoto(chatId, file, { ...mediaParams }),
|
||||
});
|
||||
} else if (kind === "video") {
|
||||
await withMediaErrorHandler("sendVideo", runtime, () =>
|
||||
bot.api.sendVideo(chatId, file, { ...mediaParams }),
|
||||
);
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendVideo",
|
||||
runtime,
|
||||
fn: () => bot.api.sendVideo(chatId, file, { ...mediaParams }),
|
||||
});
|
||||
} else if (kind === "audio") {
|
||||
const { useVoice } = resolveTelegramVoiceSend({
|
||||
wantsVoice: reply.audioAsVoice === true, // default false (backward compatible)
|
||||
@@ -187,9 +176,12 @@ export async function deliverReplies(params: {
|
||||
// Switch typing indicator to record_voice before sending.
|
||||
await params.onVoiceRecording?.();
|
||||
try {
|
||||
await withMediaErrorHandler("sendVoice", runtime, () =>
|
||||
bot.api.sendVoice(chatId, file, { ...mediaParams }),
|
||||
);
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendVoice",
|
||||
runtime,
|
||||
shouldLog: (err) => !isVoiceMessagesForbidden(err),
|
||||
fn: () => bot.api.sendVoice(chatId, file, { ...mediaParams }),
|
||||
});
|
||||
} catch (voiceErr) {
|
||||
// Fall back to text if voice messages are forbidden in this chat.
|
||||
// This happens when the recipient has Telegram Premium privacy settings
|
||||
@@ -222,14 +214,18 @@ export async function deliverReplies(params: {
|
||||
}
|
||||
} else {
|
||||
// Audio file - displays with metadata (title, duration) - DEFAULT
|
||||
await withMediaErrorHandler("sendAudio", runtime, () =>
|
||||
bot.api.sendAudio(chatId, file, { ...mediaParams }),
|
||||
);
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendAudio",
|
||||
runtime,
|
||||
fn: () => bot.api.sendAudio(chatId, file, { ...mediaParams }),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
await withMediaErrorHandler("sendDocument", runtime, () =>
|
||||
bot.api.sendDocument(chatId, file, { ...mediaParams }),
|
||||
);
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendDocument",
|
||||
runtime,
|
||||
fn: () => bot.api.sendDocument(chatId, file, { ...mediaParams }),
|
||||
});
|
||||
}
|
||||
if (replyToId && !hasReplied) {
|
||||
hasReplied = true;
|
||||
@@ -371,11 +367,17 @@ async function sendTelegramText(
|
||||
const textMode = opts?.textMode ?? "markdown";
|
||||
const htmlText = textMode === "html" ? text : markdownToTelegramHtml(text);
|
||||
try {
|
||||
const res = await bot.api.sendMessage(chatId, htmlText, {
|
||||
parse_mode: "HTML",
|
||||
...(linkPreviewOptions ? { link_preview_options: linkPreviewOptions } : {}),
|
||||
...(opts?.replyMarkup ? { reply_markup: opts.replyMarkup } : {}),
|
||||
...baseParams,
|
||||
const res = await withTelegramApiErrorLogging({
|
||||
operation: "sendMessage",
|
||||
runtime,
|
||||
shouldLog: (err) => !PARSE_ERR_RE.test(formatErrorMessage(err)),
|
||||
fn: () =>
|
||||
bot.api.sendMessage(chatId, htmlText, {
|
||||
parse_mode: "HTML",
|
||||
...(linkPreviewOptions ? { link_preview_options: linkPreviewOptions } : {}),
|
||||
...(opts?.replyMarkup ? { reply_markup: opts.replyMarkup } : {}),
|
||||
...baseParams,
|
||||
}),
|
||||
});
|
||||
return res.message_id;
|
||||
} catch (err) {
|
||||
@@ -383,10 +385,15 @@ async function sendTelegramText(
|
||||
if (PARSE_ERR_RE.test(errText)) {
|
||||
runtime.log?.(`telegram HTML parse failed; retrying without formatting: ${errText}`);
|
||||
const fallbackText = opts?.plainText ?? text;
|
||||
const res = await bot.api.sendMessage(chatId, fallbackText, {
|
||||
...(linkPreviewOptions ? { link_preview_options: linkPreviewOptions } : {}),
|
||||
...(opts?.replyMarkup ? { reply_markup: opts.replyMarkup } : {}),
|
||||
...baseParams,
|
||||
const res = await withTelegramApiErrorLogging({
|
||||
operation: "sendMessage",
|
||||
runtime,
|
||||
fn: () =>
|
||||
bot.api.sendMessage(chatId, fallbackText, {
|
||||
...(linkPreviewOptions ? { link_preview_options: linkPreviewOptions } : {}),
|
||||
...(opts?.replyMarkup ? { reply_markup: opts.replyMarkup } : {}),
|
||||
...baseParams,
|
||||
}),
|
||||
});
|
||||
return res.message_id;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { type ApiClientOptions, Bot, HttpError, InputFile } from "grammy";
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import { logVerbose } from "../globals.js";
|
||||
import { recordChannelActivity } from "../infra/channel-activity.js";
|
||||
import { withTelegramApiErrorLogging } from "./api-logging.js";
|
||||
import { formatErrorMessage, formatUncaughtError } from "../infra/errors.js";
|
||||
import { isDiagnosticFlagEnabled } from "../infra/diagnostic-flags.js";
|
||||
import type { RetryConfig } from "../infra/retry.js";
|
||||
@@ -210,7 +211,10 @@ export async function sendMessageTelegram(
|
||||
});
|
||||
const logHttpError = createTelegramHttpLogger(cfg);
|
||||
const requestWithDiag = <T>(fn: () => Promise<T>, label?: string) =>
|
||||
request(fn, label).catch((err) => {
|
||||
withTelegramApiErrorLogging({
|
||||
operation: label ?? "request",
|
||||
fn: () => request(fn, label),
|
||||
}).catch((err) => {
|
||||
logHttpError(label ?? "request", err);
|
||||
throw err;
|
||||
});
|
||||
@@ -442,7 +446,10 @@ export async function reactMessageTelegram(
|
||||
});
|
||||
const logHttpError = createTelegramHttpLogger(cfg);
|
||||
const requestWithDiag = <T>(fn: () => Promise<T>, label?: string) =>
|
||||
request(fn, label).catch((err) => {
|
||||
withTelegramApiErrorLogging({
|
||||
operation: label ?? "request",
|
||||
fn: () => request(fn, label),
|
||||
}).catch((err) => {
|
||||
logHttpError(label ?? "request", err);
|
||||
throw err;
|
||||
});
|
||||
@@ -492,7 +499,10 @@ export async function deleteMessageTelegram(
|
||||
});
|
||||
const logHttpError = createTelegramHttpLogger(cfg);
|
||||
const requestWithDiag = <T>(fn: () => Promise<T>, label?: string) =>
|
||||
request(fn, label).catch((err) => {
|
||||
withTelegramApiErrorLogging({
|
||||
operation: label ?? "request",
|
||||
fn: () => request(fn, label),
|
||||
}).catch((err) => {
|
||||
logHttpError(label ?? "request", err);
|
||||
throw err;
|
||||
});
|
||||
@@ -537,7 +547,10 @@ export async function editMessageTelegram(
|
||||
});
|
||||
const logHttpError = createTelegramHttpLogger(cfg);
|
||||
const requestWithDiag = <T>(fn: () => Promise<T>, label?: string) =>
|
||||
request(fn, label).catch((err) => {
|
||||
withTelegramApiErrorLogging({
|
||||
operation: label ?? "request",
|
||||
fn: () => request(fn, label),
|
||||
}).catch((err) => {
|
||||
logHttpError(label ?? "request", err);
|
||||
throw err;
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { type ApiClientOptions, Bot } from "grammy";
|
||||
import type { TelegramNetworkConfig } from "../config/types.telegram.js";
|
||||
import { resolveTelegramFetch } from "./fetch.js";
|
||||
import { withTelegramApiErrorLogging } from "./api-logging.js";
|
||||
|
||||
export async function setTelegramWebhook(opts: {
|
||||
token: string;
|
||||
@@ -14,9 +15,13 @@ export async function setTelegramWebhook(opts: {
|
||||
? { fetch: fetchImpl as unknown as ApiClientOptions["fetch"] }
|
||||
: undefined;
|
||||
const bot = new Bot(opts.token, client ? { client } : undefined);
|
||||
await bot.api.setWebhook(opts.url, {
|
||||
secret_token: opts.secret,
|
||||
drop_pending_updates: opts.dropPendingUpdates ?? false,
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "setWebhook",
|
||||
fn: () =>
|
||||
bot.api.setWebhook(opts.url, {
|
||||
secret_token: opts.secret,
|
||||
drop_pending_updates: opts.dropPendingUpdates ?? false,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -29,5 +34,8 @@ export async function deleteTelegramWebhook(opts: {
|
||||
? { fetch: fetchImpl as unknown as ApiClientOptions["fetch"] }
|
||||
: undefined;
|
||||
const bot = new Bot(opts.token, client ? { client } : undefined);
|
||||
await bot.api.deleteWebhook();
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "deleteWebhook",
|
||||
fn: () => bot.api.deleteWebhook(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
} from "../logging/diagnostic.js";
|
||||
import { resolveTelegramAllowedUpdates } from "./allowed-updates.js";
|
||||
import { createTelegramBot } from "./bot.js";
|
||||
import { withTelegramApiErrorLogging } from "./api-logging.js";
|
||||
|
||||
export async function startTelegramWebhook(opts: {
|
||||
token: string;
|
||||
@@ -97,9 +98,14 @@ export async function startTelegramWebhook(opts: {
|
||||
const publicUrl =
|
||||
opts.publicUrl ?? `http://${host === "0.0.0.0" ? "localhost" : host}:${port}${path}`;
|
||||
|
||||
await bot.api.setWebhook(publicUrl, {
|
||||
secret_token: opts.secret,
|
||||
allowed_updates: resolveTelegramAllowedUpdates(),
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "setWebhook",
|
||||
runtime,
|
||||
fn: () =>
|
||||
bot.api.setWebhook(publicUrl, {
|
||||
secret_token: opts.secret,
|
||||
allowed_updates: resolveTelegramAllowedUpdates(),
|
||||
}),
|
||||
});
|
||||
|
||||
await new Promise<void>((resolve) => server.listen(port, host, resolve));
|
||||
|
||||
Reference in New Issue
Block a user