feat(core): mark snaps as seen

This commit is contained in:
rhunk
2023-11-08 01:43:47 +01:00
parent c357825dc7
commit 8823093b30
4 changed files with 82 additions and 6 deletions

View File

@ -5,18 +5,20 @@ import me.rhunk.snapenhance.core.event.events.impl.OnSnapInteractionEvent
import me.rhunk.snapenhance.core.features.Feature
import me.rhunk.snapenhance.core.features.FeatureLoadParams
import me.rhunk.snapenhance.core.features.impl.spying.StealthMode
import me.rhunk.snapenhance.core.util.EvictingMap
import me.rhunk.snapenhance.core.util.hook.HookStage
import me.rhunk.snapenhance.core.util.hook.Hooker
import me.rhunk.snapenhance.core.util.hook.hook
import me.rhunk.snapenhance.core.util.hook.hookConstructor
import me.rhunk.snapenhance.core.util.ktx.getObjectField
import me.rhunk.snapenhance.core.util.ktx.getObjectFieldOrNull
import me.rhunk.snapenhance.core.wrapper.impl.ConversationManager
import me.rhunk.snapenhance.core.wrapper.impl.Message
import me.rhunk.snapenhance.core.wrapper.impl.SnapUUID
class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC or FeatureLoadParams.INIT_ASYNC or FeatureLoadParams.INIT_SYNC) {
var conversationManager: ConversationManager? = null
private set
var openedConversationUUID: SnapUUID? = null
private set
var lastFetchConversationUserUUID: SnapUUID? = null
@ -27,8 +29,10 @@ class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_C
var lastFocusedMessageId: Long = -1
private set
private val feedCachedSnapMessages = EvictingMap<String, List<Long>>(100)
override fun init() {
Hooker.hookConstructor(context.classCache.conversationManager, HookStage.BEFORE) { param ->
context.classCache.conversationManager.hookConstructor(HookStage.BEFORE) { param ->
conversationManager = ConversationManager(context, param.thisObject())
context.messagingBridge.triggerSessionStart()
context.mainActivity?.takeIf { it.intent.getBooleanExtra(ReceiversConfig.MESSAGING_PREVIEW_EXTRA,false) }?.run {
@ -37,6 +41,8 @@ class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_C
}
}
fun getFeedCachedMessageIds(conversationId: String) = feedCachedSnapMessages[conversationId]
override fun onActivityCreate() {
context.mappings.getMappedObjectNullable("FriendsFeedEventDispatcher").let { it as? Map<*, *> }?.let { mappings ->
findClass(mappings["class"].toString()).hook("onItemLongPress", HookStage.BEFORE) { param ->
@ -51,6 +57,19 @@ class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_C
}
}
val myUserId = context.database.myUserId
context.classCache.feedEntry.hookConstructor(HookStage.AFTER) { param ->
val instance = param.thisObject<Any>()
val interactionInfo = instance.getObjectFieldOrNull("mInteractionInfo") ?: return@hookConstructor
val messages = (interactionInfo.getObjectFieldOrNull("mMessages") as? List<*>)?.map { Message(it) } ?: return@hookConstructor
val conversationId = SnapUUID(instance.getObjectFieldOrNull("mConversationId") ?: return@hookConstructor).toString()
feedCachedSnapMessages[conversationId] = messages.filter { msg ->
msg.messageMetadata?.seenBy?.none { it.toString() == myUserId } == true
}.sortedBy { it.orderKey }.mapNotNull { it.messageDescriptor?.messageId }
}
context.mappings.getMappedClass("callbacks", "GetOneOnOneConversationIdsCallback").hook("onSuccess", HookStage.BEFORE) { param ->
val userIdToConversation = (param.arg<ArrayList<*>>(0))
.takeIf { it.isNotEmpty() }
@ -96,12 +115,12 @@ class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_C
lastFocusedMessageId = event.messageId
}
Hooker.hook(context.classCache.conversationManager, "fetchMessage", HookStage.BEFORE) { param ->
context.classCache.conversationManager.hook("fetchMessage", HookStage.BEFORE) { param ->
lastFetchConversationUserUUID = SnapUUID((param.arg(0) as Any))
lastFocusedMessageId = param.arg(1)
}
Hooker.hook(context.classCache.conversationManager, "sendTypingNotification", HookStage.BEFORE, {
context.classCache.conversationManager.hook("sendTypingNotification", HookStage.BEFORE, {
hideTypingNotification || stealthMode.canUseRule(openedConversationUUID.toString())
}) {
it.setResult(null)

View File

@ -9,9 +9,15 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.CompoundButton
import android.widget.ProgressBar
import android.widget.Switch
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import me.rhunk.snapenhance.common.data.ContentType
import me.rhunk.snapenhance.common.data.FriendLinkType
import me.rhunk.snapenhance.common.data.MessageUpdate
import me.rhunk.snapenhance.common.database.impl.ConversationMessage
import me.rhunk.snapenhance.common.database.impl.FriendInfo
import me.rhunk.snapenhance.common.database.impl.UserConversationLink
@ -29,6 +35,9 @@ import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
import kotlin.random.Random
class FriendFeedInfoMenu : AbstractMenu() {
private fun getImageDrawable(url: String): Drawable {
@ -101,6 +110,44 @@ class FriendFeedInfoMenu : AbstractMenu() {
}
}
private fun markAsSeen(conversationId: String) {
val messaging = context.feature(Messaging::class)
val messageIds = messaging.getFeedCachedMessageIds(conversationId)?.takeIf { it.isNotEmpty() } ?: run {
context.shortToast("No recent snaps found")
return
}
var job: Job? = null
val dialog = ViewAppearanceHelper.newAlertDialogBuilder(context.mainActivity)
.setTitle("Processing...")
.setView(ProgressBar(context.mainActivity).apply {
setPadding(10, 10, 10, 10)
})
.setOnDismissListener { job?.cancel() }
.show()
context.coroutineScope.launch(Dispatchers.IO) {
messageIds.forEach { messageId ->
suspendCoroutine { continuation ->
messaging.conversationManager?.updateMessage(conversationId, messageId, MessageUpdate.READ) {
continuation.resume(Unit)
if (it != null && it != "DUPLICATEREQUEST") {
context.log.error("Error marking message as read $it")
}
}
}
delay(Random.nextLong(20, 60))
context.runOnUiThread {
dialog.setTitle("Processing... (${messageIds.indexOf(messageId) + 1}/${messageIds.size})")
}
}
}.also { job = it }.invokeOnCompletion {
context.runOnUiThread {
dialog.dismiss()
}
}
}
private fun showPreview(userId: String?, conversationId: String) {
//query message
val messageLogger = context.feature(MessageLogger::class)
@ -253,5 +300,13 @@ class FriendFeedInfoMenu : AbstractMenu() {
{ ruleFeature.setState(conversationId, it) }
)
}
if (friendFeedMenuOptions.contains("mark_as_seen")) {
viewConsumer(Button(view.context).apply {
text = modContext.translation["friend_menu_option.mark_as_seen"]
applyTheme(view.width, hasRadius = true)
setOnClickListener { markAsSeen(conversationId) }
})
}
}
}