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:
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