event dispatcher

This commit is contained in:
rhunk
2023-08-02 09:17:17 +02:00
parent 369ed0a8a6
commit 5e827d1c46
10 changed files with 87 additions and 25 deletions

View File

@ -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)
}
}
}
}
}

View File

@ -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)

View File

@ -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()

View File

@ -1,10 +1,12 @@
package me.rhunk.snapenhance.event
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> {
@ -23,12 +25,14 @@ class EventBus(
subscribers[event]!!.add(listener)
}
fun <T : Event> subscribe(event: KClass<T>, listener: (T) -> Unit) {
subscribe(event, object : IListener<T> {
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>) {
@ -38,22 +42,22 @@ class EventBus(
subscribers[event]!!.remove(listener)
}
fun <T : Event> post(event: T) {
fun <T : Event> post(event: T): T? {
if (!subscribers.containsKey(event::class)) {
return
return null
}
event.context = context
subscribers[event::class]!!.forEach { listener ->
subscribers[event::class]?.forEach { listener ->
@Suppress("UNCHECKED_CAST")
try {
runCatching {
(listener as IListener<T>).handle(event)
} catch (t: Throwable) {
println("Error while handling event ${event::class.simpleName} by ${listener::class.simpleName}")
t.printStackTrace()
}.onFailure { t ->
Logger.error("Error while handling event ${event::class.simpleName} by ${listener::class.simpleName}", t)
}
}
return event
}
fun clear() {

View File

@ -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()

View File

@ -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()

View File

@ -1,3 +0,0 @@
package me.rhunk.snapenhance.event
//TODO: addView event

View File

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

View File

@ -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
}
}
}

View File

@ -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
}
}
}