From 63318bf66e695e75e0a825130beb0dfab948da68 Mon Sep 17 00:00:00 2001 From: Mac DeCourcy <49794076+mdecourcy@users.noreply.github.com> Date: Fri, 9 Jan 2026 13:56:20 -0800 Subject: [PATCH] Dedup repeat messages/reactions due to sfpp (#4174) Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com> Co-authored-by: James Rich <2199651+jamesarich@users.noreply.github.com> --- .../mesh/service/MeshDataHandler.kt | 22 +++++++++++++++++++ .../core/data/repository/PacketRepository.kt | 6 +++++ 2 files changed, 28 insertions(+) diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshDataHandler.kt b/app/src/main/java/com/geeksville/mesh/service/MeshDataHandler.kt index 2565b74b1..1580ba388 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshDataHandler.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshDataHandler.kt @@ -602,6 +602,17 @@ constructor( ) scope.handledLaunch { packetRepository.get().apply { + // Check for duplicates before inserting + val existingPackets = findPacketsWithId(dataPacket.id) + if (existingPackets.isNotEmpty()) { + Logger.d { + "Skipping duplicate packet: packetId=${dataPacket.id} from=${dataPacket.from} " + + "to=${dataPacket.to} contactKey=$contactKey" + + " (already have ${existingPackets.size} packet(s))" + } + return@handledLaunch + } + insert(packetToSave) val isMuted = getContactSettings(contactKey).isMuted if (!isMuted) { @@ -685,6 +696,17 @@ constructor( to = toId, channel = packet.channel, ) + + // Check for duplicates before inserting + val existingReactions = packetRepository.get().findReactionsWithId(packet.id) + if (existingReactions.isNotEmpty()) { + Logger.d { + "Skipping duplicate reaction: packetId=${packet.id} replyId=${packet.decoded.replyId} " + + "from=$fromId emoji=$emoji (already have ${existingReactions.size} reaction(s))" + } + return@handledLaunch + } + packetRepository.get().insertReaction(reaction) // Find the original packet to get the contactKey diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/PacketRepository.kt b/core/data/src/main/kotlin/org/meshtastic/core/data/repository/PacketRepository.kt index 3ebfbf16c..2764f63b7 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/PacketRepository.kt +++ b/core/data/src/main/kotlin/org/meshtastic/core/data/repository/PacketRepository.kt @@ -150,6 +150,9 @@ constructor( suspend fun getPacketByPacketId(packetId: Int) = withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().getPacketByPacketId(packetId) } + suspend fun findPacketsWithId(packetId: Int) = + withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().findPacketsWithId(packetId) } + @Suppress("CyclomaticComplexMethod") suspend fun updateSFPPStatus( packetId: Int, @@ -280,6 +283,9 @@ constructor( suspend fun getReactionByPacketId(packetId: Int) = withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().getReactionByPacketId(packetId) } + suspend fun findReactionsWithId(packetId: Int) = + withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().findReactionsWithId(packetId) } + suspend fun clearPacketDB() = withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().deleteAll() } suspend fun migrateChannelsByPSK(oldSettings: List, newSettings: List) =