mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-05-03 16:04:30 +02:00
feat(core/events): conversation updated event
This commit is contained in:
parent
37daae3799
commit
d0ff3c35ef
@ -0,0 +1,10 @@
|
|||||||
|
package me.rhunk.snapenhance.core.event.events.impl
|
||||||
|
|
||||||
|
import me.rhunk.snapenhance.core.event.events.AbstractHookEvent
|
||||||
|
import me.rhunk.snapenhance.core.wrapper.impl.Message
|
||||||
|
|
||||||
|
class ConversationUpdateEvent(
|
||||||
|
val conversationId: String,
|
||||||
|
val conversation: Any?,
|
||||||
|
val messages: List<Message>
|
||||||
|
) : AbstractHookEvent()
|
@ -3,13 +3,13 @@ package me.rhunk.snapenhance.core.features.impl.messaging
|
|||||||
import me.rhunk.snapenhance.common.data.MessageState
|
import me.rhunk.snapenhance.common.data.MessageState
|
||||||
import me.rhunk.snapenhance.common.data.MessageUpdate
|
import me.rhunk.snapenhance.common.data.MessageUpdate
|
||||||
import me.rhunk.snapenhance.common.data.MessagingRuleType
|
import me.rhunk.snapenhance.common.data.MessagingRuleType
|
||||||
|
import me.rhunk.snapenhance.core.event.events.impl.ConversationUpdateEvent
|
||||||
import me.rhunk.snapenhance.core.features.FeatureLoadParams
|
import me.rhunk.snapenhance.core.features.FeatureLoadParams
|
||||||
import me.rhunk.snapenhance.core.features.MessagingRuleFeature
|
import me.rhunk.snapenhance.core.features.MessagingRuleFeature
|
||||||
import me.rhunk.snapenhance.core.features.impl.spying.MessageLogger
|
import me.rhunk.snapenhance.core.features.impl.spying.MessageLogger
|
||||||
import me.rhunk.snapenhance.core.features.impl.spying.StealthMode
|
import me.rhunk.snapenhance.core.features.impl.spying.StealthMode
|
||||||
import me.rhunk.snapenhance.core.logger.CoreLogger
|
|
||||||
import me.rhunk.snapenhance.core.util.hook.HookStage
|
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.ktx.getObjectField
|
import me.rhunk.snapenhance.core.util.ktx.getObjectField
|
||||||
import me.rhunk.snapenhance.core.wrapper.impl.Message
|
import me.rhunk.snapenhance.core.wrapper.impl.Message
|
||||||
import me.rhunk.snapenhance.core.wrapper.impl.SnapUUID
|
import me.rhunk.snapenhance.core.wrapper.impl.SnapUUID
|
||||||
@ -19,20 +19,18 @@ class AutoSave : MessagingRuleFeature("Auto Save", MessagingRuleType.AUTO_SAVE,
|
|||||||
private val asyncSaveExecutorService = Executors.newSingleThreadExecutor()
|
private val asyncSaveExecutorService = Executors.newSingleThreadExecutor()
|
||||||
|
|
||||||
private val messageLogger by lazy { context.feature(MessageLogger::class) }
|
private val messageLogger by lazy { context.feature(MessageLogger::class) }
|
||||||
private val messaging by lazy { context.feature(Messaging::class) }
|
|
||||||
|
|
||||||
private val autoSaveFilter by lazy {
|
private val autoSaveFilter by lazy {
|
||||||
context.config.messaging.autoSaveMessagesInConversations.get()
|
context.config.messaging.autoSaveMessagesInConversations.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveMessage(conversationId: SnapUUID, message: Message) {
|
fun saveMessage(conversationId: String, message: Message) {
|
||||||
val messageId = message.messageDescriptor!!.messageId!!
|
val messageId = message.messageDescriptor!!.messageId!!
|
||||||
if (messageLogger.takeIf { it.isEnabled }?.isMessageDeleted(conversationId.toString(), messageId) == true) return
|
if (messageLogger.takeIf { it.isEnabled }?.isMessageDeleted(conversationId, messageId) == true) return
|
||||||
if (message.messageState != MessageState.COMMITTED) return
|
|
||||||
|
|
||||||
runCatching {
|
runCatching {
|
||||||
context.feature(Messaging::class).conversationManager?.updateMessage(
|
context.feature(Messaging::class).conversationManager?.updateMessage(
|
||||||
conversationId.toString(),
|
conversationId,
|
||||||
messageId,
|
messageId,
|
||||||
MessageUpdate.SAVE
|
MessageUpdate.SAVE
|
||||||
) {
|
) {
|
||||||
@ -49,6 +47,8 @@ class AutoSave : MessagingRuleFeature("Auto Save", MessagingRuleType.AUTO_SAVE,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun canSaveMessage(message: Message, headless: Boolean = false): Boolean {
|
fun canSaveMessage(message: Message, headless: Boolean = false): Boolean {
|
||||||
|
if (message.messageState != MessageState.COMMITTED) return false
|
||||||
|
|
||||||
if (!headless && (context.mainActivity == null || context.isMainActivityPaused)) return false
|
if (!headless && (context.mainActivity == null || context.isMainActivityPaused)) return false
|
||||||
if (message.messageMetadata!!.savedBy!!.any { uuid -> uuid.toString() == context.database.myUserId }) return false
|
if (message.messageMetadata!!.savedBy!!.any { uuid -> uuid.toString() == context.database.myUserId }) return false
|
||||||
val contentType = message.messageContent!!.contentType.toString()
|
val contentType = message.messageContent!!.contentType.toString()
|
||||||
@ -69,9 +69,8 @@ class AutoSave : MessagingRuleFeature("Auto Save", MessagingRuleType.AUTO_SAVE,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun asyncOnActivityCreate() {
|
override fun asyncOnActivityCreate() {
|
||||||
//called when enter in a conversation (or when a message is sent)
|
// called when enter in a conversation
|
||||||
Hooker.hook(
|
context.mappings.getMappedClass("callbacks", "FetchConversationWithMessagesCallback").hook(
|
||||||
context.mappings.getMappedClass("callbacks", "FetchConversationWithMessagesCallback"),
|
|
||||||
"onFetchConversationWithMessagesComplete",
|
"onFetchConversationWithMessagesComplete",
|
||||||
HookStage.BEFORE,
|
HookStage.BEFORE,
|
||||||
{ autoSaveFilter.isNotEmpty() }
|
{ autoSaveFilter.isNotEmpty() }
|
||||||
@ -83,45 +82,22 @@ class AutoSave : MessagingRuleFeature("Auto Save", MessagingRuleType.AUTO_SAVE,
|
|||||||
messages.forEach {
|
messages.forEach {
|
||||||
if (!canSaveMessage(it)) return@forEach
|
if (!canSaveMessage(it)) return@forEach
|
||||||
asyncSaveExecutorService.submit {
|
asyncSaveExecutorService.submit {
|
||||||
saveMessage(conversationId, it)
|
saveMessage(conversationId.toString(), it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//called when a message is received
|
context.event.subscribe(
|
||||||
Hooker.hook(
|
ConversationUpdateEvent::class,
|
||||||
context.mappings.getMappedClass("callbacks", "FetchMessageCallback"),
|
|
||||||
"onFetchMessageComplete",
|
|
||||||
HookStage.BEFORE,
|
|
||||||
{ autoSaveFilter.isNotEmpty() }
|
{ autoSaveFilter.isNotEmpty() }
|
||||||
) { param ->
|
) { event ->
|
||||||
val message = Message(param.arg(0))
|
if (!canSaveInConversation(event.conversationId)) return@subscribe
|
||||||
val conversationId = message.messageDescriptor!!.conversationId!!
|
|
||||||
if (!canSaveInConversation(conversationId.toString())) return@hook
|
|
||||||
if (!canSaveMessage(message)) return@hook
|
|
||||||
|
|
||||||
asyncSaveExecutorService.submit {
|
event.messages.forEach { message ->
|
||||||
saveMessage(conversationId, message)
|
if (!canSaveMessage(message)) return@forEach
|
||||||
}
|
asyncSaveExecutorService.submit {
|
||||||
}
|
saveMessage(event.conversationId, message)
|
||||||
|
}
|
||||||
Hooker.hook(
|
|
||||||
context.mappings.getMappedClass("callbacks", "SendMessageCallback"),
|
|
||||||
"onSuccess",
|
|
||||||
HookStage.BEFORE,
|
|
||||||
{ autoSaveFilter.isNotEmpty() }
|
|
||||||
) {
|
|
||||||
val conversationUUID = messaging.openedConversationUUID ?: return@hook
|
|
||||||
runCatching {
|
|
||||||
messaging.conversationManager?.fetchConversationWithMessagesPaginated(
|
|
||||||
conversationUUID.toString(),
|
|
||||||
Long.MAX_VALUE,
|
|
||||||
10,
|
|
||||||
onSuccess = {},
|
|
||||||
onError = {}
|
|
||||||
)
|
|
||||||
}.onFailure {
|
|
||||||
CoreLogger.xposedLog("failed to save message", it)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package me.rhunk.snapenhance.core.features.impl.messaging
|
package me.rhunk.snapenhance.core.features.impl.messaging
|
||||||
|
|
||||||
import me.rhunk.snapenhance.common.ReceiversConfig
|
import me.rhunk.snapenhance.common.ReceiversConfig
|
||||||
|
import me.rhunk.snapenhance.core.event.events.impl.ConversationUpdateEvent
|
||||||
import me.rhunk.snapenhance.core.event.events.impl.OnSnapInteractionEvent
|
import me.rhunk.snapenhance.core.event.events.impl.OnSnapInteractionEvent
|
||||||
import me.rhunk.snapenhance.core.features.Feature
|
import me.rhunk.snapenhance.core.features.Feature
|
||||||
import me.rhunk.snapenhance.core.features.FeatureLoadParams
|
import me.rhunk.snapenhance.core.features.FeatureLoadParams
|
||||||
@ -41,6 +42,21 @@ class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_C
|
|||||||
finishAndRemoveTask()
|
finishAndRemoveTask()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.mappings.getMappedClass("callbacks", "ConversationManagerDelegate").apply {
|
||||||
|
hookConstructor(HookStage.AFTER) { param ->
|
||||||
|
conversationManagerDelegate = param.thisObject()
|
||||||
|
}
|
||||||
|
hook("onConversationUpdated", HookStage.BEFORE) { param ->
|
||||||
|
context.event.post(ConversationUpdateEvent(
|
||||||
|
conversationId = SnapUUID(param.arg(0)).toString(),
|
||||||
|
conversation = param.argNullable(1),
|
||||||
|
messages = param.arg<ArrayList<*>>(2).map { Message(it) },
|
||||||
|
).apply { adapter = param }) {
|
||||||
|
param.setArg(2, messages.map { it.instanceNonNull() }.toCollection(ArrayList()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFeedCachedMessageIds(conversationId: String) = feedCachedSnapMessages[conversationId]
|
fun getFeedCachedMessageIds(conversationId: String) = feedCachedSnapMessages[conversationId]
|
||||||
@ -83,11 +99,6 @@ class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.mappings.getMappedClass("callbacks", "ConversationManagerDelegate").apply {
|
|
||||||
hookConstructor(HookStage.AFTER) { param ->
|
|
||||||
conversationManagerDelegate = param.thisObject()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context.classCache.feedEntry.hookConstructor(HookStage.AFTER) { param ->
|
context.classCache.feedEntry.hookConstructor(HookStage.AFTER) { param ->
|
||||||
val instance = param.thisObject<Any>()
|
val instance = param.thisObject<Any>()
|
||||||
|
@ -37,7 +37,6 @@ import me.rhunk.snapenhance.core.util.ktx.setObjectField
|
|||||||
import me.rhunk.snapenhance.core.util.media.PreviewUtils
|
import me.rhunk.snapenhance.core.util.media.PreviewUtils
|
||||||
import me.rhunk.snapenhance.core.wrapper.impl.Message
|
import me.rhunk.snapenhance.core.wrapper.impl.Message
|
||||||
import me.rhunk.snapenhance.core.wrapper.impl.SnapUUID
|
import me.rhunk.snapenhance.core.wrapper.impl.SnapUUID
|
||||||
import me.rhunk.snapenhance.core.wrapper.impl.toSnapUUID
|
|
||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
|
||||||
class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.INIT_SYNC) {
|
class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.INIT_SYNC) {
|
||||||
@ -245,7 +244,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
|
|||||||
messages.reversed().forEach { message ->
|
messages.reversed().forEach { message ->
|
||||||
if (!autoSave.canSaveMessage(message, headless = true)) return@forEach
|
if (!autoSave.canSaveMessage(message, headless = true)) return@forEach
|
||||||
context.coroutineScope.launch(coroutineDispatcher) {
|
context.coroutineScope.launch(coroutineDispatcher) {
|
||||||
autoSave.saveMessage(conversationId.toSnapUUID(), message)
|
autoSave.saveMessage(conversationId, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -2,11 +2,11 @@ package me.rhunk.snapenhance.core.features.impl.tweaks
|
|||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import me.rhunk.snapenhance.core.event.events.impl.BindViewEvent
|
import me.rhunk.snapenhance.core.event.events.impl.BindViewEvent
|
||||||
|
import me.rhunk.snapenhance.core.event.events.impl.ConversationUpdateEvent
|
||||||
import me.rhunk.snapenhance.core.features.Feature
|
import me.rhunk.snapenhance.core.features.Feature
|
||||||
import me.rhunk.snapenhance.core.features.FeatureLoadParams
|
import me.rhunk.snapenhance.core.features.FeatureLoadParams
|
||||||
import me.rhunk.snapenhance.core.util.hook.HookStage
|
import me.rhunk.snapenhance.core.util.hook.HookStage
|
||||||
import me.rhunk.snapenhance.core.util.hook.hook
|
import me.rhunk.snapenhance.core.util.hook.hook
|
||||||
import me.rhunk.snapenhance.core.wrapper.impl.Message
|
|
||||||
import me.rhunk.snapenhance.core.wrapper.impl.SnapUUID
|
import me.rhunk.snapenhance.core.wrapper.impl.SnapUUID
|
||||||
|
|
||||||
class PreventMessageListAutoScroll : Feature("PreventMessageListAutoScroll", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) {
|
class PreventMessageListAutoScroll : Feature("PreventMessageListAutoScroll", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) {
|
||||||
@ -18,28 +18,28 @@ class PreventMessageListAutoScroll : Feature("PreventMessageListAutoScroll", loa
|
|||||||
override fun onActivityCreate() {
|
override fun onActivityCreate() {
|
||||||
if (!context.config.userInterface.preventMessageListAutoScroll.get()) return
|
if (!context.config.userInterface.preventMessageListAutoScroll.get()) return
|
||||||
|
|
||||||
context.mappings.getMappedClass("callbacks", "ConversationManagerDelegate").hook("onConversationUpdated", HookStage.BEFORE) { param ->
|
context.event.subscribe(ConversationUpdateEvent::class) { event ->
|
||||||
val updatedMessage = param.arg<ArrayList<*>>(2).map { Message(it) }.firstOrNull() ?: return@hook
|
val updatedMessage = event.messages.firstOrNull() ?: return@subscribe
|
||||||
if (openedConversationId != updatedMessage.messageDescriptor?.conversationId.toString()) return@hook
|
if (openedConversationId != updatedMessage.messageDescriptor?.conversationId.toString()) return@subscribe
|
||||||
|
|
||||||
// cancel if the message is already in focus
|
// cancel if the message is already in focus
|
||||||
if (focusedMessages.entries.any { entry -> entry.value == updatedMessage.messageDescriptor?.messageId && entry.key.isAttachedToWindow }) return@hook
|
if (focusedMessages.entries.any { entry -> entry.value == updatedMessage.messageDescriptor?.messageId && entry.key.isAttachedToWindow }) return@subscribe
|
||||||
|
|
||||||
val conversationLastMessages = context.database.getMessagesFromConversationId(
|
val conversationLastMessages = context.database.getMessagesFromConversationId(
|
||||||
openedConversationId.toString(),
|
openedConversationId.toString(),
|
||||||
4
|
4
|
||||||
) ?: return@hook
|
) ?: return@subscribe
|
||||||
|
|
||||||
if (conversationLastMessages.none {
|
if (conversationLastMessages.none {
|
||||||
focusedMessages.entries.any { entry -> entry.value == it.clientMessageId.toLong() && entry.key.isAttachedToWindow }
|
focusedMessages.entries.any { entry -> entry.value == it.clientMessageId.toLong() && entry.key.isAttachedToWindow }
|
||||||
}) {
|
}) {
|
||||||
synchronized(delayedMessageUpdates) {
|
synchronized(delayedMessageUpdates) {
|
||||||
if (firstFocusedMessageId == null) firstFocusedMessageId = conversationLastMessages.lastOrNull()?.clientMessageId?.toLong()
|
if (firstFocusedMessageId == null) firstFocusedMessageId = conversationLastMessages.lastOrNull()?.clientMessageId?.toLong()
|
||||||
delayedMessageUpdates.add {
|
delayedMessageUpdates.add {
|
||||||
param.invokeOriginal()
|
event.adapter.invokeOriginal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
param.setResult(null)
|
event.adapter.setResult(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user