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": {
|
"remove_groups_locked_status": {
|
||||||
"name": "Remove Groups Locked Status",
|
"name": "Remove Groups Locked Status",
|
||||||
"description": "Allows you to view group information after being kicked"
|
"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",
|
"not_subscribed": "Not Subscribed",
|
||||||
"basic": "Basic",
|
"basic": "Basic",
|
||||||
"ad_free": "Ad Free"
|
"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 bypassMessageRetentionPolicy = boolean("bypass_message_retention_policy") { addNotices(FeatureNotice.UNSTABLE); requireRestart() }
|
||||||
val bypassMessageActionRestrictions = boolean("bypass_message_action_restrictions") { requireRestart() }
|
val bypassMessageActionRestrictions = boolean("bypass_message_action_restrictions") { requireRestart() }
|
||||||
val removeGroupsLockedStatus = boolean("remove_groups_locked_status") { 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(),
|
BetterTranscript(),
|
||||||
VoiceNoteOverride(),
|
VoiceNoteOverride(),
|
||||||
FriendNotes(),
|
FriendNotes(),
|
||||||
|
DoubleTapChatAction(),
|
||||||
)
|
)
|
||||||
|
|
||||||
features.values.toList().forEach { feature ->
|
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 clearConversation by lazy { findMethodByName("clearConversation") }
|
||||||
private val getOneOnOneConversationIds by lazy { findMethodByName("getOneOnOneConversationIds") }
|
private val getOneOnOneConversationIds by lazy { findMethodByName("getOneOnOneConversationIds") }
|
||||||
private val dismissStreakRestore by lazy { findMethodByName("dismissStreakRestore") }
|
private val dismissStreakRestore by lazy { findMethodByName("dismissStreakRestore") }
|
||||||
|
private val reactToMessageMethod by lazy { findMethodByName("reactToMessage") }
|
||||||
|
|
||||||
|
|
||||||
private fun getCallbackClass(name: String): Class<*> {
|
private fun getCallbackClass(name: String): Class<*> {
|
||||||
@ -183,4 +184,24 @@ class ConversationManager(
|
|||||||
.override("onError") { onError(it.arg<Any>(0).toString()) }.build()
|
.override("onError") { onError(it.arg<Any>(0).toString()) }.build()
|
||||||
dismissStreakRestore.invoke(instanceNonNull(), conversationId.toSnapUUID().instanceNonNull(), callback)
|
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(),
|
PlusSubscriptionMapper(),
|
||||||
StoryBoostStateMapper(),
|
StoryBoostStateMapper(),
|
||||||
FriendsFeedEventDispatcherMapper(),
|
FriendsFeedEventDispatcherMapper(),
|
||||||
|
ChatEventDispatcherMapper(),
|
||||||
CompositeConfigurationProviderMapper(),
|
CompositeConfigurationProviderMapper(),
|
||||||
ScoreUpdateMapper(),
|
ScoreUpdateMapper(),
|
||||||
FriendRelationshipChangerMapper(),
|
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