refactor(telegram): simplify send/dispatch/target handling (#17819)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: fcb7aeeca3
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
This commit is contained in:
Ayaan Zaidi
2026-02-16 14:00:34 +05:30
committed by GitHub
parent 1f607bec49
commit b6a9741ba4
16 changed files with 1125 additions and 1141 deletions

View File

@@ -55,6 +55,10 @@ import {
resolveTelegramGroupAllowFromContext,
resolveTelegramThreadSpec,
} from "./bot/helpers.js";
import {
evaluateTelegramGroupBaseAccess,
evaluateTelegramGroupPolicyAccess,
} from "./group-access.js";
import { buildInlineKeyboard } from "./send.js";
const EMPTY_RESPONSE_FALLBACK = "No response generated. Please try again.";
@@ -172,68 +176,71 @@ async function resolveTelegramCommandAuth(params: {
effectiveGroupAllow,
hasGroupAllowOverride,
} = groupAllowContext;
const senderIdRaw = msg.from?.id;
const senderId = senderIdRaw ? String(senderIdRaw) : "";
const senderId = msg.from?.id ? String(msg.from.id) : "";
const senderUsername = msg.from?.username ?? "";
const isGroupSenderAllowed = () =>
senderIdRaw != null &&
isSenderAllowed({
allow: effectiveGroupAllow,
senderId: String(senderIdRaw),
senderUsername,
});
const rejectNotAuthorized = async () => {
const sendAuthMessage = async (text: string) => {
await withTelegramApiErrorLogging({
operation: "sendMessage",
fn: () => bot.api.sendMessage(chatId, "You are not authorized to use this command."),
fn: () => bot.api.sendMessage(chatId, text),
});
return null;
};
const rejectNotAuthorized = async () => {
return await sendAuthMessage("You are not authorized to use this command.");
};
if (isGroup && groupConfig?.enabled === false) {
await withTelegramApiErrorLogging({
operation: "sendMessage",
fn: () => bot.api.sendMessage(chatId, "This group is disabled."),
});
return null;
const baseAccess = evaluateTelegramGroupBaseAccess({
isGroup,
groupConfig,
topicConfig,
hasGroupAllowOverride,
effectiveGroupAllow,
senderId,
senderUsername,
enforceAllowOverride: requireAuth,
requireSenderForAllowOverride: true,
});
if (!baseAccess.allowed) {
if (baseAccess.reason === "group-disabled") {
return await sendAuthMessage("This group is disabled.");
}
if (baseAccess.reason === "topic-disabled") {
return await sendAuthMessage("This topic is disabled.");
}
return await rejectNotAuthorized();
}
if (isGroup && topicConfig?.enabled === false) {
await withTelegramApiErrorLogging({
operation: "sendMessage",
fn: () => bot.api.sendMessage(chatId, "This topic is disabled."),
});
return null;
}
if (requireAuth && isGroup && hasGroupAllowOverride) {
if (!isGroupSenderAllowed()) {
const policyAccess = evaluateTelegramGroupPolicyAccess({
isGroup,
chatId,
cfg,
telegramCfg,
topicConfig,
groupConfig,
effectiveGroupAllow,
senderId,
senderUsername,
resolveGroupPolicy,
enforcePolicy: useAccessGroups,
useTopicAndGroupOverrides: false,
enforceAllowlistAuthorization: requireAuth,
allowEmptyAllowlistEntries: true,
requireSenderForAllowlistAuthorization: true,
checkChatAllowlist: useAccessGroups,
});
if (!policyAccess.allowed) {
if (policyAccess.reason === "group-policy-disabled") {
return await sendAuthMessage("Telegram group commands are disabled.");
}
if (
policyAccess.reason === "group-policy-allowlist-no-sender" ||
policyAccess.reason === "group-policy-allowlist-unauthorized"
) {
return await rejectNotAuthorized();
}
}
if (isGroup && useAccessGroups) {
const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy;
const groupPolicy = telegramCfg.groupPolicy ?? defaultGroupPolicy ?? "open";
if (groupPolicy === "disabled") {
await withTelegramApiErrorLogging({
operation: "sendMessage",
fn: () => bot.api.sendMessage(chatId, "Telegram group commands are disabled."),
});
return null;
}
if (groupPolicy === "allowlist" && requireAuth) {
if (!isGroupSenderAllowed()) {
return await rejectNotAuthorized();
}
}
const groupAllowlist = resolveGroupPolicy(chatId);
if (groupAllowlist.allowlistEnabled && !groupAllowlist.allowed) {
await withTelegramApiErrorLogging({
operation: "sendMessage",
fn: () => bot.api.sendMessage(chatId, "This group is not allowed."),
});
return null;
if (policyAccess.reason === "group-chat-not-allowed") {
return await sendAuthMessage("This group is not allowed.");
}
}
@@ -252,11 +259,7 @@ async function resolveTelegramCommandAuth(params: {
modeWhenAccessGroupsOff: "configured",
});
if (requireAuth && !commandAuthorized) {
await withTelegramApiErrorLogging({
operation: "sendMessage",
fn: () => bot.api.sendMessage(chatId, "You are not authorized to use this command."),
});
return null;
return await rejectNotAuthorized();
}
return {
@@ -357,6 +360,60 @@ export const registerTelegramNativeCommands = ({
// Keep hidden commands callable by registering handlers for the full catalog.
syncTelegramMenuCommands({ bot, runtime, commandsToRegister });
const resolveCommandRuntimeContext = (params: {
msg: NonNullable<TelegramNativeCommandContext["message"]>;
isGroup: boolean;
isForum: boolean;
resolvedThreadId?: number;
}) => {
const { msg, isGroup, isForum, resolvedThreadId } = params;
const chatId = msg.chat.id;
const messageThreadId = (msg as { message_thread_id?: number }).message_thread_id;
const threadSpec = resolveTelegramThreadSpec({
isGroup,
isForum,
messageThreadId,
});
const parentPeer = buildTelegramParentPeer({ isGroup, resolvedThreadId, chatId });
const route = resolveAgentRoute({
cfg,
channel: "telegram",
accountId,
peer: {
kind: isGroup ? "group" : "direct",
id: isGroup ? buildTelegramGroupPeerId(chatId, resolvedThreadId) : String(chatId),
},
parentPeer,
});
const mediaLocalRoots = getAgentScopedMediaLocalRoots(cfg, route.agentId);
const tableMode = resolveMarkdownTableMode({
cfg,
channel: "telegram",
accountId: route.accountId,
});
const chunkMode = resolveChunkMode(cfg, "telegram", route.accountId);
return { chatId, threadSpec, route, mediaLocalRoots, tableMode, chunkMode };
};
const buildCommandDeliveryBaseOptions = (params: {
chatId: string | number;
mediaLocalRoots?: readonly string[];
threadSpec: ReturnType<typeof resolveTelegramThreadSpec>;
tableMode: ReturnType<typeof resolveMarkdownTableMode>;
chunkMode: ReturnType<typeof resolveChunkMode>;
}) => ({
chatId: String(params.chatId),
token: opts.token,
runtime,
bot,
mediaLocalRoots: params.mediaLocalRoots,
replyToMode,
textLimit,
thread: params.threadSpec,
tableMode: params.tableMode,
chunkMode: params.chunkMode,
linkPreview: telegramCfg.linkPreview,
});
if (commandsToRegister.length > 0 || pluginCatalog.commands.length > 0) {
if (typeof (bot as unknown as { command?: unknown }).command !== "function") {
logVerbose("telegram: bot.command unavailable; skipping native handlers");
@@ -397,11 +454,19 @@ export const registerTelegramNativeCommands = ({
topicConfig,
commandAuthorized,
} = auth;
const messageThreadId = (msg as { message_thread_id?: number }).message_thread_id;
const threadSpec = resolveTelegramThreadSpec({
isGroup,
isForum,
messageThreadId,
const { threadSpec, route, mediaLocalRoots, tableMode, chunkMode } =
resolveCommandRuntimeContext({
msg,
isGroup,
isForum,
resolvedThreadId,
});
const deliveryBaseOptions = buildCommandDeliveryBaseOptions({
chatId,
mediaLocalRoots,
threadSpec,
tableMode,
chunkMode,
});
const threadParams = buildTelegramThreadParams(threadSpec) ?? {};
@@ -455,18 +520,6 @@ export const registerTelegramNativeCommands = ({
});
return;
}
const parentPeer = buildTelegramParentPeer({ isGroup, resolvedThreadId, chatId });
const route = resolveAgentRoute({
cfg,
channel: "telegram",
accountId,
peer: {
kind: isGroup ? "group" : "direct",
id: isGroup ? buildTelegramGroupPeerId(chatId, resolvedThreadId) : String(chatId),
},
parentPeer,
});
const mediaLocalRoots = getAgentScopedMediaLocalRoots(cfg, route.agentId);
const baseSessionKey = route.sessionKey;
// DMs: use raw messageThreadId for thread sessions (not resolvedThreadId which is for forums)
const dmThreadId = threadSpec.scope === "dm" ? threadSpec.id : undefined;
@@ -478,11 +531,6 @@ export const registerTelegramNativeCommands = ({
})
: null;
const sessionKey = threadKeys?.sessionKey ?? baseSessionKey;
const tableMode = resolveMarkdownTableMode({
cfg,
channel: "telegram",
accountId: route.accountId,
});
const skillFilter = firstDefined(topicConfig?.skills, groupConfig?.skills);
const systemPromptParts = [
groupConfig?.systemPrompt?.trim() || null,
@@ -530,7 +578,6 @@ export const registerTelegramNativeCommands = ({
typeof telegramCfg.blockStreaming === "boolean"
? !telegramCfg.blockStreaming
: undefined;
const chunkMode = resolveChunkMode(cfg, "telegram", route.accountId);
const deliveryState = {
delivered: false,
@@ -552,17 +599,7 @@ export const registerTelegramNativeCommands = ({
deliver: async (payload, _info) => {
const result = await deliverReplies({
replies: [payload],
chatId: String(chatId),
token: opts.token,
runtime,
bot,
mediaLocalRoots,
replyToMode,
textLimit,
thread: threadSpec,
tableMode,
chunkMode,
linkPreview: telegramCfg.linkPreview,
...deliveryBaseOptions,
});
if (result.delivered) {
deliveryState.delivered = true;
@@ -586,17 +623,7 @@ export const registerTelegramNativeCommands = ({
if (!deliveryState.delivered && deliveryState.skippedNonSilent > 0) {
await deliverReplies({
replies: [{ text: EMPTY_RESPONSE_FALLBACK }],
chatId: String(chatId),
token: opts.token,
runtime,
bot,
mediaLocalRoots,
replyToMode,
textLimit,
thread: threadSpec,
tableMode,
chunkMode,
linkPreview: telegramCfg.linkPreview,
...deliveryBaseOptions,
});
}
});
@@ -640,24 +667,20 @@ export const registerTelegramNativeCommands = ({
return;
}
const { senderId, commandAuthorized, isGroup, isForum, resolvedThreadId } = auth;
const messageThreadId = (msg as { message_thread_id?: number }).message_thread_id;
const threadSpec = resolveTelegramThreadSpec({
isGroup,
isForum,
messageThreadId,
const { threadSpec, mediaLocalRoots, tableMode, chunkMode } =
resolveCommandRuntimeContext({
msg,
isGroup,
isForum,
resolvedThreadId,
});
const deliveryBaseOptions = buildCommandDeliveryBaseOptions({
chatId,
mediaLocalRoots,
threadSpec,
tableMode,
chunkMode,
});
const parentPeer = buildTelegramParentPeer({ isGroup, resolvedThreadId, chatId });
const route = resolveAgentRoute({
cfg,
channel: "telegram",
accountId,
peer: {
kind: isGroup ? "group" : "direct",
id: isGroup ? buildTelegramGroupPeerId(chatId, resolvedThreadId) : String(chatId),
},
parentPeer,
});
const mediaLocalRoots = getAgentScopedMediaLocalRoots(cfg, route.agentId);
const from = isGroup
? buildTelegramGroupFrom(chatId, threadSpec.id)
: `telegram:${chatId}`;
@@ -676,26 +699,10 @@ export const registerTelegramNativeCommands = ({
accountId,
messageThreadId: threadSpec.id,
});
const tableMode = resolveMarkdownTableMode({
cfg,
channel: "telegram",
accountId: route.accountId,
});
const chunkMode = resolveChunkMode(cfg, "telegram", route.accountId);
await deliverReplies({
replies: [result],
chatId: String(chatId),
token: opts.token,
runtime,
bot,
mediaLocalRoots,
replyToMode,
textLimit,
thread: threadSpec,
tableMode,
chunkMode,
linkPreview: telegramCfg.linkPreview,
...deliveryBaseOptions,
});
});
}