From d28d8a45963689f2ed98c88fe48e965b3fb946fe Mon Sep 17 00:00:00 2001 From: Renato Alcara Date: Sun, 1 Mar 2026 20:57:40 -0300 Subject: [PATCH] =?UTF-8?q?fix:=20address=20PR=20review=20=E2=80=94=20lint?= =?UTF-8?q?,=20dedup=20scope,=20and=20cleanup=20targeting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove unused `autoCleanCorrupted` param from decryptMessageNode (lint fix) - Scope retry dedup by participant JID in group chats (not group JID) - Move dedup registration after early-return checks to avoid blocking subsequent messages when max retries reached - Use participant JID for session cleanup in groups (Signal sessions are per-participant, not per-group) Co-Authored-By: Claude Opus 4.6 --- src/Socket/messages-recv.ts | 27 ++++++++++++++++----------- src/Utils/decode-wa-message.ts | 1 - 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Socket/messages-recv.ts b/src/Socket/messages-recv.ts index bad99c43..3d4bceb5 100644 --- a/src/Socket/messages-recv.ts +++ b/src/Socket/messages-recv.ts @@ -1220,7 +1220,10 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { // Per-JID deduplication: when multiple messages from the same contact // fail with Bad MAC simultaneously, only send ONE retry request. // The peer will resend all failed messages when it receives the retry receipt. - const retryDedupeJid = jidNormalizedUser(node.attrs.from!) + // For group messages, scope by participant (each participant has its own Signal session). + const retryDedupeJid = msgKey.participant + ? jidNormalizedUser(msgKey.participant) + : jidNormalizedUser(node.attrs.from!) if (retryRequestActiveJids.has(retryDedupeJid)) { logger.debug( { fromJid: retryDedupeJid, msgId }, @@ -1229,9 +1232,6 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { return } - retryRequestActiveJids.add(retryDedupeJid) - setTimeout(() => retryRequestActiveJids.delete(retryDedupeJid), 5_000) - if (messageRetryManager) { // Check if we've exceeded max retries using the new system if (messageRetryManager.hasExceededMaxRetries(msgId)) { @@ -1244,10 +1244,11 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { // on every Bad MAC in the hot path (decode-wa-message.ts). // The Signal Protocol recovers naturally via retry+pkmsg for most cases; // this cleanup only runs as a last resort. + // For group messages, use participant JID (Signal sessions are per-participant, not per-group). if (autoCleanCorrupted) { - const fromJid = node.attrs.from! + const senderJid = msgKey.participant ? jidNormalizedUser(msgKey.participant) : jidNormalizedUser(node.attrs.from!) try { - const decryptionJid = await getDecryptionJid(fromJid, signalRepository) + const decryptionJid = await getDecryptionJid(senderJid, signalRepository) const deletedCount = await cleanupCorruptedSession(decryptionJid, signalRepository, logger) if (deletedCount > 0) { logger.info( @@ -1256,7 +1257,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { ) } } catch (cleanupErr) { - logger.warn({ msgId, fromJid, err: cleanupErr }, 'Failed to cleanup session after retry exhaustion') + logger.warn({ msgId, senderJid, err: cleanupErr }, 'Failed to cleanup session after retry exhaustion') } } @@ -1281,12 +1282,12 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { // Safety net cleanup (same as new system above) if (autoCleanCorrupted) { - const fromJid = node.attrs.from! + const senderJid = msgKey.participant ? jidNormalizedUser(msgKey.participant) : jidNormalizedUser(node.attrs.from!) try { - const decryptionJid = await getDecryptionJid(fromJid, signalRepository) + const decryptionJid = await getDecryptionJid(senderJid, signalRepository) await cleanupCorruptedSession(decryptionJid, signalRepository, logger) } catch (cleanupErr) { - logger.warn({ msgId, fromJid, err: cleanupErr }, 'Failed to cleanup session after retry exhaustion') + logger.warn({ msgId, senderJid, err: cleanupErr }, 'Failed to cleanup session after retry exhaustion') } } @@ -1298,6 +1299,11 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { recordMessageRetry('retry') } + // Register dedup AFTER early-return checks so that max-retries paths + // don't block subsequent messages from the same JID for 5 seconds. + retryRequestActiveJids.add(retryDedupeJid) + setTimeout(() => retryRequestActiveJids.delete(retryDedupeJid), 5_000) + const key = `${msgId}:${msgKey?.participant}` const retryCount = (await msgRetryCache.get(key)) || 1 @@ -2228,7 +2234,6 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { authState.creds.me!.lid || '', signalRepository, logger, - autoCleanCorrupted ) const alt = msg.key.participantAlt || msg.key.remoteJidAlt diff --git a/src/Utils/decode-wa-message.ts b/src/Utils/decode-wa-message.ts index 5c3cb50e..4da274b4 100644 --- a/src/Utils/decode-wa-message.ts +++ b/src/Utils/decode-wa-message.ts @@ -276,7 +276,6 @@ export const decryptMessageNode = ( meLid: string, repository: SignalRepositoryWithLIDStore, logger: ILogger, - autoCleanCorrupted = true ) => { const { fullMessage, author, sender } = decodeMessageNode(stanza, meId, meLid) return {