fix(core): arroyo conversation

Signed-off-by: rhunk <101876869+rhunk@users.noreply.github.com>
This commit is contained in:
rhunk
2025-07-21 16:33:09 +02:00
parent 68c9a4cc4e
commit b1abac22ee
4 changed files with 105 additions and 43 deletions

View File

@@ -325,7 +325,7 @@ class SnapEnhance {
MessagingFriendInfo(
friendUserId,
appContext.database.getConversationLinkFromUserId(friendUserId)?.clientConversationId,
it.key,
friend.displayName,
friend.mutableUsername ?: friend.usernameForSorting!!,
friend.bitmojiAvatarId,
@@ -344,7 +344,7 @@ class SnapEnhance {
return appContext.database.getFriendInfo(uuid)?.let {
MessagingFriendInfo(
userId = it.userId!!,
dmConversationId = appContext.database.getConversationLinkFromUserId(it.userId!!)?.clientConversationId,
dmConversationId = null,
displayName = it.displayName,
mutableUsername = it.mutableUsername!!,
bitmojiId = it.bitmojiAvatarId,

View File

@@ -54,7 +54,6 @@ import me.rhunk.snapenhance.core.ui.ViewAppearanceHelper
import me.rhunk.snapenhance.core.util.EvictingMap
import me.rhunk.snapenhance.core.util.dataBuilder
import me.rhunk.snapenhance.mapper.impl.FriendRelationshipChangerMapper
import java.net.URL
import java.text.DateFormat
import java.util.Date
import kotlin.random.Random
@@ -184,7 +183,7 @@ class BulkMessagingAction : AbstractAction() {
}
private fun getDMLastMessage(userId: String?): ConversationMessage? {
return context.database.getConversationLinkFromUserId(userId ?: return null)?.clientConversationId?.let {
return context.database.getDMConversationId(userId ?: return null)?.let {
context.database.getMessagesFromConversationId(it, 1)
}?.firstOrNull()
}

View File

@@ -13,6 +13,7 @@ import me.rhunk.snapenhance.common.util.ktx.getInteger
import me.rhunk.snapenhance.common.util.ktx.getStringOrNull
import me.rhunk.snapenhance.common.util.protobuf.ProtoReader
import me.rhunk.snapenhance.core.ModContext
import me.rhunk.snapenhance.core.wrapper.impl.toSnapUUID
import me.rhunk.snapenhance.nativelib.NativeLib
@@ -30,6 +31,14 @@ class DatabaseAccess(
) {
private val openedDatabases = mutableMapOf<DatabaseType, SQLiteDatabase>()
private val hasArroyoConversationTable by lazy {
useDatabase(DatabaseType.ARROYO)?.performOperation {
safeRawQuery("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'conversation'")?.use { query ->
query.moveToFirst() && query.getStringOrNull("name") == "conversation"
}
} == true
}
private fun useDatabase(database: DatabaseType, writeMode: Boolean = false): SQLiteDatabase? {
// only cache read-only databases
if (!writeMode && openedDatabases.containsKey(database) && openedDatabases[database]?.isOpen == true) {
@@ -88,7 +97,44 @@ class DatabaseAccess(
}.getOrNull()
}
private val friendDMsCache by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
getFeedEntries(Int.MAX_VALUE)
.filter { it.conversationType == 0 && it.participantsSize == 2 }
.associate { it.participants?.firstOrNull { it != myUserId } to it.key }
.toMutableMap()
}
private val dmOtherParticipantCache by lazy {
if (hasArroyoConversationTable) {
return@lazy useDatabase(DatabaseType.ARROYO)?.performOperation {
safeRawQuery(
"SELECT client_conversation_id, conversation_metadata FROM conversation",
)?.use { query ->
val result = mutableMapOf<String, String?>()
if (!query.moveToFirst()) {
return@performOperation null
}
do {
val conversationId = query.getStringOrNull("client_conversation_id") ?: continue
val conversationMetadata = ProtoReader(query.getBlobOrNull("conversation_metadata") ?: continue)
val participants = mutableListOf<String>()
conversationMetadata.eachBuffer(3) {
participants.add(getByteArray(1, 1)?.toSnapUUID()?.toString() ?: return@eachBuffer)
}
result[conversationId] = if (participants.size == 2) {
participants.firstOrNull { it != myUserId }?.also {
result[it] = null
}
} else null
} while (query.moveToNext())
result
}
}?.toMutableMap() ?: mutableMapOf()
}
(useDatabase(DatabaseType.ARROYO)?.performOperation {
safeRawQuery(
"SELECT client_conversation_id, conversation_type, user_id FROM user_conversation WHERE user_id != ?",
@@ -293,6 +339,10 @@ class DatabaseAccess(
}
fun getConversationType(conversationId: String): Int? {
if (hasArroyoConversationTable) {
return getFeedEntryByConversationId(conversationId)?.conversationType
}
return useDatabase(DatabaseType.ARROYO)?.performOperation {
safeRawQuery(
"SELECT conversation_type FROM user_conversation WHERE client_conversation_id = ?",
@@ -306,37 +356,68 @@ class DatabaseAccess(
}
}
fun getConversationLinkFromUserId(userId: String): UserConversationLink? {
fun getDMConversationId(userId: String): String? {
if (hasArroyoConversationTable) {
return friendDMsCache[userId]
}
return useDatabase(DatabaseType.ARROYO)?.performOperation {
readDatabaseObject(
UserConversationLink(),
"user_conversation",
"user_id = ? AND conversation_type = 0",
arrayOf(userId)
)
)?.clientConversationId
}
}
private fun getConversationParticipantsRaw(conversationId: String): List<String>? {
if (hasArroyoConversationTable) {
return useDatabase(DatabaseType.ARROYO)?.performOperation {
safeRawQuery(
"SELECT conversation_metadata FROM conversation WHERE client_conversation_id = ?",
arrayOf(conversationId)
)?.use { query ->
val participants = mutableListOf<String>()
if (!query.moveToFirst()) {
return@performOperation null
}
val conversationMetadata = ProtoReader(query.getBlobOrNull("conversation_metadata") ?: return@performOperation null)
conversationMetadata.eachBuffer(3) {
participants.add(getByteArray(1, 1)?.toSnapUUID()?.toString() ?: return@eachBuffer)
}
participants
}
}
}
return useDatabase(DatabaseType.ARROYO)?.performOperation {
safeRawQuery(
"SELECT user_id FROM user_conversation WHERE client_conversation_id = ?",
arrayOf(conversationId)
)?.use { query ->
if (!query.moveToFirst()) {
return@performOperation emptyList()
}
val participants = mutableListOf<String>()
do {
query.getStringOrNull("user_id")?.let { participants.add(it) }
} while (query.moveToNext())
participants
}
}
}
fun getDMOtherParticipant(conversationId: String): String? {
if (dmOtherParticipantCache.containsKey(conversationId)) return dmOtherParticipantCache[conversationId]
return useDatabase(DatabaseType.ARROYO)?.performOperation {
safeRawQuery(
"SELECT user_id FROM user_conversation WHERE client_conversation_id = ? AND conversation_type = 0",
arrayOf(conversationId)
)?.use { query ->
val participants = mutableListOf<String>()
if (!query.moveToFirst()) {
return@performOperation null
}
do {
participants.add(query.getStringOrNull("user_id")!!)
} while (query.moveToNext())
participants.firstOrNull { it != myUserId }
}.also { dmOtherParticipantCache[conversationId] = it }
return getConversationParticipantsRaw(conversationId)?.takeIf { it.size == 2 }?.firstOrNull { it != myUserId }.also {
dmOtherParticipantCache[conversationId] = it
}
}
fun getStoryEntryFromId(storyId: String): StoryEntry? {
return useDatabase(DatabaseType.MAIN)?.performOperation {
readDatabaseObject(StoryEntry(), "Story", "storyId = ?", arrayOf(storyId))
@@ -345,28 +426,10 @@ class DatabaseAccess(
fun getConversationParticipants(conversationId: String, useCache: Boolean = true): List<String>? {
if (dmOtherParticipantCache[conversationId] != null && useCache) return dmOtherParticipantCache[conversationId]?.let { listOf(myUserId, it) }
return useDatabase(DatabaseType.ARROYO)?.performOperation {
safeRawQuery(
"SELECT user_id, conversation_type FROM user_conversation WHERE client_conversation_id = ?",
arrayOf(conversationId)
)?.use { cursor ->
if (!cursor.moveToFirst()) {
return@performOperation null
}
val participants = mutableListOf<String>()
var conversationType = -1
do {
if (conversationType == -1) conversationType = cursor.getInteger("conversation_type")
participants.add(cursor.getStringOrNull("user_id")!!)
} while (cursor.moveToNext())
if (!dmOtherParticipantCache.containsKey(conversationId)) {
dmOtherParticipantCache[conversationId] = when (conversationType) {
0 -> participants.firstOrNull { it != myUserId }
else -> null
}
}
participants
return getConversationParticipantsRaw(conversationId)?.also {
if (!dmOtherParticipantCache.containsKey(conversationId)) {
dmOtherParticipantCache[conversationId] = it.firstOrNull { it != myUserId }
}
}
}

View File

@@ -116,5 +116,5 @@ class CoreMessagingBridge(
}
}
override fun getOneToOneConversationId(userId: String) = context.database.getConversationLinkFromUserId(userId)?.clientConversationId
override fun getOneToOneConversationId(userId: String) = context.database.getDMConversationId(userId)
}