@@ -2743,13 +2743,127 @@ export const registerTelegramHandlers = ({
27432743 } as Message ;
27442744 } ;
27452745
2746- const recordEditedMessageForReplyChain = async (
2747- ctxForDedupe : TelegramUpdateKeyContext ,
2748- msg : Message ,
2749- ) => {
2750- if ( shouldSkipUpdate ( ctxForDedupe ) ) {
2746+ type TelegramInboundGate =
2747+ | { allowed : false }
2748+ | {
2749+ allowed : true ;
2750+ context : TelegramEventAuthorizationContext ;
2751+ effectiveDmAllow : NormalizedAllowFrom ;
2752+ } ;
2753+
2754+ // Single authorization gate for every message-like update that can reach the
2755+ // reply-chain cache or dispatch: fresh messages, edits, channel posts. Must run
2756+ // before any cache/dedupe side effect so blocked content is never recorded.
2757+ // dmAccess "challenge" may send a pairing reply; "silent" only decides (edits
2758+ // must never reply).
2759+ const authorizeInboundMessage = async ( params : {
2760+ msg : Message ;
2761+ chatId : number ;
2762+ isGroup : boolean ;
2763+ isForum : boolean ;
2764+ messageThreadId ?: number ;
2765+ senderId : string ;
2766+ senderUsername : string ;
2767+ requireConfiguredGroup : boolean ;
2768+ dmAccess : "challenge" | "silent" ;
2769+ } ) : Promise < TelegramInboundGate > => {
2770+ const context = await resolveTelegramEventAuthorizationContext ( {
2771+ chatId : params . chatId ,
2772+ isGroup : params . isGroup ,
2773+ isForum : params . isForum ,
2774+ senderId : params . senderId ,
2775+ messageThreadId : params . messageThreadId ,
2776+ } ) ;
2777+ const {
2778+ dmPolicy,
2779+ resolvedThreadId,
2780+ dmThreadId,
2781+ storeAllowFrom,
2782+ groupConfig,
2783+ topicConfig,
2784+ groupAllowOverride,
2785+ effectiveGroupAllow,
2786+ hasGroupAllowOverride,
2787+ } = context ;
2788+ // For DMs, prefer per-DM/topic allowFrom (groupAllowOverride) over account-level allowFrom
2789+ const expandedDmAllowFrom = await expandTelegramAllowFromWithAccessGroups ( {
2790+ cfg,
2791+ allowFrom : groupAllowOverride ?? allowFrom ,
2792+ accountId,
2793+ senderId : params . senderId ,
2794+ } ) ;
2795+ const effectiveDmAllow = normalizeDmAllowFromWithStore ( {
2796+ allowFrom : expandedDmAllowFrom ,
2797+ storeAllowFrom,
2798+ dmPolicy,
2799+ } ) ;
2800+
2801+ if ( params . requireConfiguredGroup && ( ! groupConfig || groupConfig . enabled === false ) ) {
2802+ logVerbose ( `Blocked telegram channel ${ params . chatId } (channel disabled)` ) ;
2803+ return { allowed : false } ;
2804+ }
2805+
2806+ if (
2807+ shouldSkipGroupMessage ( {
2808+ isGroup : params . isGroup ,
2809+ chatId : params . chatId ,
2810+ chatTitle : params . msg . chat . title ,
2811+ resolvedThreadId,
2812+ senderId : params . senderId ,
2813+ senderUsername : params . senderUsername ,
2814+ effectiveGroupAllow,
2815+ hasGroupAllowOverride,
2816+ groupConfig,
2817+ topicConfig,
2818+ } )
2819+ ) {
2820+ return { allowed : false } ;
2821+ }
2822+
2823+ if ( ! params . isGroup ) {
2824+ const requireTopic =
2825+ groupConfig && "requireTopic" in groupConfig ? groupConfig . requireTopic : undefined ;
2826+ if ( requireTopic === true && dmThreadId == null ) {
2827+ logVerbose ( `Blocked telegram DM ${ params . chatId } : requireTopic=true but no topic present` ) ;
2828+ return { allowed : false } ;
2829+ }
2830+ const dmAuthorized =
2831+ params . dmAccess === "challenge"
2832+ ? await enforceTelegramDmAccess ( {
2833+ isGroup : params . isGroup ,
2834+ dmPolicy,
2835+ msg : params . msg ,
2836+ chatId : params . chatId ,
2837+ effectiveDmAllow,
2838+ accountId,
2839+ bot,
2840+ logger,
2841+ upsertPairingRequest : telegramDeps . upsertChannelPairingRequest ,
2842+ } )
2843+ : await isTelegramDmAccessAllowed ( {
2844+ dmPolicy,
2845+ msg : params . msg ,
2846+ chatId : params . chatId ,
2847+ effectiveDmAllow,
2848+ accountId,
2849+ } ) ;
2850+ if ( ! dmAuthorized ) {
2851+ return { allowed : false } ;
2852+ }
2853+ }
2854+
2855+ return { allowed : true , context, effectiveDmAllow } ;
2856+ } ;
2857+
2858+ const recordEditedMessageForReplyChain = async ( params : {
2859+ ctxForDedupe : TelegramUpdateKeyContext ;
2860+ msg : Message ;
2861+ requireConfiguredGroup : boolean ;
2862+ } ) => {
2863+ if ( shouldSkipUpdate ( params . ctxForDedupe ) ) {
27512864 return ;
27522865 }
2866+ const msg = params . msg ;
27532867 const isGroup = msg . chat . type === "group" || msg . chat . type === "supergroup" ;
27542868 const isForum = await resolveTelegramForumFlag ( {
27552869 chatId : msg . chat . id ,
@@ -2760,52 +2874,21 @@ export const registerTelegramHandlers = ({
27602874 getChat,
27612875 } ) ;
27622876 const normalizedMsg = withResolvedTelegramForumFlag ( msg , isForum ) ;
2763- const resolvedThreadId = resolveTelegramForumThreadId ( {
2877+ const gate = await authorizeInboundMessage ( {
2878+ msg : normalizedMsg ,
2879+ chatId : normalizedMsg . chat . id ,
2880+ isGroup,
27642881 isForum,
27652882 messageThreadId : normalizedMsg . message_thread_id ,
2883+ senderId : normalizedMsg . from ?. id != null ? String ( normalizedMsg . from . id ) : "" ,
2884+ senderUsername : normalizedMsg . from ?. username ?? "" ,
2885+ requireConfiguredGroup : params . requireConfiguredGroup ,
2886+ dmAccess : "silent" ,
27662887 } ) ;
2767- const dmThreadId = ! isGroup ? normalizedMsg . message_thread_id : undefined ;
2768- if ( ! isGroup ) {
2769- const senderId = normalizedMsg . from ?. id != null ? String ( normalizedMsg . from . id ) : "" ;
2770- const eventAuthContext = await resolveTelegramEventAuthorizationContext ( {
2771- chatId : normalizedMsg . chat . id ,
2772- isGroup,
2773- isForum,
2774- senderId,
2775- messageThreadId : normalizedMsg . message_thread_id ,
2776- } ) ;
2777- const { dmPolicy, storeAllowFrom, groupConfig, groupAllowOverride } = eventAuthContext ;
2778- const requireTopic =
2779- groupConfig && "requireTopic" in groupConfig ? groupConfig . requireTopic : undefined ;
2780- if ( requireTopic === true && dmThreadId == null ) {
2781- logVerbose (
2782- `Blocked telegram DM ${ normalizedMsg . chat . id } : requireTopic=true but no topic present` ,
2783- ) ;
2784- return ;
2785- }
2786- const dmAllowFrom = groupAllowOverride ?? allowFrom ;
2787- const expandedDmAllowFrom = await expandTelegramAllowFromWithAccessGroups ( {
2788- cfg,
2789- allowFrom : dmAllowFrom ,
2790- accountId,
2791- senderId,
2792- } ) ;
2793- const effectiveDmAllow = normalizeDmAllowFromWithStore ( {
2794- allowFrom : expandedDmAllowFrom ,
2795- storeAllowFrom,
2796- dmPolicy,
2797- } ) ;
2798- const dmAuthorized = await isTelegramDmAccessAllowed ( {
2799- dmPolicy,
2800- msg : normalizedMsg ,
2801- chatId : normalizedMsg . chat . id ,
2802- effectiveDmAllow,
2803- accountId,
2804- } ) ;
2805- if ( ! dmAuthorized ) {
2806- return ;
2807- }
2888+ if ( ! gate . allowed ) {
2889+ return ;
28082890 }
2891+ const { resolvedThreadId, dmThreadId } = gate . context ;
28092892 await recordMessageForReplyChain ( normalizedMsg , resolvedThreadId ?? dmThreadId ) ;
28102893 } ;
28112894
@@ -2815,85 +2898,30 @@ export const registerTelegramHandlers = ({
28152898 if ( shouldSkipUpdate ( event . ctxForDedupe ) ) {
28162899 return ;
28172900 }
2818- const eventAuthContext = await resolveTelegramEventAuthorizationContext ( {
2901+ const gate = await authorizeInboundMessage ( {
2902+ msg : event . msg ,
28192903 chatId : event . chatId ,
28202904 isGroup : event . isGroup ,
28212905 isForum : event . isForum ,
2822- senderId : event . senderId ,
28232906 messageThreadId : event . messageThreadId ,
2907+ senderId : event . senderId ,
2908+ senderUsername : event . senderUsername ,
2909+ requireConfiguredGroup : event . requireConfiguredGroup ,
2910+ dmAccess : "challenge" ,
28242911 } ) ;
2912+ if ( ! gate . allowed ) {
2913+ return ;
2914+ }
2915+ const { effectiveDmAllow } = gate ;
28252916 const {
28262917 dmPolicy,
28272918 resolvedThreadId,
28282919 dmThreadId,
28292920 storeAllowFrom,
28302921 groupConfig,
28312922 topicConfig,
2832- groupAllowOverride,
28332923 effectiveGroupAllow,
2834- hasGroupAllowOverride,
2835- } = eventAuthContext ;
2836- // For DMs, prefer per-DM/topic allowFrom (groupAllowOverride) over account-level allowFrom
2837- const dmAllowFrom = groupAllowOverride ?? allowFrom ;
2838- const expandedDmAllowFrom = await expandTelegramAllowFromWithAccessGroups ( {
2839- cfg,
2840- allowFrom : dmAllowFrom ,
2841- accountId,
2842- senderId : event . senderId ,
2843- } ) ;
2844- const effectiveDmAllow = normalizeDmAllowFromWithStore ( {
2845- allowFrom : expandedDmAllowFrom ,
2846- storeAllowFrom,
2847- dmPolicy,
2848- } ) ;
2849-
2850- if ( event . requireConfiguredGroup && ( ! groupConfig || groupConfig . enabled === false ) ) {
2851- logVerbose ( `Blocked telegram channel ${ event . chatId } (channel disabled)` ) ;
2852- return ;
2853- }
2854-
2855- if (
2856- shouldSkipGroupMessage ( {
2857- isGroup : event . isGroup ,
2858- chatId : event . chatId ,
2859- chatTitle : event . msg . chat . title ,
2860- resolvedThreadId,
2861- senderId : event . senderId ,
2862- senderUsername : event . senderUsername ,
2863- effectiveGroupAllow,
2864- hasGroupAllowOverride,
2865- groupConfig,
2866- topicConfig,
2867- } )
2868- ) {
2869- return ;
2870- }
2871-
2872- const requireTopic =
2873- ! event . isGroup && groupConfig && "requireTopic" in groupConfig
2874- ? groupConfig . requireTopic
2875- : undefined ;
2876- if ( ! event . isGroup && requireTopic === true && dmThreadId == null ) {
2877- logVerbose ( `Blocked telegram DM ${ event . chatId } : requireTopic=true but no topic present` ) ;
2878- return ;
2879- }
2880-
2881- if ( ! event . isGroup ) {
2882- const dmAuthorized = await enforceTelegramDmAccess ( {
2883- isGroup : event . isGroup ,
2884- dmPolicy,
2885- msg : event . msg ,
2886- chatId : event . chatId ,
2887- effectiveDmAllow,
2888- accountId,
2889- bot,
2890- logger,
2891- upsertPairingRequest : telegramDeps . upsertChannelPairingRequest ,
2892- } ) ;
2893- if ( ! dmAuthorized ) {
2894- return ;
2895- }
2896- }
2924+ } = gate . context ;
28972925
28982926 const promptContextMinTimestampMs = normalizePromptContextMinTimestampMs (
28992927 resolveTelegramSessionState ( {
@@ -2999,7 +3027,11 @@ export const registerTelegramHandlers = ({
29993027 if ( ! msg ) {
30003028 return ;
30013029 }
3002- await recordEditedMessageForReplyChain ( ctx , msg ) ;
3030+ await recordEditedMessageForReplyChain ( {
3031+ ctxForDedupe : ctx ,
3032+ msg,
3033+ requireConfiguredGroup : false ,
3034+ } ) ;
30033035 } ) ;
30043036
30053037 // Handle channel posts — enables bot-to-bot communication via Telegram channels.
@@ -3040,6 +3072,10 @@ export const registerTelegramHandlers = ({
30403072 if ( ! post ) {
30413073 return ;
30423074 }
3043- await recordEditedMessageForReplyChain ( ctx , normalizeChannelPostMessage ( post ) ) ;
3075+ await recordEditedMessageForReplyChain ( {
3076+ ctxForDedupe : ctx ,
3077+ msg : normalizeChannelPostMessage ( post ) ,
3078+ requireConfiguredGroup : true ,
3079+ } ) ;
30443080 } ) ;
30453081} ;
0 commit comments