mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-06-13 05:37:48 +02:00
refactor: wrappers
This commit is contained in:
@ -176,7 +176,7 @@ class ExportChatMessages : AbstractAction() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fetchedMessages.firstOrNull()?.let {
|
fetchedMessages.firstOrNull()?.let {
|
||||||
lastMessageId = it.messageDescriptor.messageId
|
lastMessageId = it.messageDescriptor!!.messageId!!
|
||||||
}
|
}
|
||||||
setStatus("Exporting (${foundMessages.size} / ${foundMessages.firstOrNull()?.orderKey})")
|
setStatus("Exporting (${foundMessages.size} / ${foundMessages.firstOrNull()?.orderKey})")
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ class ScopeSync : Feature("Scope Sync", loadParams = FeatureLoadParams.INIT_SYNC
|
|||||||
if (event.messageContent.contentType != ContentType.SNAP) return@subscribe
|
if (event.messageContent.contentType != ContentType.SNAP) return@subscribe
|
||||||
|
|
||||||
event.addCallbackResult("onSuccess") {
|
event.addCallbackResult("onSuccess") {
|
||||||
event.destinations.conversations.map { it.toString() }.forEach { conversationId ->
|
event.destinations.conversations!!.map { it.toString() }.forEach { conversationId ->
|
||||||
updateJobs[conversationId]?.also { it.cancel() }
|
updateJobs[conversationId]?.also { it.cancel() }
|
||||||
|
|
||||||
updateJobs[conversationId] = (context.coroutineScope.launch {
|
updateJobs[conversationId] = (context.coroutineScope.launch {
|
||||||
|
@ -77,7 +77,7 @@ object MessageDecoder {
|
|||||||
|
|
||||||
fun decode(messageContent: MessageContent): List<DecodedAttachment> {
|
fun decode(messageContent: MessageContent): List<DecodedAttachment> {
|
||||||
return decode(
|
return decode(
|
||||||
ProtoReader(messageContent.content),
|
ProtoReader(messageContent.content!!),
|
||||||
customMediaReferences = getEncodedMediaReferences(gson.toJsonTree(messageContent.instanceNonNull()))
|
customMediaReferences = getEncodedMediaReferences(gson.toJsonTree(messageContent.instanceNonNull()))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -12,10 +12,10 @@ class SnapToChatMedia : Feature("SnapToChatMedia", loadParams = FeatureLoadParam
|
|||||||
if (!context.config.experimental.snapToChatMedia.get()) return
|
if (!context.config.experimental.snapToChatMedia.get()) return
|
||||||
|
|
||||||
context.event.subscribe(BuildMessageEvent::class, priority = 100) { event ->
|
context.event.subscribe(BuildMessageEvent::class, priority = 100) { event ->
|
||||||
if (event.message.messageContent.contentType != ContentType.SNAP) return@subscribe
|
if (event.message.messageContent!!.contentType != ContentType.SNAP) return@subscribe
|
||||||
|
|
||||||
val snapMessageContent = ProtoReader(event.message.messageContent.content).followPath(11)?.getBuffer() ?: return@subscribe
|
val snapMessageContent = ProtoReader(event.message.messageContent!!.content!!).followPath(11)?.getBuffer() ?: return@subscribe
|
||||||
event.message.messageContent.content = ProtoWriter().apply {
|
event.message.messageContent!!.content = ProtoWriter().apply {
|
||||||
from(3) {
|
from(3) {
|
||||||
addBuffer(3, snapMessageContent)
|
addBuffer(3, snapMessageContent)
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ class BypassVideoLengthRestriction :
|
|||||||
})
|
})
|
||||||
|
|
||||||
context.event.subscribe(SendMessageWithContentEvent::class) { event ->
|
context.event.subscribe(SendMessageWithContentEvent::class) { event ->
|
||||||
if (event.destinations.stories.isEmpty()) return@subscribe
|
if (event.destinations.stories!!.isEmpty()) return@subscribe
|
||||||
fileObserver.startWatching()
|
fileObserver.startWatching()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,8 @@ class AutoSave : MessagingRuleFeature("Auto Save", MessagingRuleType.AUTO_SAVE,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun saveMessage(conversationId: SnapUUID, message: Message) {
|
private fun saveMessage(conversationId: SnapUUID, message: Message) {
|
||||||
val messageId = message.messageDescriptor.messageId
|
val messageId = message.messageDescriptor!!.messageId!!
|
||||||
if (messageLogger.takeIf { it.isEnabled }?.isMessageDeleted(conversationId.toString(), message.messageDescriptor.messageId) == true) return
|
if (messageLogger.takeIf { it.isEnabled }?.isMessageDeleted(conversationId.toString(), messageId) == true) return
|
||||||
if (message.messageState != MessageState.COMMITTED) return
|
if (message.messageState != MessageState.COMMITTED) return
|
||||||
|
|
||||||
runCatching {
|
runCatching {
|
||||||
@ -50,8 +50,8 @@ class AutoSave : MessagingRuleFeature("Auto Save", MessagingRuleType.AUTO_SAVE,
|
|||||||
|
|
||||||
private fun canSaveMessage(message: Message): Boolean {
|
private fun canSaveMessage(message: Message): Boolean {
|
||||||
if (context.mainActivity == null || context.isMainActivityPaused) return false
|
if (context.mainActivity == null || context.isMainActivityPaused) return false
|
||||||
if (message.messageMetadata.savedBy.any { uuid -> uuid.toString() == context.database.myUserId }) return false
|
if (message.messageMetadata!!.savedBy!!.any { uuid -> uuid.toString() == context.database.myUserId }) return false
|
||||||
val contentType = message.messageContent.contentType.toString()
|
val contentType = message.messageContent!!.contentType.toString()
|
||||||
|
|
||||||
return autoSaveFilter.any { it == contentType }
|
return autoSaveFilter.any { it == contentType }
|
||||||
}
|
}
|
||||||
@ -96,7 +96,7 @@ class AutoSave : MessagingRuleFeature("Auto Save", MessagingRuleType.AUTO_SAVE,
|
|||||||
{ autoSaveFilter.isNotEmpty() }
|
{ autoSaveFilter.isNotEmpty() }
|
||||||
) { param ->
|
) { param ->
|
||||||
val message = Message(param.arg(0))
|
val message = Message(param.arg(0))
|
||||||
val conversationId = message.messageDescriptor.conversationId
|
val conversationId = message.messageDescriptor!!.conversationId!!
|
||||||
if (!canSaveInConversation(conversationId.toString())) return@hook
|
if (!canSaveInConversation(conversationId.toString())) return@hook
|
||||||
if (!canSaveMessage(message)) return@hook
|
if (!canSaveMessage(message)) return@hook
|
||||||
|
|
||||||
|
@ -68,11 +68,11 @@ class InstantDelete : Feature("InstantDelete", loadParams = FeatureLoadParams.AC
|
|||||||
|
|
||||||
if (chatActionMenuOptions["chat_action_menu_erase_quote"] == menuOptionText.text) {
|
if (chatActionMenuOptions["chat_action_menu_erase_quote"] == menuOptionText.text) {
|
||||||
conversationManager.fetchMessage(conversationId, messageId.toLong(), onSuccess = { message ->
|
conversationManager.fetchMessage(conversationId, messageId.toLong(), onSuccess = { message ->
|
||||||
val quotedMessage = message.messageContent.quotedMessage.takeIf { it.isPresent() }!!
|
val quotedMessage = message.messageContent!!.quotedMessage!!.takeIf { it.isPresent() }!!
|
||||||
|
|
||||||
conversationManager.updateMessage(
|
conversationManager.updateMessage(
|
||||||
conversationId,
|
conversationId,
|
||||||
quotedMessage.content.messageId,
|
quotedMessage.content!!.messageId!!,
|
||||||
MessageUpdate.ERASE,
|
MessageUpdate.ERASE,
|
||||||
onResult = onCallbackResult
|
onResult = onCallbackResult
|
||||||
)
|
)
|
||||||
|
@ -27,7 +27,6 @@ import me.rhunk.snapenhance.core.features.FeatureLoadParams
|
|||||||
import me.rhunk.snapenhance.core.features.impl.downloader.MediaDownloader
|
import me.rhunk.snapenhance.core.features.impl.downloader.MediaDownloader
|
||||||
import me.rhunk.snapenhance.core.features.impl.downloader.decoder.MessageDecoder
|
import me.rhunk.snapenhance.core.features.impl.downloader.decoder.MessageDecoder
|
||||||
import me.rhunk.snapenhance.core.features.impl.spying.StealthMode
|
import me.rhunk.snapenhance.core.features.impl.spying.StealthMode
|
||||||
import me.rhunk.snapenhance.core.util.CallbackBuilder
|
|
||||||
import me.rhunk.snapenhance.core.util.hook.HookStage
|
import me.rhunk.snapenhance.core.util.hook.HookStage
|
||||||
import me.rhunk.snapenhance.core.util.hook.hook
|
import me.rhunk.snapenhance.core.util.hook.hook
|
||||||
import me.rhunk.snapenhance.core.util.ktx.setObjectField
|
import me.rhunk.snapenhance.core.util.ktx.setObjectField
|
||||||
@ -264,14 +263,14 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val contentType = snapMessage.messageContent.contentType ?: return@onEach
|
val contentType = snapMessage.messageContent!!.contentType ?: return@onEach
|
||||||
val contentData = snapMessage.messageContent.content
|
val contentData = snapMessage.messageContent!!.content!!
|
||||||
|
|
||||||
val formatUsername: (String) -> String = { "$senderUsername: $it" }
|
val formatUsername: (String) -> String = { "$senderUsername: $it" }
|
||||||
val notificationCache = cachedMessages.let { it.computeIfAbsent(conversationId) { mutableListOf() } }
|
val notificationCache = cachedMessages.let { it.computeIfAbsent(conversationId) { mutableListOf() } }
|
||||||
val appendNotifications: () -> Unit = { setNotificationText(notificationData.notification, conversationId)}
|
val appendNotifications: () -> Unit = { setNotificationText(notificationData.notification, conversationId)}
|
||||||
|
|
||||||
setupNotificationActionButtons(contentType, conversationId, snapMessage.messageDescriptor.messageId, notificationData)
|
setupNotificationActionButtons(contentType, conversationId, snapMessage.messageDescriptor!!.messageId!!, notificationData)
|
||||||
|
|
||||||
when (contentType) {
|
when (contentType) {
|
||||||
ContentType.NOTE -> {
|
ContentType.NOTE -> {
|
||||||
@ -286,14 +285,14 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
|
|||||||
}
|
}
|
||||||
ContentType.SNAP, ContentType.EXTERNAL_MEDIA -> {
|
ContentType.SNAP, ContentType.EXTERNAL_MEDIA -> {
|
||||||
val mediaReferences = MessageDecoder.getMediaReferences(
|
val mediaReferences = MessageDecoder.getMediaReferences(
|
||||||
messageContent = context.gson.toJsonTree(snapMessage.messageContent.instanceNonNull())
|
messageContent = context.gson.toJsonTree(snapMessage.messageContent!!.instanceNonNull())
|
||||||
)
|
)
|
||||||
|
|
||||||
val mediaReferenceKeys = mediaReferences.map { reference ->
|
val mediaReferenceKeys = mediaReferences.map { reference ->
|
||||||
reference.asJsonObject.getAsJsonArray("mContentObject").map { it.asByte }.toByteArray()
|
reference.asJsonObject.getAsJsonArray("mContentObject").map { it.asByte }.toByteArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageDecoder.decode(snapMessage.messageContent).firstOrNull()?.also { media ->
|
MessageDecoder.decode(snapMessage.messageContent!!).firstOrNull()?.also { media ->
|
||||||
val mediaType = MediaReferenceType.valueOf(mediaReferences.first().asJsonObject["mMediaType"].asString)
|
val mediaType = MediaReferenceType.valueOf(mediaReferences.first().asJsonObject["mMediaType"].asString)
|
||||||
|
|
||||||
runCatching {
|
runCatching {
|
||||||
|
@ -58,7 +58,7 @@ class SendOverride : Feature("Send Override", loadParams = FeatureLoadParams.INI
|
|||||||
if (localMessageContent.contentType != ContentType.EXTERNAL_MEDIA) return@subscribe
|
if (localMessageContent.contentType != ContentType.EXTERNAL_MEDIA) return@subscribe
|
||||||
|
|
||||||
//prevent story replies
|
//prevent story replies
|
||||||
val messageProtoReader = ProtoReader(localMessageContent.content)
|
val messageProtoReader = ProtoReader(localMessageContent.content!!)
|
||||||
if (messageProtoReader.contains(7)) return@subscribe
|
if (messageProtoReader.contains(7)) return@subscribe
|
||||||
|
|
||||||
event.canceled = true
|
event.canceled = true
|
||||||
|
@ -15,13 +15,13 @@ class UnlimitedSnapViewTime :
|
|||||||
|
|
||||||
context.event.subscribe(BuildMessageEvent::class, { state }, priority = 101) { event ->
|
context.event.subscribe(BuildMessageEvent::class, { state }, priority = 101) { event ->
|
||||||
if (event.message.messageState != MessageState.COMMITTED) return@subscribe
|
if (event.message.messageState != MessageState.COMMITTED) return@subscribe
|
||||||
if (event.message.messageContent.contentType != ContentType.SNAP) return@subscribe
|
if (event.message.messageContent!!.contentType != ContentType.SNAP) return@subscribe
|
||||||
|
|
||||||
val messageContent = event.message.messageContent
|
val messageContent = event.message.messageContent
|
||||||
|
|
||||||
val mediaAttributes = ProtoReader(messageContent.content).followPath(11, 5, 2) ?: return@subscribe
|
val mediaAttributes = ProtoReader(messageContent!!.content!!).followPath(11, 5, 2) ?: return@subscribe
|
||||||
if (mediaAttributes.contains(6)) return@subscribe
|
if (mediaAttributes.contains(6)) return@subscribe
|
||||||
messageContent.content = ProtoEditor(messageContent.content).apply {
|
messageContent.content = ProtoEditor(messageContent.content!!).apply {
|
||||||
edit(11, 5, 2) {
|
edit(11, 5, 2) {
|
||||||
remove(8)
|
remove(8)
|
||||||
addBuffer(6, byteArrayOf())
|
addBuffer(6, byteArrayOf())
|
||||||
|
@ -101,14 +101,14 @@ class MessageLogger : Feature("MessageLogger",
|
|||||||
val messageInstance = event.message.instanceNonNull()
|
val messageInstance = event.message.instanceNonNull()
|
||||||
if (event.message.messageState != MessageState.COMMITTED) return@subscribe
|
if (event.message.messageState != MessageState.COMMITTED) return@subscribe
|
||||||
|
|
||||||
cachedIdLinks[event.message.messageDescriptor.messageId] = event.message.orderKey
|
cachedIdLinks[event.message.messageDescriptor!!.messageId!!] = event.message.orderKey!!
|
||||||
val conversationId = event.message.messageDescriptor.conversationId.toString()
|
val conversationId = event.message.messageDescriptor!!.conversationId.toString()
|
||||||
//exclude messages sent by me
|
//exclude messages sent by me
|
||||||
if (event.message.senderId.toString() == context.database.myUserId) return@subscribe
|
if (event.message.senderId.toString() == context.database.myUserId) return@subscribe
|
||||||
|
|
||||||
val uniqueMessageIdentifier = computeMessageIdentifier(conversationId, event.message.orderKey)
|
val uniqueMessageIdentifier = computeMessageIdentifier(conversationId, event.message.orderKey!!)
|
||||||
|
|
||||||
if (event.message.messageContent.contentType != ContentType.STATUS) {
|
if (event.message.messageContent!!.contentType != ContentType.STATUS) {
|
||||||
if (fetchedMessages.contains(uniqueMessageIdentifier)) return@subscribe
|
if (fetchedMessages.contains(uniqueMessageIdentifier)) return@subscribe
|
||||||
fetchedMessages.add(uniqueMessageIdentifier)
|
fetchedMessages.add(uniqueMessageIdentifier)
|
||||||
|
|
||||||
|
@ -13,13 +13,13 @@ import me.rhunk.snapenhance.core.features.impl.messaging.Messaging
|
|||||||
|
|
||||||
fun me.rhunk.snapenhance.core.wrapper.impl.Message.toBridge(): Message {
|
fun me.rhunk.snapenhance.core.wrapper.impl.Message.toBridge(): Message {
|
||||||
return Message().also { output ->
|
return Message().also { output ->
|
||||||
output.conversationId = this.messageDescriptor.conversationId.toString()
|
output.conversationId = this.messageDescriptor!!.conversationId.toString()
|
||||||
output.senderId = this.senderId.toString()
|
output.senderId = this.senderId.toString()
|
||||||
output.clientMessageId = this.messageDescriptor.messageId
|
output.clientMessageId = this.messageDescriptor!!.messageId!!
|
||||||
output.serverMessageId = this.orderKey
|
output.serverMessageId = this.orderKey!!
|
||||||
output.contentType = this.messageContent.contentType?.id ?: -1
|
output.contentType = this.messageContent?.contentType?.id ?: -1
|
||||||
output.content = this.messageContent.content
|
output.content = this.messageContent?.content
|
||||||
output.mediaReferences = MessageDecoder.getEncodedMediaReferences(this.messageContent)
|
output.mediaReferences = MessageDecoder.getEncodedMediaReferences(this.messageContent!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,8 +74,8 @@ class MessageExporter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun serializeMessageContent(message: Message): String? {
|
private fun serializeMessageContent(message: Message): String? {
|
||||||
return if (message.messageContent.contentType == ContentType.CHAT) {
|
return if (message.messageContent!!.contentType == ContentType.CHAT) {
|
||||||
ProtoReader(message.messageContent.content).getString(2, 1) ?: "Failed to parse message"
|
ProtoReader(message.messageContent!!.content!!).getString(2, 1) ?: "Failed to parse message"
|
||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,8 +93,8 @@ class MessageExporter(
|
|||||||
val sender = conversationParticipants[message.senderId.toString()]
|
val sender = conversationParticipants[message.senderId.toString()]
|
||||||
val senderUsername = sender?.usernameForSorting ?: message.senderId.toString()
|
val senderUsername = sender?.usernameForSorting ?: message.senderId.toString()
|
||||||
val senderDisplayName = sender?.displayName ?: message.senderId.toString()
|
val senderDisplayName = sender?.displayName ?: message.senderId.toString()
|
||||||
val messageContent = serializeMessageContent(message) ?: message.messageContent.contentType?.name
|
val messageContent = serializeMessageContent(message) ?: message.messageContent!!.contentType?.name
|
||||||
val date = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH).format(Date(message.messageMetadata.createdAt))
|
val date = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH).format(Date(message.messageMetadata!!.createdAt!!))
|
||||||
writer.write("[$date] - $senderDisplayName (${senderUsername}): $messageContent\n")
|
writer.write("[$date] - $senderDisplayName (${senderUsername}): $messageContent\n")
|
||||||
}
|
}
|
||||||
writer.flush()
|
writer.flush()
|
||||||
@ -110,17 +110,17 @@ class MessageExporter(
|
|||||||
|
|
||||||
fun updateProgress(type: String) {
|
fun updateProgress(type: String) {
|
||||||
val total = messages.filter {
|
val total = messages.filter {
|
||||||
mediaToDownload?.contains(it.messageContent.contentType) ?: false
|
mediaToDownload?.contains(it.messageContent!!.contentType) ?: false
|
||||||
}.size
|
}.size
|
||||||
processCount++
|
processCount++
|
||||||
printLog("$type $processCount/$total")
|
printLog("$type $processCount/$total")
|
||||||
}
|
}
|
||||||
|
|
||||||
messages.filter {
|
messages.filter {
|
||||||
mediaToDownload?.contains(it.messageContent.contentType) ?: false
|
mediaToDownload?.contains(it.messageContent!!.contentType) ?: false
|
||||||
}.forEach { message ->
|
}.forEach { message ->
|
||||||
threadPool.execute {
|
threadPool.execute {
|
||||||
MessageDecoder.decode(message.messageContent).forEach decode@{ attachment ->
|
MessageDecoder.decode(message.messageContent!!).forEach decode@{ attachment ->
|
||||||
val protoMediaReference = Base64.UrlSafe.decode(attachment.mediaUrlKey ?: return@decode)
|
val protoMediaReference = Base64.UrlSafe.decode(attachment.mediaUrlKey ?: return@decode)
|
||||||
|
|
||||||
runCatching {
|
runCatching {
|
||||||
@ -145,8 +145,8 @@ class MessageExporter(
|
|||||||
|
|
||||||
updateProgress("downloaded")
|
updateProgress("downloaded")
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
printLog("failed to download media for ${message.messageDescriptor.conversationId}_${message.orderKey}")
|
printLog("failed to download media for ${message.messageDescriptor!!.conversationId}_${message.orderKey}")
|
||||||
context.log.error("failed to download media for ${message.messageDescriptor.conversationId}_${message.orderKey}", it)
|
context.log.error("failed to download media for ${message.messageDescriptor!!.conversationId}_${message.orderKey}", it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -270,7 +270,7 @@ class MessageExporter(
|
|||||||
add(JsonObject().apply {
|
add(JsonObject().apply {
|
||||||
addProperty("orderKey", message.orderKey)
|
addProperty("orderKey", message.orderKey)
|
||||||
addProperty("senderId", participants.getOrDefault(message.senderId.toString(), -1))
|
addProperty("senderId", participants.getOrDefault(message.senderId.toString(), -1))
|
||||||
addProperty("type", message.messageContent.contentType.toString())
|
addProperty("type", message.messageContent!!.contentType.toString())
|
||||||
|
|
||||||
fun addUUIDList(name: String, list: List<SnapUUID>) {
|
fun addUUIDList(name: String, list: List<SnapUUID>) {
|
||||||
add(name, JsonArray().apply {
|
add(name, JsonArray().apply {
|
||||||
@ -278,12 +278,12 @@ class MessageExporter(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
addUUIDList("savedBy", message.messageMetadata.savedBy)
|
addUUIDList("savedBy", message.messageMetadata!!.savedBy!!)
|
||||||
addUUIDList("seenBy", message.messageMetadata.seenBy)
|
addUUIDList("seenBy", message.messageMetadata!!.seenBy!!)
|
||||||
addUUIDList("openedBy", message.messageMetadata.openedBy)
|
addUUIDList("openedBy", message.messageMetadata!!.openedBy!!)
|
||||||
|
|
||||||
add("reactions", JsonObject().apply {
|
add("reactions", JsonObject().apply {
|
||||||
message.messageMetadata.reactions.forEach { reaction ->
|
message.messageMetadata!!.reactions!!.forEach { reaction ->
|
||||||
addProperty(
|
addProperty(
|
||||||
participants.getOrDefault(reaction.userId.toString(), -1L).toString(),
|
participants.getOrDefault(reaction.userId.toString(), -1L).toString(),
|
||||||
reaction.reactionId
|
reaction.reactionId
|
||||||
@ -291,13 +291,13 @@ class MessageExporter(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
addProperty("createdTimestamp", message.messageMetadata.createdAt)
|
addProperty("createdTimestamp", message.messageMetadata!!.createdAt)
|
||||||
addProperty("readTimestamp", message.messageMetadata.readAt)
|
addProperty("readTimestamp", message.messageMetadata!!.readAt)
|
||||||
addProperty("serializedContent", serializeMessageContent(message))
|
addProperty("serializedContent", serializeMessageContent(message))
|
||||||
addProperty("rawContent", Base64.UrlSafe.encode(message.messageContent.content))
|
addProperty("rawContent", Base64.UrlSafe.encode(message.messageContent!!.content!!))
|
||||||
|
|
||||||
add("attachments", JsonArray().apply {
|
add("attachments", JsonArray().apply {
|
||||||
MessageDecoder.decode(message.messageContent)
|
MessageDecoder.decode(message.messageContent!!)
|
||||||
.forEach attachments@{ attachments ->
|
.forEach attachments@{ attachments ->
|
||||||
if (attachments.type == AttachmentType.STICKER) //TODO: implement stickers
|
if (attachments.type == AttachmentType.STICKER) //TODO: implement stickers
|
||||||
return@attachments
|
return@attachments
|
||||||
|
@ -94,12 +94,12 @@ class MessageSender(
|
|||||||
|
|
||||||
val localMessageContent = context.gson.fromJson(localMessageContentTemplate, context.classCache.localMessageContent)
|
val localMessageContent = context.gson.fromJson(localMessageContentTemplate, context.classCache.localMessageContent)
|
||||||
val messageDestinations = MessageDestinations(AbstractWrapper.newEmptyInstance(context.classCache.messageDestinations)).also {
|
val messageDestinations = MessageDestinations(AbstractWrapper.newEmptyInstance(context.classCache.messageDestinations)).also {
|
||||||
it.conversations = conversations
|
it.conversations = conversations.toCollection(ArrayList())
|
||||||
it.mPhoneNumbers = arrayListOf()
|
it.mPhoneNumbers = arrayListOf<Any>()
|
||||||
it.stories = arrayListOf()
|
it.stories = arrayListOf<Any>()
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessageWithContentMethod.invoke(context.feature(Messaging::class).conversationManager, messageDestinations.instanceNonNull(), localMessageContent, callback)
|
sendMessageWithContentMethod.invoke(context.feature(Messaging::class).conversationManager?.instanceNonNull(), messageDestinations.instanceNonNull(), localMessageContent, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendChatMessage(conversations: List<SnapUUID>, message: String, onError: (Any) -> Unit = {}, onSuccess: () -> Unit = {}) {
|
fun sendChatMessage(conversations: List<SnapUUID>, message: String, onError: (Any) -> Unit = {}, onSuccess: () -> Unit = {}) {
|
||||||
|
@ -2,24 +2,47 @@ package me.rhunk.snapenhance.core.wrapper
|
|||||||
|
|
||||||
import de.robv.android.xposed.XposedHelpers
|
import de.robv.android.xposed.XposedHelpers
|
||||||
import me.rhunk.snapenhance.core.util.CallbackBuilder
|
import me.rhunk.snapenhance.core.util.CallbackBuilder
|
||||||
|
import me.rhunk.snapenhance.core.wrapper.impl.SnapUUID
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
abstract class AbstractWrapper(
|
abstract class AbstractWrapper(
|
||||||
protected var instance: Any?
|
protected var instance: Any?
|
||||||
) {
|
) {
|
||||||
|
protected val uuidArrayListMapper: (Any?) -> ArrayList<SnapUUID> get() = { (it as ArrayList<*>).map { i -> SnapUUID(i) }.toCollection(ArrayList()) }
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
inner class EnumAccessor<T>(private val fieldName: String, private val defaultValue: T) {
|
inner class EnumAccessor<T>(private val fieldName: String, private val defaultValue: T) {
|
||||||
operator fun getValue(obj: Any, property: KProperty<*>): T? = getEnumValue(fieldName, defaultValue as Enum<*>) as? T
|
operator fun getValue(obj: Any, property: KProperty<*>): T? = getEnumValue(fieldName, defaultValue as Enum<*>) as? T
|
||||||
operator fun setValue(obj: Any, property: KProperty<*>, value: Any?) = setEnumValue(fieldName, value as Enum<*>)
|
operator fun setValue(obj: Any, property: KProperty<*>, value: Any?) = setEnumValue(fieldName, value as Enum<*>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inner class FieldAccessor<T>(private val fieldName: String, private val mapper: ((Any?) -> T?)? = null) {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
operator fun getValue(obj: Any, property: KProperty<*>): T? {
|
||||||
|
val value = XposedHelpers.getObjectField(instance, fieldName)
|
||||||
|
return if (mapper != null) {
|
||||||
|
mapper.invoke(value)
|
||||||
|
} else {
|
||||||
|
value as? T
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun setValue(obj: Any, property: KProperty<*>, value: Any?) {
|
||||||
|
XposedHelpers.setObjectField(instance, fieldName, when (value) {
|
||||||
|
is AbstractWrapper -> value.instance
|
||||||
|
is ArrayList<*> -> value.map { if (it is AbstractWrapper) it.instance else it }.toMutableList()
|
||||||
|
else -> value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun newEmptyInstance(clazz: Class<*>): Any {
|
fun newEmptyInstance(clazz: Class<*>): Any {
|
||||||
return CallbackBuilder.createEmptyObject(clazz.constructors[0]) ?: throw NullPointerException()
|
return CallbackBuilder.createEmptyObject(clazz.constructors[0]) ?: throw NullPointerException()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun instanceNonNull(): Any = instance!!
|
fun instanceNonNull(): Any = instance ?: throw NullPointerException("Instance of ${this::class.simpleName} is null")
|
||||||
fun isPresent(): Boolean = instance != null
|
fun isPresent(): Boolean = instance != null
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
@ -31,6 +54,7 @@ abstract class AbstractWrapper(
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected fun <T> enum(fieldName: String, defaultValue: T) = EnumAccessor(fieldName, defaultValue)
|
protected fun <T> enum(fieldName: String, defaultValue: T) = EnumAccessor(fieldName, defaultValue)
|
||||||
|
protected fun <T> field(fieldName: String, mapper: ((Any?) -> T?)? = null) = FieldAccessor(fieldName, mapper)
|
||||||
|
|
||||||
fun <T : Enum<*>> getEnumValue(fieldName: String, defaultValue: T?): T? {
|
fun <T : Enum<*>> getEnumValue(fieldName: String, defaultValue: T?): T? {
|
||||||
if (defaultValue == null) return null
|
if (defaultValue == null) return null
|
||||||
|
@ -7,7 +7,10 @@ import me.rhunk.snapenhance.core.wrapper.AbstractWrapper
|
|||||||
|
|
||||||
typealias CallbackResult = (error: String?) -> Unit
|
typealias CallbackResult = (error: String?) -> Unit
|
||||||
|
|
||||||
class ConversationManager(val context: ModContext, obj: Any) : AbstractWrapper(obj) {
|
class ConversationManager(
|
||||||
|
val context: ModContext,
|
||||||
|
obj: Any
|
||||||
|
) : AbstractWrapper(obj) {
|
||||||
private fun findMethodByName(name: String) = context.classCache.conversationManager.declaredMethods.find { it.name == name } ?: throw RuntimeException("Could not find method $name")
|
private fun findMethodByName(name: String) = context.classCache.conversationManager.declaredMethods.find { it.name == name } ?: throw RuntimeException("Could not find method $name")
|
||||||
|
|
||||||
private val updateMessageMethod by lazy { findMethodByName("updateMessage") }
|
private val updateMessageMethod by lazy { findMethodByName("updateMessage") }
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
package me.rhunk.snapenhance.core.wrapper.impl
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import android.util.AttributeSet
|
|
||||||
import android.view.View
|
|
||||||
import me.rhunk.snapenhance.core.SnapEnhance
|
|
||||||
import me.rhunk.snapenhance.core.wrapper.AbstractWrapper
|
|
||||||
|
|
||||||
class FriendActionButton(
|
|
||||||
obj: View
|
|
||||||
) : AbstractWrapper(obj) {
|
|
||||||
private val iconDrawableContainer by lazy {
|
|
||||||
instanceNonNull().javaClass.declaredFields.first { it.type != Int::class.javaPrimitiveType }[instanceNonNull()]
|
|
||||||
}
|
|
||||||
|
|
||||||
private val setIconDrawableMethod by lazy {
|
|
||||||
iconDrawableContainer.javaClass.declaredMethods.first {
|
|
||||||
it.parameterTypes.size == 1 &&
|
|
||||||
it.parameterTypes[0] == Drawable::class.java &&
|
|
||||||
it.name != "invalidateDrawable" &&
|
|
||||||
it.returnType == Void::class.javaPrimitiveType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setIconDrawable(drawable: Drawable) {
|
|
||||||
setIconDrawableMethod.invoke(iconDrawableContainer, drawable)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun new(context: Context): FriendActionButton {
|
|
||||||
val instance = SnapEnhance.classLoader.loadClass("com.snap.profile.shared.view.FriendActionButton")
|
|
||||||
.getConstructor(Context::class.java, AttributeSet::class.java)
|
|
||||||
.newInstance(context, null) as View
|
|
||||||
return FriendActionButton(instance)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +1,21 @@
|
|||||||
package me.rhunk.snapenhance.core.wrapper.impl
|
package me.rhunk.snapenhance.core.wrapper.impl
|
||||||
|
|
||||||
import me.rhunk.snapenhance.common.data.MessageState
|
import me.rhunk.snapenhance.common.data.MessageState
|
||||||
import me.rhunk.snapenhance.core.util.ktx.getObjectField
|
|
||||||
import me.rhunk.snapenhance.core.wrapper.AbstractWrapper
|
import me.rhunk.snapenhance.core.wrapper.AbstractWrapper
|
||||||
|
import org.mozilla.javascript.annotations.JSGetter
|
||||||
|
import org.mozilla.javascript.annotations.JSSetter
|
||||||
|
|
||||||
class Message(obj: Any?) : AbstractWrapper(obj) {
|
class Message(obj: Any?) : AbstractWrapper(obj) {
|
||||||
val orderKey get() = instanceNonNull().getObjectField("mOrderKey") as Long
|
@get:JSGetter @set:JSSetter
|
||||||
val senderId get() = SnapUUID(instanceNonNull().getObjectField("mSenderId"))
|
var orderKey by field<Long>("mOrderKey")
|
||||||
val messageContent get() = MessageContent(instanceNonNull().getObjectField("mMessageContent"))
|
@get:JSGetter @set:JSSetter
|
||||||
val messageDescriptor get() = MessageDescriptor(instanceNonNull().getObjectField("mDescriptor"))
|
var senderId by field("mSenderId") { SnapUUID(it) }
|
||||||
val messageMetadata get() = MessageMetadata(instanceNonNull().getObjectField("mMetadata"))
|
@get:JSGetter @set:JSSetter
|
||||||
|
var messageContent by field("mMessageContent") { MessageContent(it) }
|
||||||
|
@get:JSGetter @set:JSSetter
|
||||||
|
var messageDescriptor by field("mDescriptor") { MessageDescriptor(it) }
|
||||||
|
@get:JSGetter @set:JSSetter
|
||||||
|
var messageMetadata by field("mMetadata") { MessageMetadata(it) }
|
||||||
|
@get:JSGetter @set:JSSetter
|
||||||
var messageState by enum("mState", MessageState.COMMITTED)
|
var messageState by enum("mState", MessageState.COMMITTED)
|
||||||
}
|
}
|
@ -1,15 +1,15 @@
|
|||||||
package me.rhunk.snapenhance.core.wrapper.impl
|
package me.rhunk.snapenhance.core.wrapper.impl
|
||||||
|
|
||||||
import me.rhunk.snapenhance.common.data.ContentType
|
import me.rhunk.snapenhance.common.data.ContentType
|
||||||
import me.rhunk.snapenhance.core.util.ktx.getObjectField
|
|
||||||
import me.rhunk.snapenhance.core.util.ktx.setObjectField
|
|
||||||
import me.rhunk.snapenhance.core.wrapper.AbstractWrapper
|
import me.rhunk.snapenhance.core.wrapper.AbstractWrapper
|
||||||
|
import org.mozilla.javascript.annotations.JSGetter
|
||||||
|
import org.mozilla.javascript.annotations.JSSetter
|
||||||
|
|
||||||
class MessageContent(obj: Any?) : AbstractWrapper(obj) {
|
class MessageContent(obj: Any?) : AbstractWrapper(obj) {
|
||||||
var content
|
@get:JSGetter @set:JSSetter
|
||||||
get() = instanceNonNull().getObjectField("mContent") as ByteArray
|
var content by field<ByteArray>("mContent")
|
||||||
set(value) = instanceNonNull().setObjectField("mContent", value)
|
@get:JSGetter @set:JSSetter
|
||||||
val quotedMessage
|
var quotedMessage by field("mQuotedMessage") { QuotedMessage(it) }
|
||||||
get() = QuotedMessage(instanceNonNull().getObjectField("mQuotedMessage"))
|
@get:JSGetter @set:JSSetter
|
||||||
var contentType by enum("mContentType", ContentType.UNKNOWN)
|
var contentType by enum("mContentType", ContentType.UNKNOWN)
|
||||||
}
|
}
|
@ -1,9 +1,11 @@
|
|||||||
package me.rhunk.snapenhance.core.wrapper.impl
|
package me.rhunk.snapenhance.core.wrapper.impl
|
||||||
|
|
||||||
import me.rhunk.snapenhance.core.util.ktx.getObjectField
|
|
||||||
import me.rhunk.snapenhance.core.wrapper.AbstractWrapper
|
import me.rhunk.snapenhance.core.wrapper.AbstractWrapper
|
||||||
|
import org.mozilla.javascript.annotations.JSGetter
|
||||||
|
import org.mozilla.javascript.annotations.JSSetter
|
||||||
|
|
||||||
class MessageDescriptor(obj: Any?) : AbstractWrapper(obj) {
|
class MessageDescriptor(obj: Any?) : AbstractWrapper(obj) {
|
||||||
val messageId: Long get() = instanceNonNull().getObjectField("mMessageId") as Long
|
@get:JSGetter @set:JSSetter
|
||||||
val conversationId: SnapUUID get() = SnapUUID(instanceNonNull().getObjectField("mConversationId")!!)
|
var messageId by field<Long>("mMessageId")
|
||||||
|
val conversationId by field("mConversationId") { SnapUUID(it) }
|
||||||
}
|
}
|
@ -1,15 +1,10 @@
|
|||||||
package me.rhunk.snapenhance.core.wrapper.impl
|
package me.rhunk.snapenhance.core.wrapper.impl
|
||||||
|
|
||||||
import me.rhunk.snapenhance.core.util.ktx.getObjectField
|
|
||||||
import me.rhunk.snapenhance.core.util.ktx.setObjectField
|
|
||||||
import me.rhunk.snapenhance.core.wrapper.AbstractWrapper
|
import me.rhunk.snapenhance.core.wrapper.AbstractWrapper
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
class MessageDestinations(obj: Any) : AbstractWrapper(obj){
|
class MessageDestinations(obj: Any) : AbstractWrapper(obj){
|
||||||
var conversations get() = (instanceNonNull().getObjectField("mConversations") as ArrayList<*>).map { SnapUUID(it) }
|
var conversations by field("mConversations", uuidArrayListMapper)
|
||||||
set(value) = instanceNonNull().setObjectField("mConversations", value.map { it.instanceNonNull() }.toCollection(ArrayList()))
|
var stories by field<ArrayList<*>>("mStories")
|
||||||
var stories get() = instanceNonNull().getObjectField("mStories") as ArrayList<Any>
|
var mPhoneNumbers by field<ArrayList<*>>("mPhoneNumbers")
|
||||||
set(value) = instanceNonNull().setObjectField("mStories", value)
|
|
||||||
var mPhoneNumbers get() = instanceNonNull().getObjectField("mPhoneNumbers") as ArrayList<Any>
|
|
||||||
set(value) = instanceNonNull().setObjectField("mPhoneNumbers", value)
|
|
||||||
}
|
}
|
@ -1,28 +1,26 @@
|
|||||||
package me.rhunk.snapenhance.core.wrapper.impl
|
package me.rhunk.snapenhance.core.wrapper.impl
|
||||||
|
|
||||||
import me.rhunk.snapenhance.common.data.PlayableSnapState
|
import me.rhunk.snapenhance.common.data.PlayableSnapState
|
||||||
import me.rhunk.snapenhance.core.util.ktx.getObjectField
|
|
||||||
import me.rhunk.snapenhance.core.wrapper.AbstractWrapper
|
import me.rhunk.snapenhance.core.wrapper.AbstractWrapper
|
||||||
|
import org.mozilla.javascript.annotations.JSGetter
|
||||||
|
import org.mozilla.javascript.annotations.JSSetter
|
||||||
|
|
||||||
class MessageMetadata(obj: Any?) : AbstractWrapper(obj){
|
class MessageMetadata(obj: Any?) : AbstractWrapper(obj){
|
||||||
val createdAt: Long get() = instanceNonNull().getObjectField("mCreatedAt") as Long
|
@get:JSGetter @set:JSSetter
|
||||||
val readAt: Long get() = instanceNonNull().getObjectField("mReadAt") as Long
|
var createdAt by field<Long>("mCreatedAt")
|
||||||
|
@get:JSGetter @set:JSSetter
|
||||||
|
var readAt by field<Long>("mReadAt")
|
||||||
|
@get:JSGetter @set:JSSetter
|
||||||
var playableSnapState by enum("mPlayableSnapState", PlayableSnapState.PLAYABLE)
|
var playableSnapState by enum("mPlayableSnapState", PlayableSnapState.PLAYABLE)
|
||||||
|
|
||||||
private fun getUUIDList(name: String): List<SnapUUID> {
|
@get:JSGetter @set:JSSetter
|
||||||
return (instanceNonNull().getObjectField(name) as List<*>).map { SnapUUID(it!!) }
|
var savedBy by field("mSavedBy", uuidArrayListMapper)
|
||||||
}
|
@get:JSGetter @set:JSSetter
|
||||||
|
var openedBy by field("mOpenedBy", uuidArrayListMapper)
|
||||||
val savedBy: List<SnapUUID> by lazy {
|
@get:JSGetter @set:JSSetter
|
||||||
getUUIDList("mSavedBy")
|
var seenBy by field("mSeenBy", uuidArrayListMapper)
|
||||||
}
|
@get:JSGetter @set:JSSetter
|
||||||
val openedBy: List<SnapUUID> by lazy {
|
var reactions by field("mReactions") {
|
||||||
getUUIDList("mOpenedBy")
|
(it as ArrayList<*>).map { i -> UserIdToReaction(i) }.toMutableList()
|
||||||
}
|
|
||||||
val seenBy: List<SnapUUID> by lazy {
|
|
||||||
getUUIDList("mSeenBy")
|
|
||||||
}
|
|
||||||
val reactions: List<UserIdToReaction> by lazy {
|
|
||||||
(instanceNonNull().getObjectField("mReactions") as List<*>).map { UserIdToReaction(it!!) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,8 +1,10 @@
|
|||||||
package me.rhunk.snapenhance.core.wrapper.impl
|
package me.rhunk.snapenhance.core.wrapper.impl
|
||||||
|
|
||||||
import me.rhunk.snapenhance.core.util.ktx.getObjectField
|
|
||||||
import me.rhunk.snapenhance.core.wrapper.AbstractWrapper
|
import me.rhunk.snapenhance.core.wrapper.AbstractWrapper
|
||||||
|
import org.mozilla.javascript.annotations.JSGetter
|
||||||
|
import org.mozilla.javascript.annotations.JSSetter
|
||||||
|
|
||||||
class QuotedMessage(obj: Any?) : AbstractWrapper(obj) {
|
class QuotedMessage(obj: Any?) : AbstractWrapper(obj) {
|
||||||
val content get() = QuotedMessageContent(instanceNonNull().getObjectField("mContent"))
|
@get:JSGetter @set:JSSetter
|
||||||
|
var content by field("mContent") { QuotedMessageContent(it) }
|
||||||
}
|
}
|
@ -1,10 +1,10 @@
|
|||||||
package me.rhunk.snapenhance.core.wrapper.impl
|
package me.rhunk.snapenhance.core.wrapper.impl
|
||||||
|
|
||||||
import me.rhunk.snapenhance.core.util.ktx.getObjectField
|
|
||||||
import me.rhunk.snapenhance.core.util.ktx.setObjectField
|
|
||||||
import me.rhunk.snapenhance.core.wrapper.AbstractWrapper
|
import me.rhunk.snapenhance.core.wrapper.AbstractWrapper
|
||||||
|
import org.mozilla.javascript.annotations.JSGetter
|
||||||
|
import org.mozilla.javascript.annotations.JSSetter
|
||||||
|
|
||||||
class QuotedMessageContent(obj: Any?) : AbstractWrapper(obj) {
|
class QuotedMessageContent(obj: Any?) : AbstractWrapper(obj) {
|
||||||
var messageId get() = instanceNonNull().getObjectField("mMessageId") as Long
|
@get:JSGetter @set:JSSetter
|
||||||
set(value) = instanceNonNull().setObjectField("mMessageId", value)
|
var messageId by field<Long>("mMessageId")
|
||||||
}
|
}
|
@ -1,11 +1,21 @@
|
|||||||
package me.rhunk.snapenhance.core.wrapper.impl
|
package me.rhunk.snapenhance.core.wrapper.impl
|
||||||
|
|
||||||
import me.rhunk.snapenhance.core.util.ktx.getObjectField
|
import me.rhunk.snapenhance.core.util.ktx.getObjectField
|
||||||
|
import me.rhunk.snapenhance.core.util.ktx.setObjectField
|
||||||
import me.rhunk.snapenhance.core.wrapper.AbstractWrapper
|
import me.rhunk.snapenhance.core.wrapper.AbstractWrapper
|
||||||
|
import org.mozilla.javascript.annotations.JSGetter
|
||||||
|
import org.mozilla.javascript.annotations.JSSetter
|
||||||
|
|
||||||
class UserIdToReaction(obj: Any?) : AbstractWrapper(obj) {
|
class UserIdToReaction(obj: Any?) : AbstractWrapper(obj) {
|
||||||
val userId = SnapUUID(instanceNonNull().getObjectField("mUserId"))
|
@get:JSGetter @set:JSSetter
|
||||||
val reactionId = (instanceNonNull().getObjectField("mReaction")
|
var userId by field("mUserId") { SnapUUID(it) }
|
||||||
|
@get:JSGetter @set:JSSetter
|
||||||
|
var reactionId get() = (instanceNonNull().getObjectField("mReaction")
|
||||||
?.getObjectField("mReactionContent")
|
?.getObjectField("mReactionContent")
|
||||||
?.getObjectField("mIntentionType") as Long?) ?: 0
|
?.getObjectField("mIntentionType") as Long?) ?: -1
|
||||||
|
set(value) {
|
||||||
|
instanceNonNull().getObjectField("mReaction")
|
||||||
|
?.getObjectField("mReactionContent")
|
||||||
|
?.setObjectField("mIntentionType", value)
|
||||||
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user