@@ -63,6 +63,13 @@ type PersistedMessageReadResult = TelegramMessageCacheBucket & {
6363 needsRewrite : boolean ;
6464} ;
6565
66+ type TelegramMessageObservationMode = "authoritative" | "partial" ;
67+
68+ type TelegramCachedMessageObservation = {
69+ node : TelegramCachedMessageNode ;
70+ mode : TelegramMessageObservationMode ;
71+ } ;
72+
6673const DEFAULT_MAX_MESSAGES = 5000 ;
6774const COMPACT_THRESHOLD_RATIO = 2 ;
6875const persistedMessageCacheBuckets = new Map < string , TelegramMessageCacheBucket > ( ) ;
@@ -164,14 +171,18 @@ function resolveMessageThreadId(msg: Message): number | undefined {
164171function normalizeMessageNodes (
165172 msg : Message ,
166173 params : { threadId ?: number } ,
167- ) : TelegramCachedMessageNode [ ] {
168- const nodes : TelegramCachedMessageNode [ ] = [ ] ;
174+ ) : TelegramCachedMessageObservation [ ] {
175+ const observations : TelegramCachedMessageObservation [ ] = [ ] ;
169176 const visited = new Set < string > ( ) ;
170177 const nodeThreadId = ( node : TelegramCachedMessageNode ) => {
171178 const threadId = Number ( node . threadId ) ;
172179 return Number . isFinite ( threadId ) ? threadId : undefined ;
173180 } ;
174- const visit = ( message : Message , inheritedThreadId ?: number ) => {
181+ const visit = (
182+ message : Message ,
183+ inheritedThreadId : number | undefined ,
184+ mode : TelegramMessageObservationMode ,
185+ ) => {
175186 const node = normalizeMessageNode ( message , {
176187 threadId : resolveMessageThreadId ( message ) ?? inheritedThreadId ,
177188 } ) ;
@@ -181,12 +192,12 @@ function normalizeMessageNodes(
181192 visited . add ( node . messageId ) ;
182193 const replyMessage = resolveEmbeddedReplyMessage ( message ) ;
183194 if ( replyMessage ?. message_id != null ) {
184- visit ( replyMessage , nodeThreadId ( node ) ?? inheritedThreadId ) ;
195+ visit ( replyMessage , nodeThreadId ( node ) ?? inheritedThreadId , "partial" ) ;
185196 }
186- nodes . push ( node ) ;
197+ observations . push ( { node, mode } ) ;
187198 } ;
188- visit ( msg , params . threadId ) ;
189- return nodes ;
199+ visit ( msg , params . threadId , "authoritative" ) ;
200+ return observations ;
190201}
191202
192203function isRecord ( value : unknown ) : value is Record < string , unknown > {
@@ -215,6 +226,7 @@ function isTelegramSourceMessage(value: unknown): value is Message {
215226function parsePersistedEntry ( value : unknown ) : Array < {
216227 key : string ;
217228 node : TelegramCachedMessageNode ;
229+ mode : TelegramMessageObservationMode ;
218230} > {
219231 if ( ! isRecord ( value ) || ! isString ( value . key ) ) {
220232 return [ ] ;
@@ -229,10 +241,15 @@ function parsePersistedEntry(value: unknown): Array<{
229241 }
230242 const keyPrefix = value . key . slice ( 0 , separatorIndex + 1 ) ;
231243 const threadId = Number ( readOptionalString ( value . node , "threadId" ) ) ;
244+ const sourceMessageId = String ( value . node . sourceMessage . message_id ) ;
232245 return normalizeMessageNodes (
233246 value . node . sourceMessage ,
234247 Number . isFinite ( threadId ) ? { threadId } : { } ,
235- ) . map ( ( node ) => ( { key : `${ keyPrefix } ${ node . messageId } ` , node } ) ) ;
248+ ) . map ( ( { node, mode } ) => ( {
249+ key : `${ keyPrefix } ${ node . messageId } ` ,
250+ node,
251+ mode : node . messageId === sourceMessageId ? "authoritative" : mode ,
252+ } ) ) ;
236253}
237254
238255function findJsonArrayEnd ( text : string ) : number {
@@ -337,26 +354,41 @@ function mergeTelegramSourceMessage(existing: Message, incoming: Message): Messa
337354 return merged ;
338355}
339356
357+ function mergeAuthoritativeTelegramSourceMessage ( existing : Message , incoming : Message ) : Message {
358+ const existingReply = resolveEmbeddedReplyMessage ( existing ) ;
359+ const incomingReply = resolveEmbeddedReplyMessage ( incoming ) ;
360+ if ( existingReply ?. message_id != null && incomingReply ?. message_id === existingReply . message_id ) {
361+ return {
362+ ...incoming ,
363+ reply_to_message : mergeTelegramSourceMessage ( existingReply , incomingReply ) ,
364+ } ;
365+ }
366+ return incoming ;
367+ }
368+
340369function mergeCachedMessageNode (
341370 existing : TelegramCachedMessageNode ,
342371 incoming : TelegramCachedMessageNode ,
372+ mode : TelegramMessageObservationMode ,
343373) : TelegramCachedMessageNode {
344374 const threadId = Number ( incoming . threadId ?? existing . threadId ) ;
345- return normalizeRequiredMessageNode (
346- mergeTelegramSourceMessage ( existing . sourceMessage , incoming . sourceMessage ) ,
347- {
348- ...( Number . isFinite ( threadId ) ? { threadId } : { } ) ,
349- } ,
350- ) ;
375+ const sourceMessage =
376+ mode === "authoritative"
377+ ? mergeAuthoritativeTelegramSourceMessage ( existing . sourceMessage , incoming . sourceMessage )
378+ : mergeTelegramSourceMessage ( existing . sourceMessage , incoming . sourceMessage ) ;
379+ return normalizeRequiredMessageNode ( sourceMessage , {
380+ ...( Number . isFinite ( threadId ) ? { threadId } : { } ) ,
381+ } ) ;
351382}
352383
353384function upsertCachedMessageNode ( params : {
354385 messages : Map < string , TelegramCachedMessageNode > ;
355386 key : string ;
356387 node : TelegramCachedMessageNode ;
388+ mode : TelegramMessageObservationMode ;
357389} ) : TelegramCachedMessageNode {
358390 const existing = params . messages . get ( params . key ) ;
359- const node = existing ? mergeCachedMessageNode ( existing , params . node ) : params . node ;
391+ const node = existing ? mergeCachedMessageNode ( existing , params . node , params . mode ) : params . node ;
360392 params . messages . delete ( params . key ) ;
361393 params . messages . set ( params . key , node ) ;
362394 return node ;
@@ -375,7 +407,12 @@ function readPersistedMessages(filePath: string, maxMessages: number): Persisted
375407 for ( const value of persisted . values ) {
376408 for ( const entry of parsePersistedEntry ( value ) ) {
377409 persistedEntryCount ++ ;
378- upsertCachedMessageNode ( { messages, key : entry . key , node : entry . node } ) ;
410+ upsertCachedMessageNode ( {
411+ messages,
412+ key : entry . key ,
413+ node : entry . node ,
414+ mode : entry . mode ,
415+ } ) ;
379416 trimMessages ( messages , maxMessages ) ;
380417 }
381418 }
@@ -515,16 +552,16 @@ export function createTelegramMessageCache(params?: {
515552
516553 return {
517554 record : ( { accountId, chatId, msg, threadId } ) => {
518- const entries = normalizeMessageNodes ( msg , { threadId } ) ;
519- const entry = entries . at ( - 1 ) ;
520- if ( ! entry ) {
555+ const observations = normalizeMessageNodes ( msg , { threadId } ) ;
556+ const currentObservation = observations . at ( - 1 ) ;
557+ if ( ! currentObservation ) {
521558 return null ;
522559 }
523560 let recordedEntry : TelegramCachedMessageNode | null = null ;
524- for ( const node of entries ) {
561+ for ( const { node, mode } of observations ) {
525562 const key = telegramMessageCacheKey ( { accountId, chatId, messageId : node . messageId } ) ;
526- const cachedNode = upsertCachedMessageNode ( { messages, key, node } ) ;
527- if ( node . messageId === entry . messageId ) {
563+ const cachedNode = upsertCachedMessageNode ( { messages, key, node, mode } ) ;
564+ if ( node . messageId === currentObservation . node . messageId ) {
528565 recordedEntry = cachedNode ;
529566 }
530567 trimMessages ( messages , maxMessages ) ;
@@ -544,7 +581,7 @@ export function createTelegramMessageCache(params?: {
544581 logVerbose ( `telegram: failed to persist message cache: ${ String ( error ) } ` ) ;
545582 }
546583 }
547- return recordedEntry ?? entry ;
584+ return recordedEntry ?? currentObservation . node ;
548585 } ,
549586 get,
550587 recentBefore : ( { accountId, chatId, messageId, threadId, limit } ) => {
0 commit comments