




















@@ -7,10 +7,7 @@ import {
77resolveInboundDebounceMs,
88} from "openclaw/plugin-sdk/channel-inbound-debounce";
99import { resolveStoredModelOverride } from "openclaw/plugin-sdk/command-auth";
10-import {
11-resolveCommandAuthorization,
12-resolveCommandAuthorizedFromAuthorizers,
13-} from "openclaw/plugin-sdk/command-auth-native";
10+import { resolveCommandAuthorizedFromAuthorizers } from "openclaw/plugin-sdk/command-auth-native";
1411import { buildCommandsMessagePaginated } from "openclaw/plugin-sdk/command-status";
1512import { replaceConfigFile } from "openclaw/plugin-sdk/config-mutation";
1613import type { DmPolicy, OpenClawConfig } from "openclaw/plugin-sdk/config-types";
@@ -34,12 +31,16 @@ import {
3431resolveSessionStoreEntry,
3532updateSessionStore,
3633} from "openclaw/plugin-sdk/session-store-runtime";
37-import { expandTelegramAllowFromWithAccessGroups } from "./access-groups.js";
34+import {
35+expandTelegramAllowFromWithAccessGroups,
36+resolveTelegramDmAllow,
37+} from "./access-groups.js";
3838import { resolveTelegramAccount, resolveTelegramMediaRuntimeOptions } from "./accounts.js";
3939import { withTelegramApiErrorLogging } from "./api-logging.js";
4040import {
4141isSenderAllowed,
4242normalizeDmAllowFromWithStore,
43+resolveTelegramEffectiveDmPolicy,
4344type NormalizedAllowFrom,
4445} from "./bot-access.js";
4546import {
@@ -71,9 +72,10 @@ import {
7172import { resolveMedia } from "./bot/delivery.resolve-media.js";
7273import {
7374getTelegramTextParts,
74-buildTelegramGroupFrom,
7575buildTelegramGroupPeerId,
7676buildTelegramParentPeer,
77+isTelegramCommandsAllowFromConfigured,
78+resolveTelegramCommandAuthorization,
7779resolveTelegramForumFlag,
7880resolveTelegramForumThreadId,
7981resolveTelegramGroupAllowFromContext,
@@ -728,13 +730,11 @@ export const registerTelegramHandlers = ({
728730readChannelAllowFromStore: telegramDeps.readChannelAllowFromStore,
729731 resolveTelegramGroupConfig,
730732}));
731-// Use direct config dmPolicy override if available for DMs
732-const effectiveDmPolicy =
733-!params.isGroup &&
734-groupAllowContext.groupConfig &&
735-"dmPolicy" in groupAllowContext.groupConfig
736- ? (groupAllowContext.groupConfig.dmPolicy ?? telegramCfg.dmPolicy ?? "pairing")
737- : (telegramCfg.dmPolicy ?? "pairing");
733+const effectiveDmPolicy = resolveTelegramEffectiveDmPolicy({
734+isGroup: params.isGroup,
735+groupConfig: groupAllowContext.groupConfig,
736+dmPolicy: telegramCfg.dmPolicy,
737+});
738738return { dmPolicy: effectiveDmPolicy, ...groupAllowContext };
739739};
740740@@ -831,27 +831,15 @@ export const registerTelegramHandlers = ({
831831const { chatId, isGroup, senderId, senderUsername, context, cfg } = params;
832832const useAccessGroups = cfg.commands?.useAccessGroups !== false;
833833const dmAllowFrom = context.groupAllowOverride ?? allowFrom;
834-const commandsAllowFrom = cfg.commands?.allowFrom;
835-const commandsAllowFromConfigured =
836-commandsAllowFrom != null &&
837-typeof commandsAllowFrom === "object" &&
838-(Array.isArray(commandsAllowFrom.telegram) || Array.isArray(commandsAllowFrom["*"]));
839-if (commandsAllowFromConfigured) {
840-return resolveCommandAuthorization({
841-ctx: {
842-Provider: "telegram",
843-Surface: "telegram",
844-OriginatingChannel: "telegram",
845-AccountId: accountId,
846-ChatType: isGroup ? "group" : "direct",
847-From: isGroup
848- ? buildTelegramGroupFrom(chatId, context.resolvedThreadId)
849- : `telegram:${chatId}`,
850-SenderId: senderId || undefined,
851-SenderUsername: senderUsername || undefined,
852-},
834+if (isTelegramCommandsAllowFromConfigured(cfg)) {
835+return resolveTelegramCommandAuthorization({
853836 cfg,
854-commandAuthorized: false,
837+ accountId,
838+ chatId,
839+ isGroup,
840+resolvedThreadId: context.resolvedThreadId,
841+ senderId,
842+ senderUsername,
855843}).isAuthorizedSender;
856844}
857845@@ -896,7 +884,6 @@ export const registerTelegramHandlers = ({
896884});
897885};
898886899-// Handle emoji reactions to messages.
900887bot.on("message_reaction", async (ctx) => {
901888try {
902889const reaction = ctx.messageReaction;
@@ -915,7 +902,6 @@ export const registerTelegramHandlers = ({
915902const isGroup = reaction.chat.type === "group" || reaction.chat.type === "supergroup";
916903const isForum = reaction.chat.is_forum === true;
917904918-// Resolve reaction notification mode (default: "own").
919905const reactionMode = telegramCfg.reactionNotifications ?? "own";
920906if (reactionMode === "off") {
921907return;
@@ -963,7 +949,6 @@ export const registerTelegramHandlers = ({
963949}
964950}
965951966-// Detect added reactions.
967952const oldEmojis = new Set(
968953reaction.old_reaction
969954.filter((r): r is ReactionTypeEmoji => r.type === "emoji")
@@ -977,7 +962,6 @@ export const registerTelegramHandlers = ({
977962return;
978963}
979964980-// Build sender label.
981965const senderName = user
982966 ? [user.first_name, user.last_name].filter(Boolean).join(" ").trim() || user.username
983967 : undefined;
@@ -1001,7 +985,6 @@ export const registerTelegramHandlers = ({
1001985 : undefined;
1002986const peerId = isGroup ? buildTelegramGroupPeerId(chatId, resolvedThreadId) : String(chatId);
1003987const parentPeer = buildTelegramParentPeer({ isGroup, resolvedThreadId, chatId });
1004-// Fresh config for bindings lookup; other routing inputs are payload-derived.
1005988const route = resolveAgentRoute({
1006989cfg: telegramDeps.getRuntimeConfig(),
1007990channel: "telegram",
@@ -1011,7 +994,6 @@ export const registerTelegramHandlers = ({
1011994});
1012995const sessionKey = route.sessionKey;
10139961014-// Enqueue system event for each added reaction.
1015997for (const r of addedReactions) {
1016998const emoji = r.emoji;
1017999const text = `Telegram reaction added: ${emoji} by ${senderLabel} on msg ${messageId}`;
@@ -1047,14 +1029,11 @@ export const registerTelegramHandlers = ({
10471029 oversizeLogMessage,
10481030} = params;
104910311050-// Text fragment handling - Telegram splits long pastes into multiple inbound messages (~4096 chars).
1051-// We buffer “near-limit” messages and append immediately-following parts.
10521032const text = typeof msg.text === "string" ? msg.text : undefined;
10531033const isCommandLike = (text ?? "").trim().startsWith("/");
10541034if (text && !isCommandLike) {
10551035const nowMs = Date.now();
10561036const senderId = msg.from?.id != null ? String(msg.from.id) : "unknown";
1057-// Use resolvedThreadId for forum groups, dmThreadId for DM topics
10581037const threadId = resolvedThreadId ?? dmThreadId;
10591038const key = `text:${chatId}:${threadId ?? "main"}:${senderId}`;
10601039const existing = textFragmentBuffer.get(key);
@@ -1087,7 +1066,6 @@ export const registerTelegramHandlers = ({
10871066}
10881067}
108910681090-// Not appendable (or limits exceeded): flush buffered entry first, then continue normally.
10911069clearTimeout(existing.timer);
10921070textFragmentBuffer.delete(key);
10931071textFragmentProcessing = textFragmentProcessing
@@ -1111,7 +1089,6 @@ export const registerTelegramHandlers = ({
11111089}
11121090}
111310911114-// Media group handling - buffer multi-image messages
11151092const mediaGroupId = msg.media_group_id;
11161093if (mediaGroupId) {
11171094const existing = mediaGroupBuffer.get(mediaGroupId);
@@ -1186,8 +1163,6 @@ export const registerTelegramHandlers = ({
11861163return;
11871164}
118811651189-// Skip sticker-only messages where the sticker was skipped (animated/video)
1190-// These have no media and no text content to process.
11911166const hasText = Boolean(getTelegramTextParts(msg).text.trim());
11921167if (msg.sticker && !media && !hasText) {
11931168logVerbose("telegram: skipping sticker-only message (unsupported sticker type)");
@@ -1240,7 +1215,6 @@ export const registerTelegramHandlers = ({
12401215typeof (ctx as { answerCallbackQuery?: unknown }).answerCallbackQuery === "function"
12411216 ? () => ctx.answerCallbackQuery()
12421217 : () => bot.api.answerCallbackQuery(callback.id);
1243-// Answer immediately to prevent Telegram from retrying while we process
12441218await withTelegramApiErrorLogging({
12451219operation: "answerCallbackQuery",
12461220 runtime,
@@ -1573,7 +1547,6 @@ export const registerTelegramHandlers = ({
15731547return;
15741548}
157515491576-// Model selection callback handler (mdl_prov, mdl_list_*, mdl_sel_*, mdl_back)
15771550const modelCallback = parseModelCallbackData(data);
15781551if (modelCallback) {
15791552if (
@@ -1660,7 +1633,6 @@ export const registerTelegramHandlers = ({
16601633const { provider, page } = modelCallback;
16611634const modelSet = byProvider.get(provider);
16621635if (!modelSet || modelSet.size === 0) {
1663-// Provider not found or no models - show providers list
16641636const providerInfos: ProviderInfo[] = providers.map((p) => ({
16651637id: p,
16661638count: byProvider.get(p)?.size ?? 0,
@@ -1681,7 +1653,6 @@ export const registerTelegramHandlers = ({
16811653const totalPages = calculateTotalPages(models.length, pageSize);
16821654const safePage = Math.max(1, Math.min(page, totalPages));
168316551684-// Resolve current model from session (prefer overrides)
16851656const currentModel = sessionState.model;
1686165716871658const buttons = buildModelsKeyboard({
@@ -1744,7 +1715,6 @@ export const registerTelegramHandlers = ({
17441715return;
17451716}
174617171747-// Directly set model override in session
17481718try {
17491719// Use the fresh runtimeCfg (loaded at callback entry) so store path
17501720// and default-model resolution stay consistent with the next
@@ -1782,7 +1752,6 @@ export const registerTelegramHandlers = ({
17821752throw new TelegramRetryableCallbackError(err);
17831753}
178417541785-// Update message to show success with visual feedback
17861755const escapeHtml = (text: string) =>
17871756text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
17881757const actionText = isDefaultSelection
@@ -1832,7 +1801,6 @@ export const registerTelegramHandlers = ({
18321801}
18331802});
183418031835-// Handle group migration to supergroup (chat ID changes)
18361804bot.on("message:migrate_to_chat_id", async (ctx) => {
18371805try {
18381806const msg = ctx.message;
@@ -1854,7 +1822,6 @@ export const registerTelegramHandlers = ({
18541822return;
18551823}
185618241857-// Check if old chat ID has config and migrate it
18581825const currentConfig = telegramDeps.getRuntimeConfig();
18591826const migration = migrateTelegramGroupConfig({
18601827cfg: currentConfig,
@@ -1927,16 +1894,12 @@ export const registerTelegramHandlers = ({
19271894 effectiveGroupAllow,
19281895 hasGroupAllowOverride,
19291896} = eventAuthContext;
1930-// For DMs, prefer per-DM/topic allowFrom (groupAllowOverride) over account-level allowFrom
1931-const dmAllowFrom = groupAllowOverride ?? allowFrom;
1932-const expandedDmAllowFrom = await expandTelegramAllowFromWithAccessGroups({
1897+const dmAllow = await resolveTelegramDmAllow({
19331898 cfg,
1934-allowFrom: dmAllowFrom,
1899+ groupAllowOverride,
1900+ allowFrom,
19351901 accountId,
19361902senderId: event.senderId,
1937-});
1938-const effectiveDmAllow = normalizeDmAllowFromWithStore({
1939-allowFrom: expandedDmAllowFrom,
19401903 storeAllowFrom,
19411904 dmPolicy,
19421905});
@@ -1969,7 +1932,7 @@ export const registerTelegramHandlers = ({
19691932 dmPolicy,
19701933msg: event.msg,
19711934chatId: event.chatId,
1972- effectiveDmAllow,
1935+effectiveDmAllow: dmAllow.effectiveAllow,
19731936 accountId,
19741937 bot,
19751938 logger,
@@ -2031,9 +1994,6 @@ export const registerTelegramHandlers = ({
20311994});
20321995});
203319962034-// Handle channel posts — enables bot-to-bot communication via Telegram channels.
2035-// Telegram bots cannot see other bot messages in groups, but CAN in channels.
2036-// This handler normalizes channel_post updates into the standard message pipeline.
20371997bot.on("channel_post", async (ctx) => {
20381998const post = ctx.channelPost;
20391999if (!post) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。