mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-04-29 22:24:35 +02:00
feat(core/tweaks): double tap chat action
- like, delete, copy message text, mark as read Signed-off-by: rhunk <101876869+rhunk@users.noreply.github.com>
This commit is contained in:
parent
0c99ae15ba
commit
008c094cd4
@ -743,6 +743,10 @@
|
||||
"remove_groups_locked_status": {
|
||||
"name": "Remove Groups Locked Status",
|
||||
"description": "Allows you to view group information after being kicked"
|
||||
},
|
||||
"double_tap_chat_action": {
|
||||
"name": "Double Tap Chat Action",
|
||||
"description": "Performs a custom action when double tapping a message in chat"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1385,6 +1389,12 @@
|
||||
"not_subscribed": "Not Subscribed",
|
||||
"basic": "Basic",
|
||||
"ad_free": "Ad Free"
|
||||
},
|
||||
"double_tap_chat_action": {
|
||||
"like_message": "Like Message",
|
||||
"copy_text": "Copy Text to Clipboard",
|
||||
"delete_message": "Delete Message",
|
||||
"mark_as_read": "Mark as Read"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -99,4 +99,5 @@ class MessagingTweaks : ConfigContainer() {
|
||||
val bypassMessageRetentionPolicy = boolean("bypass_message_retention_policy") { addNotices(FeatureNotice.UNSTABLE); requireRestart() }
|
||||
val bypassMessageActionRestrictions = boolean("bypass_message_action_restrictions") { requireRestart() }
|
||||
val removeGroupsLockedStatus = boolean("remove_groups_locked_status") { requireRestart() }
|
||||
val doubleTapChatAction = unique("double_tap_chat_action", "like_message", "copy_text", "delete_message", "mark_as_read") { requireRestart() }
|
||||
}
|
@ -141,6 +141,7 @@ class FeatureManager(
|
||||
BetterTranscript(),
|
||||
VoiceNoteOverride(),
|
||||
FriendNotes(),
|
||||
DoubleTapChatAction(),
|
||||
)
|
||||
|
||||
features.values.toList().forEach { feature ->
|
||||
|
@ -0,0 +1,56 @@
|
||||
package me.rhunk.snapenhance.core.features.impl.tweaks
|
||||
|
||||
import me.rhunk.snapenhance.common.data.ContentType
|
||||
import me.rhunk.snapenhance.common.data.MessageUpdate
|
||||
import me.rhunk.snapenhance.common.util.ktx.copyToClipboard
|
||||
import me.rhunk.snapenhance.common.util.ktx.findFieldsToString
|
||||
import me.rhunk.snapenhance.common.util.protobuf.ProtoReader
|
||||
import me.rhunk.snapenhance.core.features.Feature
|
||||
import me.rhunk.snapenhance.core.features.impl.messaging.Messaging
|
||||
import me.rhunk.snapenhance.core.util.hook.HookStage
|
||||
import me.rhunk.snapenhance.core.util.hook.hook
|
||||
import me.rhunk.snapenhance.core.wrapper.impl.getMessageText
|
||||
import me.rhunk.snapenhance.mapper.impl.ChatEventDispatcherMapper
|
||||
|
||||
class DoubleTapChatAction: Feature("Double Tap Chat Action") {
|
||||
override fun init() {
|
||||
var action = context.config.messaging.doubleTapChatAction.getNullable() ?: return
|
||||
|
||||
context.mappings.useMapper(ChatEventDispatcherMapper::class) {
|
||||
classReference.getAsClass()?.hook("onChatItemDoubleClickEvent", HookStage.BEFORE) { param ->
|
||||
param.setResult(null)
|
||||
val event = param.arg<Any>(0)
|
||||
val viewModel = event.javaClass.findFieldsToString(event, once = true) { field, value -> value.contains("ChatViewModel") }.firstOrNull()?.get(event)?.toString() ?: return@hook
|
||||
|
||||
val (conversationId, _, clientMessageId) = viewModel.substringAfter("messageId=").substringBefore(",").split(":").takeIf { it.size == 3 } ?: return@hook
|
||||
|
||||
val messageId = clientMessageId.toLongOrNull() ?: return@hook
|
||||
|
||||
if (action == "like_message") {
|
||||
context.feature(Messaging::class).conversationManager?.reactToMessage(
|
||||
conversationId,
|
||||
messageId,
|
||||
intentionType = 1L,
|
||||
onError = {},
|
||||
onSuccess = {}
|
||||
)
|
||||
}
|
||||
|
||||
if (action == "copy_text") {
|
||||
var messageContent = context.database.getConversationMessageFromId(messageId)?.messageContent ?: return@hook
|
||||
var proto = ProtoReader(messageContent).followPath(4, 4) ?: return@hook
|
||||
context.androidContext.copyToClipboard(proto.getBuffer().getMessageText(ContentType.fromMessageContainer(proto) ?: ContentType.CHAT) ?: return@hook, "Chat Message")
|
||||
}
|
||||
|
||||
if (action == "delete_message" || action == "mark_as_read") {
|
||||
context.feature(Messaging::class).conversationManager?.updateMessage(
|
||||
conversationId,
|
||||
messageId,
|
||||
if (action == "delete_message") MessageUpdate.ERASE else MessageUpdate.READ,
|
||||
onResult = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -27,6 +27,7 @@ class ConversationManager(
|
||||
private val clearConversation by lazy { findMethodByName("clearConversation") }
|
||||
private val getOneOnOneConversationIds by lazy { findMethodByName("getOneOnOneConversationIds") }
|
||||
private val dismissStreakRestore by lazy { findMethodByName("dismissStreakRestore") }
|
||||
private val reactToMessageMethod by lazy { findMethodByName("reactToMessage") }
|
||||
|
||||
|
||||
private fun getCallbackClass(name: String): Class<*> {
|
||||
@ -183,4 +184,24 @@ class ConversationManager(
|
||||
.override("onError") { onError(it.arg<Any>(0).toString()) }.build()
|
||||
dismissStreakRestore.invoke(instanceNonNull(), conversationId.toSnapUUID().instanceNonNull(), callback)
|
||||
}
|
||||
|
||||
fun reactToMessage(conversationId: String, messageId: Long, emoji: String? = null, intentionType: Long? = null, onSuccess: () -> Unit, onError: (error: String) -> Unit) {
|
||||
reactToMessageMethod.invoke(
|
||||
instanceNonNull(),
|
||||
conversationId.toSnapUUID().instanceNonNull(),
|
||||
messageId,
|
||||
reactToMessageMethod.parameterTypes[2].dataBuilder {
|
||||
set("mEmoji", emoji)
|
||||
set("mIntentionType", intentionType)
|
||||
},
|
||||
reactToMessageMethod.parameterTypes[3].dataBuilder {
|
||||
set("mMetricsMessageMediaType", "NO_MEDIA")
|
||||
set("mMetricsMessageType", "TEXT")
|
||||
set("mReactionSource", "NONE")
|
||||
},
|
||||
CallbackBuilder(getCallbackClass("Callback"))
|
||||
.override("onSuccess") { onSuccess() }
|
||||
.override("onError") { onError(it.arg<Any>(0).toString()) }.build()
|
||||
)
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ class ClassMapper(
|
||||
PlusSubscriptionMapper(),
|
||||
StoryBoostStateMapper(),
|
||||
FriendsFeedEventDispatcherMapper(),
|
||||
ChatEventDispatcherMapper(),
|
||||
CompositeConfigurationProviderMapper(),
|
||||
ScoreUpdateMapper(),
|
||||
FriendRelationshipChangerMapper(),
|
||||
|
@ -0,0 +1,18 @@
|
||||
package me.rhunk.snapenhance.mapper.impl
|
||||
|
||||
import me.rhunk.snapenhance.mapper.AbstractClassMapper
|
||||
import me.rhunk.snapenhance.mapper.ext.getClassName
|
||||
|
||||
class ChatEventDispatcherMapper : AbstractClassMapper("ChatEventDispatcher") {
|
||||
val classReference = classReference("class")
|
||||
|
||||
init {
|
||||
mapper {
|
||||
for (clazz in classes) {
|
||||
if (clazz.methods.firstOrNull { it.name == "onChatItemDoubleClickEvent" } == null) continue
|
||||
classReference.set(clazz.getClassName())
|
||||
return@mapper
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user