refactor: wrappers

This commit is contained in:
rhunk
2023-11-02 16:26:08 +01:00
parent 3fc06550bf
commit 07daeaf994
25 changed files with 149 additions and 147 deletions

View File

@ -176,7 +176,7 @@ class ExportChatMessages : AbstractAction() {
}
fetchedMessages.firstOrNull()?.let {
lastMessageId = it.messageDescriptor.messageId
lastMessageId = it.messageDescriptor!!.messageId!!
}
setStatus("Exporting (${foundMessages.size} / ${foundMessages.firstOrNull()?.orderKey})")
}

View File

@ -29,7 +29,7 @@ class ScopeSync : Feature("Scope Sync", loadParams = FeatureLoadParams.INIT_SYNC
if (event.messageContent.contentType != ContentType.SNAP) return@subscribe
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] = (context.coroutineScope.launch {

View File

@ -77,7 +77,7 @@ object MessageDecoder {
fun decode(messageContent: MessageContent): List<DecodedAttachment> {
return decode(
ProtoReader(messageContent.content),
ProtoReader(messageContent.content!!),
customMediaReferences = getEncodedMediaReferences(gson.toJsonTree(messageContent.instanceNonNull()))
)
}

View File

@ -12,10 +12,10 @@ class SnapToChatMedia : Feature("SnapToChatMedia", loadParams = FeatureLoadParam
if (!context.config.experimental.snapToChatMedia.get()) return
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
event.message.messageContent.content = ProtoWriter().apply {
val snapMessageContent = ProtoReader(event.message.messageContent!!.content!!).followPath(11)?.getBuffer() ?: return@subscribe
event.message.messageContent!!.content = ProtoWriter().apply {
from(3) {
addBuffer(3, snapMessageContent)
}

View File

@ -40,7 +40,7 @@ class BypassVideoLengthRestriction :
})
context.event.subscribe(SendMessageWithContentEvent::class) { event ->
if (event.destinations.stories.isEmpty()) return@subscribe
if (event.destinations.stories!!.isEmpty()) return@subscribe
fileObserver.startWatching()
}
}

View File

@ -26,8 +26,8 @@ class AutoSave : MessagingRuleFeature("Auto Save", MessagingRuleType.AUTO_SAVE,
}
private fun saveMessage(conversationId: SnapUUID, message: Message) {
val messageId = message.messageDescriptor.messageId
if (messageLogger.takeIf { it.isEnabled }?.isMessageDeleted(conversationId.toString(), message.messageDescriptor.messageId) == true) return
val messageId = message.messageDescriptor!!.messageId!!
if (messageLogger.takeIf { it.isEnabled }?.isMessageDeleted(conversationId.toString(), messageId) == true) return
if (message.messageState != MessageState.COMMITTED) return
runCatching {
@ -50,8 +50,8 @@ class AutoSave : MessagingRuleFeature("Auto Save", MessagingRuleType.AUTO_SAVE,
private fun canSaveMessage(message: Message): Boolean {
if (context.mainActivity == null || context.isMainActivityPaused) return false
if (message.messageMetadata.savedBy.any { uuid -> uuid.toString() == context.database.myUserId }) return false
val contentType = message.messageContent.contentType.toString()
if (message.messageMetadata!!.savedBy!!.any { uuid -> uuid.toString() == context.database.myUserId }) return false
val contentType = message.messageContent!!.contentType.toString()
return autoSaveFilter.any { it == contentType }
}
@ -96,7 +96,7 @@ class AutoSave : MessagingRuleFeature("Auto Save", MessagingRuleType.AUTO_SAVE,
{ autoSaveFilter.isNotEmpty() }
) { param ->
val message = Message(param.arg(0))
val conversationId = message.messageDescriptor.conversationId
val conversationId = message.messageDescriptor!!.conversationId!!
if (!canSaveInConversation(conversationId.toString())) return@hook
if (!canSaveMessage(message)) return@hook

View File

@ -68,11 +68,11 @@ class InstantDelete : Feature("InstantDelete", loadParams = FeatureLoadParams.AC
if (chatActionMenuOptions["chat_action_menu_erase_quote"] == menuOptionText.text) {
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(
conversationId,
quotedMessage.content.messageId,
quotedMessage.content!!.messageId!!,
MessageUpdate.ERASE,
onResult = onCallbackResult
)

View File

@ -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.decoder.MessageDecoder
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.hook
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 contentData = snapMessage.messageContent.content
val contentType = snapMessage.messageContent!!.contentType ?: return@onEach
val contentData = snapMessage.messageContent!!.content!!
val formatUsername: (String) -> String = { "$senderUsername: $it" }
val notificationCache = cachedMessages.let { it.computeIfAbsent(conversationId) { mutableListOf() } }
val appendNotifications: () -> Unit = { setNotificationText(notificationData.notification, conversationId)}
setupNotificationActionButtons(contentType, conversationId, snapMessage.messageDescriptor.messageId, notificationData)
setupNotificationActionButtons(contentType, conversationId, snapMessage.messageDescriptor!!.messageId!!, notificationData)
when (contentType) {
ContentType.NOTE -> {
@ -286,14 +285,14 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
}
ContentType.SNAP, ContentType.EXTERNAL_MEDIA -> {
val mediaReferences = MessageDecoder.getMediaReferences(
messageContent = context.gson.toJsonTree(snapMessage.messageContent.instanceNonNull())
messageContent = context.gson.toJsonTree(snapMessage.messageContent!!.instanceNonNull())
)
val mediaReferenceKeys = mediaReferences.map { reference ->
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)
runCatching {

View File

@ -58,7 +58,7 @@ class SendOverride : Feature("Send Override", loadParams = FeatureLoadParams.INI
if (localMessageContent.contentType != ContentType.EXTERNAL_MEDIA) return@subscribe
//prevent story replies
val messageProtoReader = ProtoReader(localMessageContent.content)
val messageProtoReader = ProtoReader(localMessageContent.content!!)
if (messageProtoReader.contains(7)) return@subscribe
event.canceled = true

View File

@ -15,13 +15,13 @@ class UnlimitedSnapViewTime :
context.event.subscribe(BuildMessageEvent::class, { state }, priority = 101) { event ->
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 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
messageContent.content = ProtoEditor(messageContent.content).apply {
messageContent.content = ProtoEditor(messageContent.content!!).apply {
edit(11, 5, 2) {
remove(8)
addBuffer(6, byteArrayOf())

View File

@ -101,14 +101,14 @@ class MessageLogger : Feature("MessageLogger",
val messageInstance = event.message.instanceNonNull()
if (event.message.messageState != MessageState.COMMITTED) return@subscribe
cachedIdLinks[event.message.messageDescriptor.messageId] = event.message.orderKey
val conversationId = event.message.messageDescriptor.conversationId.toString()
cachedIdLinks[event.message.messageDescriptor!!.messageId!!] = event.message.orderKey!!
val conversationId = event.message.messageDescriptor!!.conversationId.toString()
//exclude messages sent by me
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
fetchedMessages.add(uniqueMessageIdentifier)

View File

@ -13,13 +13,13 @@ import me.rhunk.snapenhance.core.features.impl.messaging.Messaging
fun me.rhunk.snapenhance.core.wrapper.impl.Message.toBridge(): Message {
return Message().also { output ->
output.conversationId = this.messageDescriptor.conversationId.toString()
output.conversationId = this.messageDescriptor!!.conversationId.toString()
output.senderId = this.senderId.toString()
output.clientMessageId = this.messageDescriptor.messageId
output.serverMessageId = this.orderKey
output.contentType = this.messageContent.contentType?.id ?: -1
output.content = this.messageContent.content
output.mediaReferences = MessageDecoder.getEncodedMediaReferences(this.messageContent)
output.clientMessageId = this.messageDescriptor!!.messageId!!
output.serverMessageId = this.orderKey!!
output.contentType = this.messageContent?.contentType?.id ?: -1
output.content = this.messageContent?.content
output.mediaReferences = MessageDecoder.getEncodedMediaReferences(this.messageContent!!)
}
}

View File

@ -74,8 +74,8 @@ class MessageExporter(
}
private fun serializeMessageContent(message: Message): String? {
return if (message.messageContent.contentType == ContentType.CHAT) {
ProtoReader(message.messageContent.content).getString(2, 1) ?: "Failed to parse message"
return if (message.messageContent!!.contentType == ContentType.CHAT) {
ProtoReader(message.messageContent!!.content!!).getString(2, 1) ?: "Failed to parse message"
} else null
}
@ -93,8 +93,8 @@ class MessageExporter(
val sender = conversationParticipants[message.senderId.toString()]
val senderUsername = sender?.usernameForSorting ?: message.senderId.toString()
val senderDisplayName = sender?.displayName ?: message.senderId.toString()
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 messageContent = serializeMessageContent(message) ?: message.messageContent!!.contentType?.name
val date = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH).format(Date(message.messageMetadata!!.createdAt!!))
writer.write("[$date] - $senderDisplayName (${senderUsername}): $messageContent\n")
}
writer.flush()
@ -110,17 +110,17 @@ class MessageExporter(
fun updateProgress(type: String) {
val total = messages.filter {
mediaToDownload?.contains(it.messageContent.contentType) ?: false
mediaToDownload?.contains(it.messageContent!!.contentType) ?: false
}.size
processCount++
printLog("$type $processCount/$total")
}
messages.filter {
mediaToDownload?.contains(it.messageContent.contentType) ?: false
mediaToDownload?.contains(it.messageContent!!.contentType) ?: false
}.forEach { message ->
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)
runCatching {
@ -145,8 +145,8 @@ class MessageExporter(
updateProgress("downloaded")
}.onFailure {
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)
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)
}
}
}
@ -270,7 +270,7 @@ class MessageExporter(
add(JsonObject().apply {
addProperty("orderKey", message.orderKey)
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>) {
add(name, JsonArray().apply {
@ -278,12 +278,12 @@ class MessageExporter(
})
}
addUUIDList("savedBy", message.messageMetadata.savedBy)
addUUIDList("seenBy", message.messageMetadata.seenBy)
addUUIDList("openedBy", message.messageMetadata.openedBy)
addUUIDList("savedBy", message.messageMetadata!!.savedBy!!)
addUUIDList("seenBy", message.messageMetadata!!.seenBy!!)
addUUIDList("openedBy", message.messageMetadata!!.openedBy!!)
add("reactions", JsonObject().apply {
message.messageMetadata.reactions.forEach { reaction ->
message.messageMetadata!!.reactions!!.forEach { reaction ->
addProperty(
participants.getOrDefault(reaction.userId.toString(), -1L).toString(),
reaction.reactionId
@ -291,13 +291,13 @@ class MessageExporter(
}
})
addProperty("createdTimestamp", message.messageMetadata.createdAt)
addProperty("readTimestamp", message.messageMetadata.readAt)
addProperty("createdTimestamp", message.messageMetadata!!.createdAt)
addProperty("readTimestamp", message.messageMetadata!!.readAt)
addProperty("serializedContent", serializeMessageContent(message))
addProperty("rawContent", Base64.UrlSafe.encode(message.messageContent.content))
addProperty("rawContent", Base64.UrlSafe.encode(message.messageContent!!.content!!))
add("attachments", JsonArray().apply {
MessageDecoder.decode(message.messageContent)
MessageDecoder.decode(message.messageContent!!)
.forEach attachments@{ attachments ->
if (attachments.type == AttachmentType.STICKER) //TODO: implement stickers
return@attachments

View File

@ -94,12 +94,12 @@ class MessageSender(
val localMessageContent = context.gson.fromJson(localMessageContentTemplate, context.classCache.localMessageContent)
val messageDestinations = MessageDestinations(AbstractWrapper.newEmptyInstance(context.classCache.messageDestinations)).also {
it.conversations = conversations
it.mPhoneNumbers = arrayListOf()
it.stories = arrayListOf()
it.conversations = conversations.toCollection(ArrayList())
it.mPhoneNumbers = arrayListOf<Any>()
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 = {}) {

View File

@ -2,24 +2,47 @@ package me.rhunk.snapenhance.core.wrapper
import de.robv.android.xposed.XposedHelpers
import me.rhunk.snapenhance.core.util.CallbackBuilder
import me.rhunk.snapenhance.core.wrapper.impl.SnapUUID
import kotlin.reflect.KProperty
abstract class AbstractWrapper(
protected var instance: Any?
) {
protected val uuidArrayListMapper: (Any?) -> ArrayList<SnapUUID> get() = { (it as ArrayList<*>).map { i -> SnapUUID(i) }.toCollection(ArrayList()) }
@Suppress("UNCHECKED_CAST")
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 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 {
fun newEmptyInstance(clazz: Class<*>): Any {
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
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> field(fieldName: String, mapper: ((Any?) -> T?)? = null) = FieldAccessor(fieldName, mapper)
fun <T : Enum<*>> getEnumValue(fieldName: String, defaultValue: T?): T? {
if (defaultValue == null) return null

View File

@ -7,7 +7,10 @@ import me.rhunk.snapenhance.core.wrapper.AbstractWrapper
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 val updateMessageMethod by lazy { findMethodByName("updateMessage") }

View File

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

View File

@ -1,14 +1,21 @@
package me.rhunk.snapenhance.core.wrapper.impl
import me.rhunk.snapenhance.common.data.MessageState
import me.rhunk.snapenhance.core.util.ktx.getObjectField
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) {
val orderKey get() = instanceNonNull().getObjectField("mOrderKey") as Long
val senderId get() = SnapUUID(instanceNonNull().getObjectField("mSenderId"))
val messageContent get() = MessageContent(instanceNonNull().getObjectField("mMessageContent"))
val messageDescriptor get() = MessageDescriptor(instanceNonNull().getObjectField("mDescriptor"))
val messageMetadata get() = MessageMetadata(instanceNonNull().getObjectField("mMetadata"))
@get:JSGetter @set:JSSetter
var orderKey by field<Long>("mOrderKey")
@get:JSGetter @set:JSSetter
var senderId by field("mSenderId") { SnapUUID(it) }
@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)
}

View File

@ -1,15 +1,15 @@
package me.rhunk.snapenhance.core.wrapper.impl
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 org.mozilla.javascript.annotations.JSGetter
import org.mozilla.javascript.annotations.JSSetter
class MessageContent(obj: Any?) : AbstractWrapper(obj) {
var content
get() = instanceNonNull().getObjectField("mContent") as ByteArray
set(value) = instanceNonNull().setObjectField("mContent", value)
val quotedMessage
get() = QuotedMessage(instanceNonNull().getObjectField("mQuotedMessage"))
@get:JSGetter @set:JSSetter
var content by field<ByteArray>("mContent")
@get:JSGetter @set:JSSetter
var quotedMessage by field("mQuotedMessage") { QuotedMessage(it) }
@get:JSGetter @set:JSSetter
var contentType by enum("mContentType", ContentType.UNKNOWN)
}

View File

@ -1,9 +1,11 @@
package me.rhunk.snapenhance.core.wrapper.impl
import me.rhunk.snapenhance.core.util.ktx.getObjectField
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) {
val messageId: Long get() = instanceNonNull().getObjectField("mMessageId") as Long
val conversationId: SnapUUID get() = SnapUUID(instanceNonNull().getObjectField("mConversationId")!!)
@get:JSGetter @set:JSSetter
var messageId by field<Long>("mMessageId")
val conversationId by field("mConversationId") { SnapUUID(it) }
}

View File

@ -1,15 +1,10 @@
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
@Suppress("UNCHECKED_CAST")
class MessageDestinations(obj: Any) : AbstractWrapper(obj){
var conversations get() = (instanceNonNull().getObjectField("mConversations") as ArrayList<*>).map { SnapUUID(it) }
set(value) = instanceNonNull().setObjectField("mConversations", value.map { it.instanceNonNull() }.toCollection(ArrayList()))
var stories get() = instanceNonNull().getObjectField("mStories") as ArrayList<Any>
set(value) = instanceNonNull().setObjectField("mStories", value)
var mPhoneNumbers get() = instanceNonNull().getObjectField("mPhoneNumbers") as ArrayList<Any>
set(value) = instanceNonNull().setObjectField("mPhoneNumbers", value)
var conversations by field("mConversations", uuidArrayListMapper)
var stories by field<ArrayList<*>>("mStories")
var mPhoneNumbers by field<ArrayList<*>>("mPhoneNumbers")
}

View File

@ -1,28 +1,26 @@
package me.rhunk.snapenhance.core.wrapper.impl
import me.rhunk.snapenhance.common.data.PlayableSnapState
import me.rhunk.snapenhance.core.util.ktx.getObjectField
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){
val createdAt: Long get() = instanceNonNull().getObjectField("mCreatedAt") as Long
val readAt: Long get() = instanceNonNull().getObjectField("mReadAt") as Long
@get:JSGetter @set:JSSetter
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)
private fun getUUIDList(name: String): List<SnapUUID> {
return (instanceNonNull().getObjectField(name) as List<*>).map { SnapUUID(it!!) }
}
val savedBy: List<SnapUUID> by lazy {
getUUIDList("mSavedBy")
}
val openedBy: List<SnapUUID> by lazy {
getUUIDList("mOpenedBy")
}
val seenBy: List<SnapUUID> by lazy {
getUUIDList("mSeenBy")
}
val reactions: List<UserIdToReaction> by lazy {
(instanceNonNull().getObjectField("mReactions") as List<*>).map { UserIdToReaction(it!!) }
@get:JSGetter @set:JSSetter
var savedBy by field("mSavedBy", uuidArrayListMapper)
@get:JSGetter @set:JSSetter
var openedBy by field("mOpenedBy", uuidArrayListMapper)
@get:JSGetter @set:JSSetter
var seenBy by field("mSeenBy", uuidArrayListMapper)
@get:JSGetter @set:JSSetter
var reactions by field("mReactions") {
(it as ArrayList<*>).map { i -> UserIdToReaction(i) }.toMutableList()
}
}

View File

@ -1,8 +1,10 @@
package me.rhunk.snapenhance.core.wrapper.impl
import me.rhunk.snapenhance.core.util.ktx.getObjectField
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) {
val content get() = QuotedMessageContent(instanceNonNull().getObjectField("mContent"))
@get:JSGetter @set:JSSetter
var content by field("mContent") { QuotedMessageContent(it) }
}

View File

@ -1,10 +1,10 @@
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 org.mozilla.javascript.annotations.JSGetter
import org.mozilla.javascript.annotations.JSSetter
class QuotedMessageContent(obj: Any?) : AbstractWrapper(obj) {
var messageId get() = instanceNonNull().getObjectField("mMessageId") as Long
set(value) = instanceNonNull().setObjectField("mMessageId", value)
@get:JSGetter @set:JSSetter
var messageId by field<Long>("mMessageId")
}

View File

@ -1,11 +1,21 @@
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 org.mozilla.javascript.annotations.JSGetter
import org.mozilla.javascript.annotations.JSSetter
class UserIdToReaction(obj: Any?) : AbstractWrapper(obj) {
val userId = SnapUUID(instanceNonNull().getObjectField("mUserId"))
val reactionId = (instanceNonNull().getObjectField("mReaction")
@get:JSGetter @set:JSSetter
var userId by field("mUserId") { SnapUUID(it) }
@get:JSGetter @set:JSSetter
var reactionId get() = (instanceNonNull().getObjectField("mReaction")
?.getObjectField("mReactionContent")
?.getObjectField("mIntentionType") as Long?) ?: 0
?.getObjectField("mIntentionType") as Long?) ?: -1
set(value) {
instanceNonNull().getObjectField("mReaction")
?.getObjectField("mReactionContent")
?.setObjectField("mIntentionType", value)
}
}