Skip to content

Commit

Permalink
Fix usernames in downloaded chat snaps.
Browse files Browse the repository at this point in the history
Refactor callback based code.
  • Loading branch information
rodit committed May 7, 2022
1 parent f6e4a3e commit 0c5f4c4
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 102 deletions.
Binary file modified app/libs/snapmod.jar
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package xyz.rodit.snapmod.features

import android.app.Activity
import android.content.Context
import xyz.rodit.snapmod.features.callbacks.CallbackManager
import xyz.rodit.snapmod.util.ConversationManager
import xyz.rodit.xposed.client.ConfigurationClient
import xyz.rodit.xposed.client.FileClient
Expand All @@ -18,6 +19,7 @@ class FeatureContext(
val server: StreamServer,
val instances: InstanceManager
) {
val callbacks: CallbackManager = CallbackManager()
val pinned: ConversationManager = ConversationManager(appContext.filesDir, PINNED_CONVERSATIONS_FILE)
val stealth: ConversationManager = ConversationManager(appContext.filesDir, STEALTH_CONVERSATIONS_FILE)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package xyz.rodit.snapmod.features.callbacks

import de.robv.android.xposed.XC_MethodHook
import xyz.rodit.dexsearch.client.xposed.MethodRef
import xyz.rodit.snapmod.isDummyProxy
import xyz.rodit.snapmod.util.before
import kotlin.reflect.KClass

typealias HookedCallback = (XC_MethodHook.MethodHookParam) -> Boolean

class CallbackManager {

private val callbacks = mutableMapOf<String, MutableList<HookedCallback>>()

fun hook(type: KClass<*>, method: MethodRef, obtainInterface: (Any) -> Any) {
method.before {
if (!obtainInterface(it.thisObject).isDummyProxy) return@before

callbacks["${type.simpleName}:${method.name}"]?.let { list ->
val remove = list.filter { c -> c(it) }
list.removeAll(remove)
}

it.result = null
}
}

fun on(type: KClass<*>, method: MethodRef, callback: HookedCallback) {
callbacks.computeIfAbsent("${type.simpleName}:${method.name}") { mutableListOf() }.add(callback)
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package xyz.rodit.snapmod.features.chatmenu

import android.app.AlertDialog
import de.robv.android.xposed.XC_MethodHook
import xyz.rodit.snapmod.CustomResources
import xyz.rodit.snapmod.createDummyProxy
import xyz.rodit.snapmod.features.FeatureContext
import xyz.rodit.snapmod.isDummyProxy
import xyz.rodit.snapmod.mappings.*
import xyz.rodit.snapmod.util.before
import xyz.rodit.snapmod.util.toSnapUUID
import xyz.rodit.snapmod.util.toUUIDString
import java.lang.Integer.min
Expand All @@ -27,62 +26,73 @@ class PreviewOption(context: FeatureContext) :
ConversationDummyInterface.getMappedClass().createDummyProxy(context.classLoader)
)

context.callbacks.on(
DefaultFetchConversationCallback::class,
DefaultFetchConversationCallback.onFetchConversationWithMessagesComplete,
this::displayPreview
)

context.instances.conversationManager.fetchConversationWithMessages(
uuid,
DefaultFetchConversationCallback(proxy, uuid, false)
)
}

override fun performHooks() {
DefaultFetchConversationCallback.onFetchConversationWithMessagesComplete.before {
if (!DefaultFetchConversationCallback.wrap(it.thisObject).dummy.isDummyProxy) return@before
context.callbacks.hook(
DefaultFetchConversationCallback::class,
DefaultFetchConversationCallback.onFetchConversationWithMessagesComplete
) { DefaultFetchConversationCallback.wrap(it).dummy }
}

val conversation = Conversation.wrap(it.args[0])
private fun displayPreview(param: XC_MethodHook.MethodHookParam): Boolean {
val conversation = Conversation.wrap(param.args[0])

val userIds = conversation.participants.map(Participant::wrap)
.map { p -> (p.participantId.id as ByteArray).toUUIDString() }
val friendData = context.instances.friendsRepository.selectFriendsByUserIds(userIds)
val userMap = friendData.map(SelectFriendsByUserIds::wrap).associateBy { u -> u.userId }
val userIds = conversation.participants.map(Participant::wrap)
.map { p -> (p.participantId.id as ByteArray).toUUIDString() }
val friendData = context.instances.friendsRepository.selectFriendsByUserIds(userIds)
val userMap = friendData.map(SelectFriendsByUserIds::wrap).associateBy { u -> u.userId }

val messageList = it.args[1] as List<*>
val previewText = StringBuilder()
if (messageList.isEmpty()) previewText.append("No messages available.")
else {
val numMessages =
min(context.config.getInt("preview_messages_count", 5), messageList.size)
previewText.append("Last ").append(numMessages).append(" messages:")
messageList.takeLast(numMessages)
.map(Message::wrap).forEach { m ->
run {
val uuidString = m.senderId.toUUIDString()
val displayName = userMap[uuidString]?.displayName ?: "Unknown"
previewText.append('\n').append(displayName).append(": ")
if (m.messageContent.contentType.instance == ContentType.CHAT().instance) {
val chatMessage =
NanoMessageContent.parse(m.messageContent.content).chatMessageContent.content
previewText.append(chatMessage)
} else {
previewText.append(m.messageContent.contentType.instance)
}
val messageList = param.args[1] as List<*>
val previewText = StringBuilder()
if (messageList.isEmpty()) previewText.append("No messages available.")
else {
val numMessages =
min(context.config.getInt("preview_messages_count", 5), messageList.size)
previewText.append("Last ").append(numMessages).append(" messages:")
messageList.takeLast(numMessages)
.map(Message::wrap).forEach { m ->
run {
val uuidString = m.senderId.toUUIDString()
val displayName = userMap[uuidString]?.displayName ?: "Unknown"
previewText.append('\n').append(displayName).append(": ")
if (m.messageContent.contentType.instance == ContentType.CHAT().instance) {
val chatMessage =
NanoMessageContent.parse(m.messageContent.content).chatMessageContent.content
previewText.append(chatMessage)
} else {
previewText.append(m.messageContent.contentType.instance)
}
}
}
}
}

userMap.values.find { f -> f.streakExpiration ?: 0L > 0L }?.let { f ->
val hourDiff =
(f.streakExpiration - System.currentTimeMillis()).toDouble() / 3600000.0
previewText.append("\n\nStreak Expires in ")
.append(String.format("%.1f", hourDiff))
.append(" hours")
}
userMap.values.find { f -> f.streakExpiration ?: 0L > 0L }?.let { f ->
val hourDiff =
(f.streakExpiration - System.currentTimeMillis()).toDouble() / 3600000.0
previewText.append("\n\nStreak Expires in ")
.append(String.format("%.1f", hourDiff))
.append(" hours")
}

context.activity?.runOnUiThread {
AlertDialog.Builder(context.activity)
.setTitle(if (conversation.title.isNullOrBlank()) "Chat Preview" else conversation.title)
.setMessage(previewText)
.setPositiveButton("Ok") { _, _ -> }
.show()
}
context.activity?.runOnUiThread {
AlertDialog.Builder(context.activity)
.setTitle(if (conversation.title.isNullOrBlank()) "Chat Preview" else conversation.title)
.setMessage(previewText)
.setPositiveButton("Ok") { _, _ -> }
.show()
}

return true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,36 +17,36 @@ class SnapOverrides(context: FeatureContext) : Feature(context) {

val content = SerializableContent.wrap(container.instance)
val message = content.message
if (GallerySnapMedia.isInstance(message.instance)) {
val id = GallerySnapMedia.wrap(message.instance).media.id
val snap = LiveSnapMedia()
val timer = context.config.getString("override_snap_timer", "0").toDouble()
snap.mediaId = id
if (context.config.getBoolean("enable_snap_type_override")) {
val overrideType = context.config.getString("snap_type_override", "IMAGE")
snap.mediaType = MediaType.valueOf(overrideType)
} else {
snap.mediaType = MediaType.IMAGE()
}
if (!GallerySnapMedia.isInstance(message.instance)) return@before

val paramPackage = ParameterPackage(
timer == 0.0,
timer,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
false
)
snap.parameterPackage = paramPackage
content.message = MediaBaseBase.wrap(snap.instance)
val id = GallerySnapMedia.wrap(message.instance).media.id
val snap = LiveSnapMedia()
val timer = context.config.getString("override_snap_timer", "0").toDouble()
snap.mediaId = id
if (context.config.getBoolean("enable_snap_type_override")) {
val overrideType = context.config.getString("snap_type_override", "IMAGE")
snap.mediaType = MediaType.valueOf(overrideType)
} else {
snap.mediaType = MediaType.IMAGE()
}

val paramPackage = ParameterPackage(
timer == 0.0,
timer,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
false
)
snap.parameterPackage = paramPackage
content.message = MediaBaseBase.wrap(snap.instance)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ package xyz.rodit.snapmod.features.saving
import de.robv.android.xposed.XposedBridge
import xyz.rodit.snapmod.features.Feature
import xyz.rodit.snapmod.features.FeatureContext
import xyz.rodit.snapmod.mappings.ContextActionMenuModel
import xyz.rodit.snapmod.mappings.ContextClickHandler
import xyz.rodit.snapmod.mappings.OperaContextAction
import xyz.rodit.snapmod.mappings.ParamsMap
import xyz.rodit.snapmod.mappings.*
import xyz.rodit.snapmod.util.PathManager
import xyz.rodit.snapmod.util.after
import xyz.rodit.snapmod.util.download
Expand All @@ -32,6 +29,11 @@ class StoriesSaving(context: FeatureContext) : Feature(context) {
)
model.onClick = ContextClickHandler.wrap(clickProxy)
}

context.callbacks.hook(
ConversationManager::class,
DefaultFetchMessageCallback.onFetchMessageComplete
) { DefaultFetchMessageCallback.wrap(it).dummy }
}

private class StoryDownloadProxy(private val context: FeatureContext) : InvocationHandler {
Expand All @@ -40,10 +42,15 @@ class StoriesSaving(context: FeatureContext) : Feature(context) {
if (method.name != ContextClickHandler.invoke.dexName) return null

val map = ParamsMap.wrap(args!![0])
val media = StoryHelper.getMediaInfo(map)
StoryHelper.getMediaInfo(context, map, this::downloadStoryMedia)

return null
}

private fun downloadStoryMedia(media: StoryHelper.StoryMedia?) {
if (media == null || media.info.isNull) {
XposedBridge.log("Null media info for story download.")
return null
return
}

val provider: StreamProvider = FileProxyStreamProvider(context.appContext) {
Expand All @@ -69,7 +76,6 @@ class StoriesSaving(context: FeatureContext) : Feature(context) {
provider,
media.username + "'s Story"
)
return null
}
}
}
Loading

0 comments on commit 0c5f4c4

Please sign in to comment.