From 7fe49f2fe4713c4b2952e772943a958343aca09a Mon Sep 17 00:00:00 2001 From: particle-box Date: Sun, 25 Jan 2026 14:28:03 +0530 Subject: [PATCH] fix: Prevent snap replay & fix remove audio note duration & transcription(thanks to RSR) --- .../impl/messaging/PreventMessageSending.kt | 42 ++--------- .../features/impl/messaging/SendOverride.kt | 69 +++++++++++++++---- 2 files changed, 63 insertions(+), 48 deletions(-) diff --git a/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/messaging/PreventMessageSending.kt b/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/messaging/PreventMessageSending.kt index 7874904d..8cd4b511 100644 --- a/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/messaging/PreventMessageSending.kt +++ b/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/messaging/PreventMessageSending.kt @@ -2,7 +2,6 @@ package me.eternal.purrfectsnap.core.features.impl.messaging import me.eternal.purrfectsnap.common.data.NotificationType import me.eternal.purrfectsnap.common.util.protobuf.ProtoEditor -import me.eternal.purrfectsnap.common.util.protobuf.ProtoReader import me.eternal.purrfectsnap.core.event.events.impl.NativeUnaryCallEvent import me.eternal.purrfectsnap.core.event.events.impl.UnaryCallEvent import me.eternal.purrfectsnap.core.event.events.impl.SendMessageWithContentEvent @@ -27,42 +26,12 @@ class PreventMessageSending : Feature("Prevent message sending") { }.toByteArray() } - fun handleCreateContentMessage(uri: String, buffer: ByteArray): Boolean { - if (uri != "/messagingcoreservice.MessagingCoreService/CreateContentMessage") return false - val reader = ProtoReader(buffer) - // check for missed audio/video call in MessageContent (field 4) - val contentReader = reader.followPath(4) ?: return false - - // try both possible field IDs based on SnapEnums and ContentType.java - val contentType = contentReader.getVarInt(2) - - val isMissedAudio = contentType == 13L || contentType == 18L - val isMissedVideo = contentType == 12L || contentType == 17L - - if (isMissedAudio && preventMessageSending.contains("abandon_audio")) return true - if (isMissedVideo && preventMessageSending.contains("abandon_video")) return true - - return false + context.event.subscribe(NativeUnaryCallEvent::class, { preventMessageSending.contains("snap_replay") }) { event -> + handleUpdateContentMessage(event.uri, event.buffer)?.let { event.buffer = it } } - context.event.subscribe(NativeUnaryCallEvent::class) { event -> - val uri = event.uri - if (!uri.startsWith("/messagingcoreservice.MessagingCoreService/")) return@subscribe - - if (handleCreateContentMessage(uri, event.buffer)) { - event.canceled = true - } - handleUpdateContentMessage(uri, event.buffer)?.let { event.buffer = it } - } - - context.event.subscribe(UnaryCallEvent::class) { event -> - val uri = event.uri - if (!uri.startsWith("/messagingcoreservice.MessagingCoreService/")) return@subscribe - - if (handleCreateContentMessage(uri, event.buffer)) { - event.canceled = true - } - handleUpdateContentMessage(uri, event.buffer)?.let { event.buffer = it } + context.event.subscribe(UnaryCallEvent::class, { preventMessageSending.contains("snap_replay") }) { event -> + handleUpdateContentMessage(event.uri, event.buffer)?.let { event.buffer = it } } context.classCache.conversationManager.hook("updateMessage", HookStage.BEFORE) { param -> @@ -75,7 +44,7 @@ class PreventMessageSending : Feature("Prevent message sending") { param.setResult(null) } - if ((messageUpdate == "REPLAY" || messageUpdate == "replay") && preventMessageSending.contains("snap_replay")) { + if (messageUpdate == "REPLAY" && preventMessageSending.contains("snap_replay")) { param.setResult(null) } } @@ -85,6 +54,7 @@ class PreventMessageSending : Feature("Prevent message sending") { val associatedType = NotificationType.fromContentType(contentType ?: return@subscribe) ?: return@subscribe if (preventMessageSending.contains(associatedType.key)) { + context.log.verbose("Preventing message sending for $associatedType") event.canceled = true } } diff --git a/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/messaging/SendOverride.kt b/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/messaging/SendOverride.kt index 6887c79d..398811fc 100644 --- a/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/messaging/SendOverride.kt +++ b/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/messaging/SendOverride.kt @@ -159,6 +159,63 @@ class SendOverride : Feature("Send Override") { if (configOverrideType == null && stripMediaMetadata.isEmpty()) return context.event.subscribe(MediaUploadEvent::class) { event -> + // Handle audio notes separately since they don't have path 11, 5 + if (stripMediaMetadata.isNotEmpty() && + (event.localMessageContent.contentType == ContentType.NOTE || + stripMediaMetadata.contains("remove_audio_note_duration") || + stripMediaMetadata.contains("remove_audio_note_transcript_capability"))) { + event.onMediaUploaded { result -> + if (result.messageContent.contentType == ContentType.NOTE) { + val contentReader = ProtoReader(result.messageContent.content!!) + result.messageContent.content = ProtoEditor(result.messageContent.content!!).apply { + // Check which path structure exists - try both to be safe + val hasFullPath = contentReader.followPath(4, 4, 6, 1, 1) != null + val hasDirectPath = contentReader.followPath(6, 1, 1) != null + + if (stripMediaMetadata.contains("remove_audio_note_duration")) { + // Audio note duration is at field 13 (confirmed from MessageDecoder line 94 and MessageSender line 27) + if (hasFullPath) { + edit(4, 4, 6, 1, 1) { + remove(13) + } + } + if (hasDirectPath || !hasFullPath) { + edit(6, 1, 1) { + remove(13) + } + } + } + if (stripMediaMetadata.contains("remove_audio_note_transcript_capability")) { + // Remove locale string (field 3) which may be used for transcript capability + // Also try other potential fields + if (hasFullPath) { + edit(4, 4, 6, 1, 1) { + remove(2) // potential transcript-related field + remove(3) // locale string (may affect transcript capability) + remove(4) // potential transcript-related field + } + edit(4, 4, 6, 1) { + remove(2) // possible transcript capability flag at parent level + remove(3) // locale at parent level + } + } + if (hasDirectPath || !hasFullPath) { + edit(6, 1, 1) { + remove(2) // potential transcript-related field + remove(3) // locale string (may affect transcript capability) + remove(4) // potential transcript-related field + } + edit(6, 1) { + remove(2) // possible transcript capability flag at parent level + remove(3) // locale at parent level (field 3 at 6,1 is locale per MessageSender) + } + } + } + }.toByteArray() + } + } + } + ProtoReader(event.localMessageContent.content!!).followPath(11, 5)?.let { snapDocPlayback -> event.onMediaUploaded { result -> result.messageContent.content = ProtoEditor(result.messageContent.content!!).apply { @@ -213,18 +270,6 @@ class SendOverride : Feature("Send Override") { } } } - ContentType.NOTE -> { - if (stripMediaMetadata.contains("remove_audio_note_duration")) { - edit(6, 1, 1) { - remove(13) - } - } - if (stripMediaMetadata.contains("remove_audio_note_transcript_capability")) { - edit(6, 1) { - remove(3) - } - } - } else -> {} } }