commit 5e827d1c46e75bcbae3e33b6ebfab8b1979fc6b3 parent 369ed0a8a640bea6e3728a1ccc3bc5225c020ad7 Author: rhunk <101876869+rhunk@users.noreply.github.com> Date: Wed, 2 Aug 2023 09:17:17 +0200 event dispatcher Diffstat:
11 files changed, 142 insertions(+), 78 deletions(-)
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/EventDispatcher.kt b/core/src/main/kotlin/me/rhunk/snapenhance/EventDispatcher.kt @@ -0,0 +1,37 @@ +package me.rhunk.snapenhance + +import me.rhunk.snapenhance.core.event.impl.OnSnapInteractionEvent +import me.rhunk.snapenhance.core.event.impl.SendMessageWithContentEvent +import me.rhunk.snapenhance.data.wrapper.impl.MessageContent +import me.rhunk.snapenhance.data.wrapper.impl.SnapUUID +import me.rhunk.snapenhance.hook.HookStage +import me.rhunk.snapenhance.hook.hook +import me.rhunk.snapenhance.manager.Manager + +class EventDispatcher( + private val context: ModContext +) : Manager { + override fun init() { + context.classCache.conversationManager.hook("sendMessageWithContent", HookStage.BEFORE) { param -> + val messageContent = MessageContent(param.arg(1)) + context.event.post(SendMessageWithContentEvent(messageContent))?.let { + if (it.canceled) { + param.setResult(null) + } + } + } + + context.classCache.snapManager.hook("onSnapInteraction", HookStage.BEFORE) { param -> + val conversationId = SnapUUID(param.arg(1)) + val messageId = param.arg<Long>(2) + context.event.post(OnSnapInteractionEvent( + conversationId = conversationId, + messageId = messageId + ))?.let { + if (it.canceled) { + param.setResult(null) + } + } + } + } +}+ \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt @@ -14,6 +14,7 @@ import kotlinx.coroutines.asCoroutineDispatcher import me.rhunk.snapenhance.bridge.BridgeClient import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper import me.rhunk.snapenhance.core.config.ModConfig +import me.rhunk.snapenhance.core.event.EventBus import me.rhunk.snapenhance.data.MessageSender import me.rhunk.snapenhance.database.DatabaseAccess import me.rhunk.snapenhance.features.Feature @@ -41,6 +42,8 @@ class ModContext { private val modConfig = ModConfig() val config by modConfig + val event = EventBus(this) + val eventDispatcher = EventDispatcher(this) val translation = TranslationWrapper() val features = FeatureManager(this) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt b/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt @@ -99,6 +99,7 @@ class SnapEnhance { with(appContext) { reloadConfig() mappings.init() + eventDispatcher.init() //if mappings aren't loaded, we can't initialize features if (!mappings.areMappingsLoaded) return features.init() diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/event/EventBus.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/event/EventBus.kt @@ -0,0 +1,66 @@ +package me.rhunk.snapenhance.core.event + +import me.rhunk.snapenhance.Logger +import me.rhunk.snapenhance.ModContext +import kotlin.reflect.KClass + +abstract class Event { + lateinit var context: ModContext + var canceled = false +} + +interface IListener<T> { + fun handle(event: T) +} + +class EventBus( + private val context: ModContext +) { + private val subscribers = mutableMapOf<KClass<out Event>, MutableList<IListener<out Event>>>() + + fun <T : Event> subscribe(event: KClass<T>, listener: IListener<T>) { + if (!subscribers.containsKey(event)) { + subscribers[event] = mutableListOf() + } + subscribers[event]!!.add(listener) + } + + inline fun <T : Event> subscribe(event: KClass<T>, crossinline listener: (T) -> Unit): () -> Unit { + val obj = object : IListener<T> { + override fun handle(event: T) { + listener(event) + } + } + subscribe(event, obj) + return { unsubscribe(event, obj) } + } + + fun <T : Event> unsubscribe(event: KClass<T>, listener: IListener<T>) { + if (!subscribers.containsKey(event)) { + return + } + subscribers[event]!!.remove(listener) + } + + fun <T : Event> post(event: T): T? { + if (!subscribers.containsKey(event::class)) { + return null + } + + event.context = context + + subscribers[event::class]?.forEach { listener -> + @Suppress("UNCHECKED_CAST") + runCatching { + (listener as IListener<T>).handle(event) + }.onFailure { t -> + Logger.error("Error while handling event ${event::class.simpleName} by ${listener::class.simpleName}", t) + } + } + return event + } + + fun clear() { + subscribers.clear() + } +}+ \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/event/impl/OnSnapInteractionEvent.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/event/impl/OnSnapInteractionEvent.kt @@ -0,0 +1,9 @@ +package me.rhunk.snapenhance.core.event.impl + +import me.rhunk.snapenhance.core.event.Event +import me.rhunk.snapenhance.data.wrapper.impl.SnapUUID + +class OnSnapInteractionEvent( + val conversationId: SnapUUID, + val messageId: Long +) : Event()+ \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/event/impl/SendMessageWithContentEvent.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/event/impl/SendMessageWithContentEvent.kt @@ -0,0 +1,8 @@ +package me.rhunk.snapenhance.core.event.impl + +import me.rhunk.snapenhance.core.event.Event +import me.rhunk.snapenhance.data.wrapper.impl.MessageContent + +class SendMessageWithContentEvent( + val messageContent: MessageContent +) : Event()+ \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/event/EventBus.kt b/core/src/main/kotlin/me/rhunk/snapenhance/event/EventBus.kt @@ -1,62 +0,0 @@ -package me.rhunk.snapenhance.event - -import me.rhunk.snapenhance.ModContext -import kotlin.reflect.KClass - -abstract class Event { - lateinit var context: ModContext -} - -interface IListener<T> { - fun handle(event: T) -} - -class EventBus( - private val context: ModContext -) { - private val subscribers = mutableMapOf<KClass<out Event>, MutableList<IListener<out Event>>>() - - fun <T : Event> subscribe(event: KClass<T>, listener: IListener<T>) { - if (!subscribers.containsKey(event)) { - subscribers[event] = mutableListOf() - } - subscribers[event]!!.add(listener) - } - - fun <T : Event> subscribe(event: KClass<T>, listener: (T) -> Unit) { - subscribe(event, object : IListener<T> { - override fun handle(event: T) { - listener(event) - } - }) - } - - fun <T : Event> unsubscribe(event: KClass<T>, listener: IListener<T>) { - if (!subscribers.containsKey(event)) { - return - } - subscribers[event]!!.remove(listener) - } - - fun <T : Event> post(event: T) { - if (!subscribers.containsKey(event::class)) { - return - } - - event.context = context - - subscribers[event::class]!!.forEach { listener -> - @Suppress("UNCHECKED_CAST") - try { - (listener as IListener<T>).handle(event) - } catch (t: Throwable) { - println("Error while handling event ${event::class.simpleName} by ${listener::class.simpleName}") - t.printStackTrace() - } - } - } - - fun clear() { - subscribers.clear() - } -}- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/event/Events.kt b/core/src/main/kotlin/me/rhunk/snapenhance/event/Events.kt @@ -1,3 +0,0 @@ -package me.rhunk.snapenhance.event - -//TODO: addView event- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt @@ -1,5 +1,6 @@ package me.rhunk.snapenhance.features.impl +import me.rhunk.snapenhance.core.event.impl.OnSnapInteractionEvent import me.rhunk.snapenhance.data.wrapper.impl.SnapUUID import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams @@ -72,9 +73,9 @@ class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_C } //get last opened snap for media downloader - Hooker.hook(context.classCache.snapManager, "onSnapInteraction", HookStage.BEFORE) { param -> - openedConversationUUID = SnapUUID(param.arg(1)) - lastFocusedMessageId = param.arg(2) + context.event.subscribe(OnSnapInteractionEvent::class) { event -> + openedConversationUUID = event.conversationId + lastFocusedMessageId = event.messageId } Hooker.hook(context.classCache.conversationManager, "fetchMessage", HookStage.BEFORE) { param -> diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt @@ -1,6 +1,7 @@ package me.rhunk.snapenhance.features.impl.privacy import me.rhunk.snapenhance.Logger +import me.rhunk.snapenhance.core.event.impl.SendMessageWithContentEvent import me.rhunk.snapenhance.data.NotificationType import me.rhunk.snapenhance.data.wrapper.impl.MessageContent import me.rhunk.snapenhance.features.Feature @@ -23,14 +24,13 @@ class PreventMessageSending : Feature("Prevent message sending", loadParams = Fe } } - context.classCache.conversationManager.hook("sendMessageWithContent", HookStage.BEFORE) { param -> - val message = MessageContent(param.arg(1)) - val contentType = message.contentType - val associatedType = NotificationType.fromContentType(contentType) ?: return@hook + context.event.subscribe(SendMessageWithContentEvent::class) { event -> + val contentType = event.messageContent.contentType + val associatedType = NotificationType.fromContentType(contentType) ?: return@subscribe if (preventMessageSending.contains(associatedType.key)) { Logger.debug("Preventing message sending for $associatedType") - param.setResult(null) + event.canceled = true } } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt @@ -1,5 +1,6 @@ package me.rhunk.snapenhance.features.impl.spying +import me.rhunk.snapenhance.core.event.impl.OnSnapInteractionEvent import me.rhunk.snapenhance.data.wrapper.impl.SnapUUID import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams @@ -19,9 +20,10 @@ class PreventReadReceipts : Feature("PreventReadReceipts", loadParams = FeatureL it.setResult(null) } } - Hooker.hook(context.classCache.snapManager, "onSnapInteraction", HookStage.BEFORE) { - if (isConversationInStealthMode(SnapUUID(it.arg(1) as Any))) { - it.setResult(null) + + context.event.subscribe(OnSnapInteractionEvent::class) { event -> + if (isConversationInStealthMode(event.conversationId)) { + event.canceled = true } } }