mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-11 01:44:33 +00:00
fix: persist resolved telegram delivery targets at runtime
This commit is contained in:
@@ -29,7 +29,12 @@ import { renderTelegramHtmlText } from "./format.js";
|
||||
import { isRecoverableTelegramNetworkError } from "./network-errors.js";
|
||||
import { makeProxyFetch } from "./proxy.js";
|
||||
import { recordSentMessage } from "./sent-message-cache.js";
|
||||
import { parseTelegramTarget, stripTelegramInternalPrefixes } from "./targets.js";
|
||||
import { maybePersistResolvedTelegramTarget } from "./target-writeback.js";
|
||||
import {
|
||||
normalizeTelegramChatId,
|
||||
normalizeTelegramLookupTarget,
|
||||
parseTelegramTarget,
|
||||
} from "./targets.js";
|
||||
import { resolveTelegramVoiceSend } from "./voice.js";
|
||||
|
||||
type TelegramApi = Bot["api"];
|
||||
@@ -136,42 +141,56 @@ function resolveToken(explicit: string | undefined, params: { accountId: string;
|
||||
return params.token.trim();
|
||||
}
|
||||
|
||||
function normalizeChatId(to: string): string {
|
||||
const trimmed = to.trim();
|
||||
if (!trimmed) {
|
||||
throw new Error("Recipient is required for Telegram sends");
|
||||
async function resolveChatId(
|
||||
to: string,
|
||||
params: { api: TelegramApiOverride; verbose?: boolean },
|
||||
): Promise<string> {
|
||||
const numericChatId = normalizeTelegramChatId(to);
|
||||
if (numericChatId) {
|
||||
return numericChatId;
|
||||
}
|
||||
const lookupTarget = normalizeTelegramLookupTarget(to);
|
||||
const getChat = params.api.getChat;
|
||||
if (!lookupTarget || typeof getChat !== "function") {
|
||||
throw new Error("Telegram recipient must be a numeric chat ID");
|
||||
}
|
||||
try {
|
||||
const chat = await getChat.call(params.api, lookupTarget);
|
||||
const resolved = normalizeTelegramChatId(String(chat?.id ?? ""));
|
||||
if (!resolved) {
|
||||
throw new Error(`resolved chat id is not numeric (${String(chat?.id ?? "")})`);
|
||||
}
|
||||
if (params.verbose) {
|
||||
sendLogger.warn(`telegram recipient ${lookupTarget} resolved to numeric chat id ${resolved}`);
|
||||
}
|
||||
return resolved;
|
||||
} catch (err) {
|
||||
const detail = formatErrorMessage(err);
|
||||
throw new Error(
|
||||
`Telegram recipient ${lookupTarget} could not be resolved to a numeric chat ID (${detail})`,
|
||||
{ cause: err },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Common internal prefixes that sometimes leak into outbound sends.
|
||||
// - ctx.To uses `telegram:<id>`
|
||||
// - group sessions often use `telegram:group:<id>`
|
||||
let normalized = stripTelegramInternalPrefixes(trimmed);
|
||||
|
||||
// Accept t.me links for public chats/channels.
|
||||
// (Invite links like `t.me/+...` are not resolvable via Bot API.)
|
||||
const m =
|
||||
/^https?:\/\/t\.me\/([A-Za-z0-9_]+)$/i.exec(normalized) ??
|
||||
/^t\.me\/([A-Za-z0-9_]+)$/i.exec(normalized);
|
||||
if (m?.[1]) {
|
||||
normalized = `@${m[1]}`;
|
||||
}
|
||||
|
||||
if (!normalized) {
|
||||
throw new Error("Recipient is required for Telegram sends");
|
||||
}
|
||||
if (normalized.startsWith("@")) {
|
||||
return normalized;
|
||||
}
|
||||
if (/^-?\d+$/.test(normalized)) {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
// If the user passed a username without `@`, assume they meant a public chat/channel.
|
||||
if (/^[A-Za-z0-9_]{5,}$/i.test(normalized)) {
|
||||
return `@${normalized}`;
|
||||
}
|
||||
|
||||
return normalized;
|
||||
async function resolveAndPersistChatId(params: {
|
||||
cfg: ReturnType<typeof loadConfig>;
|
||||
api: TelegramApiOverride;
|
||||
lookupTarget: string;
|
||||
persistTarget: string;
|
||||
verbose?: boolean;
|
||||
}): Promise<string> {
|
||||
const chatId = await resolveChatId(params.lookupTarget, {
|
||||
api: params.api,
|
||||
verbose: params.verbose,
|
||||
});
|
||||
await maybePersistResolvedTelegramTarget({
|
||||
cfg: params.cfg,
|
||||
rawTarget: params.persistTarget,
|
||||
resolvedChatId: chatId,
|
||||
verbose: params.verbose,
|
||||
});
|
||||
return chatId;
|
||||
}
|
||||
|
||||
function normalizeMessageId(raw: string | number): number {
|
||||
@@ -434,7 +453,13 @@ export async function sendMessageTelegram(
|
||||
): Promise<TelegramSendResult> {
|
||||
const { cfg, account, api } = resolveTelegramApiContext(opts);
|
||||
const target = parseTelegramTarget(to);
|
||||
const chatId = normalizeChatId(target.chatId);
|
||||
const chatId = await resolveAndPersistChatId({
|
||||
cfg,
|
||||
api,
|
||||
lookupTarget: target.chatId,
|
||||
persistTarget: to,
|
||||
verbose: opts.verbose,
|
||||
});
|
||||
const mediaUrl = opts.mediaUrl?.trim();
|
||||
const replyMarkup = buildInlineKeyboard(opts.buttons);
|
||||
|
||||
@@ -722,7 +747,14 @@ export async function reactMessageTelegram(
|
||||
opts: TelegramReactionOpts = {},
|
||||
): Promise<{ ok: true } | { ok: false; warning: string }> {
|
||||
const { cfg, account, api } = resolveTelegramApiContext(opts);
|
||||
const chatId = normalizeChatId(String(chatIdInput));
|
||||
const rawTarget = String(chatIdInput);
|
||||
const chatId = await resolveAndPersistChatId({
|
||||
cfg,
|
||||
api,
|
||||
lookupTarget: rawTarget,
|
||||
persistTarget: rawTarget,
|
||||
verbose: opts.verbose,
|
||||
});
|
||||
const messageId = normalizeMessageId(messageIdInput);
|
||||
const requestWithDiag = createTelegramRequestWithDiag({
|
||||
cfg,
|
||||
@@ -768,7 +800,14 @@ export async function deleteMessageTelegram(
|
||||
opts: TelegramDeleteOpts = {},
|
||||
): Promise<{ ok: true }> {
|
||||
const { cfg, account, api } = resolveTelegramApiContext(opts);
|
||||
const chatId = normalizeChatId(String(chatIdInput));
|
||||
const rawTarget = String(chatIdInput);
|
||||
const chatId = await resolveAndPersistChatId({
|
||||
cfg,
|
||||
api,
|
||||
lookupTarget: rawTarget,
|
||||
persistTarget: rawTarget,
|
||||
verbose: opts.verbose,
|
||||
});
|
||||
const messageId = normalizeMessageId(messageIdInput);
|
||||
const requestWithDiag = createTelegramRequestWithDiag({
|
||||
cfg,
|
||||
@@ -807,7 +846,14 @@ export async function editMessageTelegram(
|
||||
...opts,
|
||||
cfg: opts.cfg,
|
||||
});
|
||||
const chatId = normalizeChatId(String(chatIdInput));
|
||||
const rawTarget = String(chatIdInput);
|
||||
const chatId = await resolveAndPersistChatId({
|
||||
cfg,
|
||||
api,
|
||||
lookupTarget: rawTarget,
|
||||
persistTarget: rawTarget,
|
||||
verbose: opts.verbose,
|
||||
});
|
||||
const messageId = normalizeMessageId(messageIdInput);
|
||||
const requestWithDiag = createTelegramRequestWithDiag({
|
||||
cfg,
|
||||
@@ -928,7 +974,13 @@ export async function sendStickerTelegram(
|
||||
|
||||
const { cfg, account, api } = resolveTelegramApiContext(opts);
|
||||
const target = parseTelegramTarget(to);
|
||||
const chatId = normalizeChatId(target.chatId);
|
||||
const chatId = await resolveAndPersistChatId({
|
||||
cfg,
|
||||
api,
|
||||
lookupTarget: target.chatId,
|
||||
persistTarget: to,
|
||||
verbose: opts.verbose,
|
||||
});
|
||||
|
||||
const threadParams = buildTelegramThreadReplyParams({
|
||||
targetMessageThreadId: target.messageThreadId,
|
||||
@@ -1004,7 +1056,13 @@ export async function sendPollTelegram(
|
||||
): Promise<{ messageId: string; chatId: string; pollId?: string }> {
|
||||
const { cfg, account, api } = resolveTelegramApiContext(opts);
|
||||
const target = parseTelegramTarget(to);
|
||||
const chatId = normalizeChatId(target.chatId);
|
||||
const chatId = await resolveAndPersistChatId({
|
||||
cfg,
|
||||
api,
|
||||
lookupTarget: target.chatId,
|
||||
persistTarget: to,
|
||||
verbose: opts.verbose,
|
||||
});
|
||||
|
||||
// Normalize the poll input (validates question, options, maxSelections)
|
||||
const normalizedPoll = normalizePollInput(poll, { maxOptions: 10 });
|
||||
@@ -1130,10 +1188,16 @@ export async function createForumTopicTelegram(
|
||||
const token = resolveToken(opts.token, account);
|
||||
// Accept topic-qualified targets (e.g. telegram:group:<id>:topic:<thread>)
|
||||
// but createForumTopic must always target the base supergroup chat id.
|
||||
const target = parseTelegramTarget(chatId);
|
||||
const normalizedChatId = normalizeChatId(target.chatId);
|
||||
const client = resolveTelegramClientOptions(account);
|
||||
const api = opts.api ?? new Bot(token, client ? { client } : undefined).api;
|
||||
const target = parseTelegramTarget(chatId);
|
||||
const normalizedChatId = await resolveAndPersistChatId({
|
||||
cfg,
|
||||
api,
|
||||
lookupTarget: target.chatId,
|
||||
persistTarget: chatId,
|
||||
verbose: opts.verbose,
|
||||
});
|
||||
|
||||
const request = createTelegramRetryRunner({
|
||||
retry: opts.retry,
|
||||
|
||||
Reference in New Issue
Block a user