commit e7df5d1edfabb6dc624af1f8c1c0596d1e0b28c3
parent 45145f9241a55bce1816ad389667716fd79b9619
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Thu, 18 Apr 2024 23:59:40 +0200

feat(core/notifications): friend add source

Diffstat:
Mcommon/src/main/assets/lang/en_US.json | 1+
Mcommon/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/MessagingTweaks.kt | 11++++++++++-
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/features/FeatureManager.kt | 2++
Acore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/FriendMutationObserver.kt | 41+++++++++++++++++++++++++++++++++++++++++
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/messaging/Notifications.kt | 57+++++++++++++++++++++++++++++++++++++--------------------
5 files changed, 91 insertions(+), 21 deletions(-)

diff --git a/common/src/main/assets/lang/en_US.json b/common/src/main/assets/lang/en_US.json @@ -951,6 +951,7 @@ "download_button": "Add download button", "mark_as_read_button": "Mark as Read button", "mark_as_read_and_save_in_chat": "Save in Chat when marking as read (depends on Auto Save)", + "friend_add_source": "Show friend add source", "group": "Group notifications" }, "friend_feed_menu_buttons": { diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/MessagingTweaks.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/MessagingTweaks.kt @@ -73,7 +73,16 @@ class MessagingTweaks : ConfigContainer() { nativeHooks() } val instantDelete = boolean("instant_delete") { requireRestart() } - val betterNotifications = multiple("better_notifications", "chat_preview", "media_preview", "reply_button", "download_button", "mark_as_read_button", "mark_as_read_and_save_in_chat", "group") { requireRestart() } + val betterNotifications = multiple("better_notifications", + "chat_preview", + "media_preview", + "reply_button", + "download_button", + "mark_as_read_button", + "mark_as_read_and_save_in_chat", + "friend_add_source", + "group" + ) { requireRestart() } val notificationBlacklist = multiple("notification_blacklist", *NotificationType.getIncomingValues().map { it.key }.toTypedArray()) { customOptionTranslationPath = "features.options.notifications" } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/FeatureManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/FeatureManager.kt @@ -16,6 +16,7 @@ import me.rhunk.snapenhance.core.features.impl.global.* import me.rhunk.snapenhance.core.features.impl.messaging.* import me.rhunk.snapenhance.core.features.impl.spying.HalfSwipeNotifier import me.rhunk.snapenhance.core.features.impl.spying.MessageLogger +import me.rhunk.snapenhance.core.features.impl.FriendMutationObserver import me.rhunk.snapenhance.core.features.impl.spying.StealthMode import me.rhunk.snapenhance.core.features.impl.tweaks.* import me.rhunk.snapenhance.core.features.impl.ui.* @@ -64,6 +65,7 @@ class FeatureManager( ScopeSync(), PreventMessageListAutoScroll(), Messaging(), + FriendMutationObserver(), AutoMarkAsRead(), MediaDownloader(), StealthMode(), diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/FriendMutationObserver.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/FriendMutationObserver.kt @@ -0,0 +1,40 @@ +package me.rhunk.snapenhance.core.features.impl + +import com.google.gson.JsonObject +import me.rhunk.snapenhance.core.event.events.impl.NetworkApiRequestEvent +import me.rhunk.snapenhance.core.features.Feature +import me.rhunk.snapenhance.core.features.FeatureLoadParams +import me.rhunk.snapenhance.core.util.EvictingMap +import java.io.InputStreamReader + +class FriendMutationObserver: Feature("FriendMutationObserver", loadParams = FeatureLoadParams.INIT_SYNC) { + private val addSourceCache = EvictingMap<String, String>(500) + + fun getFriendAddSource(userId: String): String? { + return addSourceCache[userId] + } + + override fun init() { + context.event.subscribe(NetworkApiRequestEvent::class) { event -> + if (!event.url.contains("ami/friends")) return@subscribe + event.onSuccess { buffer -> + runCatching { + val jsonObject = context.gson.fromJson(InputStreamReader(buffer?.inputStream() ?: return@onSuccess, Charsets.UTF_8), JsonObject::class.java) + + jsonObject.getAsJsonArray("added_friends").map { it.asJsonObject }.forEach { friend -> + val userId = friend.get("user_id").asString + (friend.get("add_source")?.asString?.takeIf { + it.isNotBlank() + } ?: friend.get("add_source_type")?.asString?.takeIf { + it.isNotBlank() + })?.let { + addSourceCache[userId] = it + } + } + }.onFailure { + context.log.error("Failed to process friends", it) + } + } + } + } +}+ \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/messaging/Notifications.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/messaging/Notifications.kt @@ -12,10 +12,7 @@ import android.os.Bundle import android.os.UserHandle import de.robv.android.xposed.XposedBridge import de.robv.android.xposed.XposedHelpers -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* import me.rhunk.snapenhance.common.data.ContentType import me.rhunk.snapenhance.common.data.MediaReferenceType import me.rhunk.snapenhance.common.data.MessageUpdate @@ -28,6 +25,7 @@ import me.rhunk.snapenhance.common.util.snap.SnapWidgetBroadcastReceiverHelper import me.rhunk.snapenhance.core.event.events.impl.SnapWidgetBroadcastReceiveEvent import me.rhunk.snapenhance.core.features.Feature import me.rhunk.snapenhance.core.features.FeatureLoadParams +import me.rhunk.snapenhance.core.features.impl.FriendMutationObserver import me.rhunk.snapenhance.core.features.impl.downloader.MediaDownloader import me.rhunk.snapenhance.core.features.impl.downloader.decoder.MessageDecoder import me.rhunk.snapenhance.core.features.impl.spying.StealthMode @@ -94,6 +92,18 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN notification ) as Notification.Builder + private fun setNotificationText(notification: Notification, text: String) { + notification.extras.putString("android.text", text) + notification.extras.putString("android.bigText", text) + notification.extras.putParcelableArray("android.messages", text.split("\n").map { + Bundle().apply { + putBundle("extras", Bundle()) + putString("text", it) + putLong("time", System.currentTimeMillis()) + } + }.toTypedArray()) + } + private fun computeNotificationMessages(notification: Notification, conversationId: String) { val messageText = StringBuilder().apply { cachedMessages.computeIfAbsent(conversationId) { sortedMapOf() }.forEach { @@ -102,17 +112,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN } }.toString() - with(notification.extras) { - putString("android.text", messageText) - putString("android.bigText", messageText) - putParcelableArray("android.messages", messageText.split("\n").map { - Bundle().apply { - putBundle("extras", Bundle()) - putString("text", it) - putLong("time", System.currentTimeMillis()) - } - }.toTypedArray()) - } + setNotificationText(notification, messageText) } private fun setupNotificationActionButtons(contentType: ContentType, conversationId: String, message: Message, notificationData: NotificationData) { @@ -423,11 +423,6 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN notificationData.notification.setObjectField("mGroupKey", SNAPCHAT_NOTIFICATION_GROUP) } - val conversationId = extras.getString("conversation_id").also { id -> - sentNotifications.computeIfAbsent(notificationData.id) { id ?: "" } - } ?: return@hook - - val serverMessageId = extras.getString("message_id") ?: return@hook val notificationType = extras.getString("notification_type")?.lowercase() ?: return@hook if (!canSendNotification(notificationType)) { @@ -435,8 +430,30 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN return@hook } + if (notificationType == "addfriend" && betterNotificationFilter.contains("friend_add_source")) { + val userId = notificationData.notification.shortcutId?.split("|")?.lastOrNull() ?: return@hook + runBlocking { + var addSource: String? = null + withTimeoutOrNull(7000) { + while (true) { + addSource = context.feature(FriendMutationObserver::class).getFriendAddSource(userId) + if (addSource != null) break + delay(500) + } + } + setNotificationText(notificationData.notification, addSource ?: return@runBlocking) + } + return@hook + } + if (!betterNotificationFilter.contains("chat_preview") && !betterNotificationFilter.contains("media_preview")) return@hook if (notificationType == "typing") return@hook + + val serverMessageId = extras.getString("message_id") ?: return@hook + val conversationId = extras.getString("conversation_id").also { id -> + sentNotifications.computeIfAbsent(notificationData.id) { id ?: "" } + } ?: return@hook + param.setResult(null) val conversationManager = context.feature(Messaging::class).conversationManager ?: return@hook