mirror of
https://github.com/oxen-io/session-android.git
synced 2025-01-12 09:13:39 +00:00
Refactor
This commit is contained in:
parent
46a92457bc
commit
55589b6893
@ -7,18 +7,15 @@ import org.session.libsignal.crypto.ecc.DjbECPrivateKey
|
||||
import org.session.libsignal.crypto.ecc.DjbECPublicKey
|
||||
import org.session.libsignal.crypto.ecc.ECKeyPair
|
||||
import org.session.libsignal.database.LokiAPIDatabaseProtocol
|
||||
import org.session.libsignal.utilities.*
|
||||
import org.session.libsignal.utilities.Hex
|
||||
import org.session.libsignal.utilities.Log
|
||||
import org.session.libsignal.utilities.PublicKeyValidation
|
||||
import org.session.libsignal.utilities.Snode
|
||||
import org.session.libsignal.utilities.removingIdPrefixIfNeeded
|
||||
import org.session.libsignal.utilities.toHexString
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||
import java.util.*
|
||||
import kotlin.Array
|
||||
import kotlin.Boolean
|
||||
import kotlin.Int
|
||||
import kotlin.Long
|
||||
import kotlin.Pair
|
||||
import kotlin.String
|
||||
import kotlin.arrayOf
|
||||
import kotlin.to
|
||||
import java.util.Date
|
||||
|
||||
class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiAPIDatabaseProtocol {
|
||||
|
||||
@ -95,13 +92,18 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
public val groupPublicKey = "group_public_key"
|
||||
@JvmStatic
|
||||
val createClosedGroupPublicKeysTable = "CREATE TABLE $closedGroupPublicKeysTable ($groupPublicKey STRING PRIMARY KEY)"
|
||||
// Open group server capabilities
|
||||
private val serverCapabilitiesTable = "open_group_server_capabilities"
|
||||
private val capabilities = "capabilities"
|
||||
@JvmStatic
|
||||
val createServerCapabilitiesCommand = "CREATE TABLE $serverCapabilitiesTable($server STRING PRIMARY KEY, $capabilities STRING)"
|
||||
// Last inbox message server IDs
|
||||
private val lastInboxMessageServerIdTable = "loki_api_last_inbox_message_server_id_cache"
|
||||
private val lastInboxMessageServerIdTable = "open_group_last_inbox_message_server_id_cache"
|
||||
private val lastInboxMessageServerId = "last_inbox_message_server_id"
|
||||
@JvmStatic
|
||||
val createLastInboxMessageServerIdCommand = "CREATE TABLE $lastInboxMessageServerIdTable($server STRING PRIMARY KEY, $lastInboxMessageServerId INTEGER DEFAULT 0)"
|
||||
// Last outbox message server IDs
|
||||
private val lastOutboxMessageServerIdTable = "loki_api_last_outbox_message_server_id_cache"
|
||||
private val lastOutboxMessageServerIdTable = "open_group_last_outbox_message_server_id_cache"
|
||||
private val lastOutboxMessageServerId = "last_outbox_message_server_id"
|
||||
@JvmStatic
|
||||
val createLastOutboxMessageServerIdCommand = "CREATE TABLE $lastOutboxMessageServerIdTable($server STRING PRIMARY KEY, $lastOutboxMessageServerId INTEGER DEFAULT 0)"
|
||||
@ -450,6 +452,19 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
database.delete(closedGroupPublicKeysTable, "${Companion.groupPublicKey} = ?", wrap(groupPublicKey))
|
||||
}
|
||||
|
||||
fun setServerCapabilities(serverName: String, serverCapabilities: List<String>) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val row = wrap(mapOf(server to serverName, capabilities to serverCapabilities.joinToString(",")))
|
||||
database.insertOrUpdate(serverCapabilitiesTable, row, "$server = ?", wrap(serverName))
|
||||
}
|
||||
|
||||
fun getServerCapabilities(serverName: String): List<String> {
|
||||
val database = databaseHelper.writableDatabase
|
||||
return database.get(serverCapabilitiesTable, "$server = ?", wrap(serverName)) { cursor ->
|
||||
cursor.getString(server)
|
||||
}?.split(",") ?: emptyList()
|
||||
}
|
||||
|
||||
fun setLastInboxMessageId(serverName: String, newValue: Long) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val row = wrap(mapOf(server to serverName, lastInboxMessageServerId to newValue.toString()))
|
||||
|
@ -292,16 +292,6 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
return getAllOpenGroups().values.firstOrNull { it.server == server && it.room == room }
|
||||
}
|
||||
|
||||
override fun updateOpenGroupCapabilities(server: String, capabilities: List<String>) {
|
||||
getAllOpenGroups().values.filter { it.server == server }
|
||||
.map { it.copy(capabilities = it.capabilities) }
|
||||
.forEach(this::updateOpenGroup)
|
||||
}
|
||||
|
||||
override fun getOpenGroupServer(server: String): List<String> {
|
||||
return getAllOpenGroups().values.firstOrNull { it.server == server }?.capabilities ?: emptyList()
|
||||
}
|
||||
|
||||
override fun isDuplicateMessage(timestamp: Long): Boolean {
|
||||
return getReceivedMessageTimestamps().contains(timestamp)
|
||||
}
|
||||
@ -521,6 +511,14 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
DatabaseComponent.get(context).recipientDatabase().setExpireMessages(recipient, duration);
|
||||
}
|
||||
|
||||
override fun setServerCapabilities(server: String, capabilities: List<String>) {
|
||||
return DatabaseComponent.get(context).lokiAPIDatabase().setServerCapabilities(server, capabilities)
|
||||
}
|
||||
|
||||
override fun getServerCapabilities(server: String): List<String> {
|
||||
return DatabaseComponent.get(context).lokiAPIDatabase().getServerCapabilities(server)
|
||||
}
|
||||
|
||||
override fun getAllOpenGroups(): Map<Long, OpenGroup> {
|
||||
return DatabaseComponent.get(context).lokiThreadDatabase().getAllOpenGroups()
|
||||
}
|
||||
|
@ -126,6 +126,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
db.execSQL(LokiAPIDatabase.getCreateOpenGroupProfilePictureTableCommand());
|
||||
db.execSQL(LokiAPIDatabase.getCreateClosedGroupEncryptionKeyPairsTable());
|
||||
db.execSQL(LokiAPIDatabase.getCreateClosedGroupPublicKeysTable());
|
||||
db.execSQL(LokiAPIDatabase.getCreateServerCapabilitiesCommand());
|
||||
db.execSQL(LokiAPIDatabase.getCreateLastInboxMessageServerIdCommand());
|
||||
db.execSQL(LokiAPIDatabase.getCreateLastOutboxMessageServerIdCommand());
|
||||
db.execSQL(LokiMessageDatabase.getCreateMessageIDTableCommand());
|
||||
@ -341,6 +342,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
}
|
||||
|
||||
if (oldVersion < lokiV33) {
|
||||
db.execSQL(LokiAPIDatabase.getCreateServerCapabilitiesCommand());
|
||||
db.execSQL(LokiAPIDatabase.getCreateLastInboxMessageServerIdCommand());
|
||||
db.execSQL(LokiAPIDatabase.getCreateLastOutboxMessageServerIdCommand());
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.asLiveData
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import nl.komponents.kovenant.functional.map
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||
import org.thoughtcrime.securesms.util.State
|
||||
|
||||
@ -13,8 +14,10 @@ typealias GroupState = State<DefaultGroups>
|
||||
class DefaultGroupsViewModel : ViewModel() {
|
||||
|
||||
init {
|
||||
OpenGroupApi.getDefaultServerCapabilities().map {
|
||||
OpenGroupApi.getDefaultRoomsIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
val defaultRooms = OpenGroupApi.defaultRooms.map<DefaultGroups, GroupState> {
|
||||
State.Success(it)
|
||||
|
@ -68,14 +68,17 @@ object OpenGroupManager {
|
||||
storage.removeLastOutboxMessageId(server)
|
||||
// Store the public key
|
||||
storage.setOpenGroupPublicKey(server,publicKey)
|
||||
// Get capabilities and room info
|
||||
val (capabilities, info) = OpenGroupApi.getCapabilitiesAndRoomInfo(room, server).get()
|
||||
// Get capabilities
|
||||
val capabilities = OpenGroupApi.getCapabilities(server).get()
|
||||
storage.setServerCapabilities(server, capabilities.capabilities)
|
||||
// Get room info
|
||||
val info = OpenGroupApi.getRoomInfo(room, server).get()
|
||||
storage.setUserCount(room, server, info.activeUsers)
|
||||
// Create the group locally if not available already
|
||||
if (threadID < 0) {
|
||||
threadID = GroupManager.createOpenGroup(openGroupID, context, null, info.name).threadId
|
||||
}
|
||||
val openGroup = OpenGroup(server, room, info.name, info.infoUpdates, publicKey, capabilities.capabilities)
|
||||
val openGroup = OpenGroup(server, room, info.name, info.infoUpdates, publicKey)
|
||||
threadDB.setOpenGroupChat(openGroup, threadID)
|
||||
// Start the poller if needed
|
||||
pollers[server]?.startIfNeeded() ?: run {
|
||||
|
@ -54,6 +54,10 @@ interface StorageProtocol {
|
||||
fun setAuthToken(room: String, server: String, newValue: String)
|
||||
fun removeAuthToken(room: String, server: String)
|
||||
|
||||
// Servers
|
||||
fun setServerCapabilities(server: String, capabilities: List<String>)
|
||||
fun getServerCapabilities(server: String): List<String>
|
||||
|
||||
// Open Groups
|
||||
fun getAllOpenGroups(): Map<Long, OpenGroup>
|
||||
fun updateOpenGroup(openGroup: OpenGroup)
|
||||
@ -61,8 +65,6 @@ interface StorageProtocol {
|
||||
fun addOpenGroup(urlAsString: String)
|
||||
fun setOpenGroupServerMessageID(messageID: Long, serverID: Long, threadID: Long, isSms: Boolean)
|
||||
fun getOpenGroup(room: String, server: String): OpenGroup?
|
||||
fun updateOpenGroupCapabilities(server: String, capabilities: List<String>)
|
||||
fun getOpenGroupServer(server: String): List<String>
|
||||
|
||||
// Open Group Public Keys
|
||||
fun getOpenGroupPublicKey(server: String): String?
|
||||
|
@ -20,9 +20,9 @@ sealed class Destination {
|
||||
class OpenGroup(
|
||||
val roomToken: String,
|
||||
val server: String,
|
||||
val whisperTo: String? = null,
|
||||
val whisperTo: List<String>? = null,
|
||||
val whisperMods: Boolean = false,
|
||||
val fileIds: List<String> = emptyList()
|
||||
val fileIds: List<String>? = null
|
||||
) : Destination()
|
||||
|
||||
class OpenGroupInbox(
|
||||
@ -33,7 +33,7 @@ sealed class Destination {
|
||||
|
||||
companion object {
|
||||
|
||||
fun from(address: Address): Destination {
|
||||
fun from(address: Address, fileIds: List<String>? = null): Destination {
|
||||
return when {
|
||||
address.isContact -> {
|
||||
Contact(address.contactIdentifier())
|
||||
@ -46,11 +46,9 @@ sealed class Destination {
|
||||
address.isOpenGroup -> {
|
||||
val storage = MessagingModuleConfiguration.shared.storage
|
||||
val threadID = storage.getThreadId(address)!!
|
||||
when (val openGroup = storage.getOpenGroup(threadID)) {
|
||||
is org.session.libsession.messaging.open_groups.OpenGroup
|
||||
-> LegacyOpenGroup(openGroup.room, openGroup.server)
|
||||
else -> throw Exception("Missing open group for thread with ID: $threadID.")
|
||||
}
|
||||
storage.getOpenGroup(threadID)?.let {
|
||||
OpenGroup(roomToken = it.room, server = it.server, fileIds = fileIds)
|
||||
} ?: throw Exception("Missing open group for thread with ID: $threadID.")
|
||||
}
|
||||
address.isOpenGroupInbox -> {
|
||||
val groupInboxId = GroupUtil.getDecodedGroupID(address.serialize()).split(".")
|
||||
|
@ -11,17 +11,15 @@ data class OpenGroup(
|
||||
val name: String,
|
||||
val publicKey: String,
|
||||
val infoUpdates: Int,
|
||||
val capabilities: List<String>
|
||||
) {
|
||||
|
||||
constructor(server: String, room: String, name: String, infoUpdates: Int, publicKey: String, capabilities: List<String>) : this(
|
||||
constructor(server: String, room: String, name: String, infoUpdates: Int, publicKey: String) : this(
|
||||
server = server,
|
||||
room = room,
|
||||
id = "$server.$room",
|
||||
name = name,
|
||||
publicKey = publicKey,
|
||||
infoUpdates = infoUpdates,
|
||||
capabilities = capabilities,
|
||||
)
|
||||
|
||||
companion object {
|
||||
@ -36,7 +34,7 @@ data class OpenGroup(
|
||||
val publicKey = json.get("publicKey").asText()
|
||||
val infoUpdates = json.get("infoUpdates")?.asText()?.toIntOrNull() ?: 0
|
||||
val capabilities = json.get("capabilities")?.asText()?.split(",") ?: emptyList()
|
||||
OpenGroup(server, room, displayName, infoUpdates, publicKey, capabilities)
|
||||
OpenGroup(server, room, displayName, infoUpdates, publicKey)
|
||||
} catch (e: Exception) {
|
||||
Log.w("Loki", "Couldn't parse open group from JSON: $jsonAsString.", e);
|
||||
null
|
||||
@ -51,7 +49,6 @@ data class OpenGroup(
|
||||
"displayName" to name,
|
||||
"publicKey" to publicKey,
|
||||
"infoUpdates" to infoUpdates.toString(),
|
||||
"capabilities" to capabilities.joinToString(","),
|
||||
)
|
||||
|
||||
val joinURL: String get() = "$server/$room?public_key=$publicKey"
|
||||
|
@ -184,6 +184,15 @@ object OpenGroupApi {
|
||||
val signature: String? = null
|
||||
)
|
||||
|
||||
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy::class)
|
||||
data class SendMessageRequest(
|
||||
val data: String? = null,
|
||||
val signature: String? = null,
|
||||
val whisperTo: List<String>? = null,
|
||||
val whisperMods: Boolean? = null,
|
||||
val files: List<String>? = null
|
||||
)
|
||||
|
||||
data class MessageDeletion(
|
||||
@JsonProperty("id")
|
||||
val id: Long = 0,
|
||||
@ -242,7 +251,7 @@ object OpenGroupApi {
|
||||
}
|
||||
}
|
||||
fun execute(): Promise<OnionResponse, Exception> {
|
||||
val serverCapabilities = MessagingModuleConfiguration.shared.storage.getOpenGroupServer(request.server)
|
||||
val serverCapabilities = MessagingModuleConfiguration.shared.storage.getServerCapabilities(request.server)
|
||||
val publicKey =
|
||||
MessagingModuleConfiguration.shared.storage.getOpenGroupPublicKey(request.server)
|
||||
?: return Promise.ofFail(Error.NoPublicKey)
|
||||
@ -364,16 +373,25 @@ object OpenGroupApi {
|
||||
fun send(
|
||||
message: OpenGroupMessage,
|
||||
room: String,
|
||||
server: String
|
||||
server: String,
|
||||
whisperTo: List<String>? = null,
|
||||
whisperMods: Boolean? = null,
|
||||
fileIds: List<String>? = null
|
||||
): Promise<OpenGroupMessage, Exception> {
|
||||
val signedMessage = message.sign(room, server, fallbackSigningType = IdPrefix.STANDARD) ?: return Promise.ofFail(Error.SigningFailed)
|
||||
val jsonMessage = signedMessage.toJSON()
|
||||
val messageRequest = SendMessageRequest(
|
||||
data = signedMessage.base64EncodedData,
|
||||
signature = signedMessage.base64EncodedSignature,
|
||||
whisperTo = whisperTo,
|
||||
whisperMods = whisperMods,
|
||||
files = fileIds
|
||||
)
|
||||
val request = Request(
|
||||
verb = POST,
|
||||
room = room,
|
||||
server = server,
|
||||
endpoint = Endpoint.RoomMessage(room),
|
||||
parameters = jsonMessage
|
||||
parameters = messageRequest
|
||||
)
|
||||
return getResponseBodyJson(request).map { json ->
|
||||
@Suppress("UNCHECKED_CAST") val rawMessage = json as? Map<String, Any>
|
||||
@ -593,7 +611,7 @@ object OpenGroupApi {
|
||||
}
|
||||
)
|
||||
}
|
||||
val serverCapabilities = storage.getOpenGroupServer(server)
|
||||
val serverCapabilities = storage.getServerCapabilities(server)
|
||||
if (serverCapabilities.contains("blind")) {
|
||||
requests.add(
|
||||
if (lastInboxMessageId == null) {
|
||||
@ -690,6 +708,13 @@ object OpenGroupApi {
|
||||
}
|
||||
}
|
||||
|
||||
fun getDefaultServerCapabilities(): Promise<Capabilities, Exception> {
|
||||
return getCapabilities(defaultServer).map {
|
||||
MessagingModuleConfiguration.shared.storage.setServerCapabilities(defaultServer, it.capabilities)
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
fun getDefaultRoomsIfNeeded(): Promise<List<DefaultGroup>, Exception> {
|
||||
val storage = MessagingModuleConfiguration.shared.storage
|
||||
storage.setOpenGroupPublicKey(defaultServer, defaultServerPublicKey)
|
||||
@ -755,6 +780,13 @@ object OpenGroupApi {
|
||||
}
|
||||
}
|
||||
|
||||
fun getCapabilities(server: String): Promise<Capabilities, Exception> {
|
||||
val request = Request(verb = GET, room = null, server = server, endpoint = Endpoint.Capabilities)
|
||||
return getResponseBody(request).map { response ->
|
||||
JsonUtil.fromJson(response, Capabilities::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
fun getCapabilitiesAndRoomInfo(room: String, server: String): Promise<Pair<Capabilities, RoomInfo>, Exception> {
|
||||
val requests = mutableListOf<BatchRequestInfo<*>>(
|
||||
BatchRequestInfo(
|
||||
|
@ -49,8 +49,9 @@ data class OpenGroupMessage(
|
||||
if (base64EncodedData.isEmpty()) return null
|
||||
val userEdKeyPair = MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return null
|
||||
val openGroup = MessagingModuleConfiguration.shared.storage.getOpenGroup(room, server) ?: return null
|
||||
val serverCapabilities = MessagingModuleConfiguration.shared.storage.getServerCapabilities(server)
|
||||
val signature = when {
|
||||
openGroup.capabilities.contains("blind") -> {
|
||||
serverCapabilities.contains("blind") -> {
|
||||
val blindedKeyPair = SodiumUtilities.blindedKeyPair(openGroup.publicKey, userEdKeyPair) ?: return null
|
||||
SodiumUtilities.sogsSignature(
|
||||
decode(base64EncodedData),
|
||||
|
@ -205,9 +205,10 @@ object MessageSender {
|
||||
message.sentTimestamp = System.currentTimeMillis()
|
||||
}
|
||||
val openGroup = storage.getOpenGroup(message.threadID!!)
|
||||
val serverCapabilities = storage.getServerCapabilities(openGroup?.server!!)
|
||||
val userEdKeyPair = MessagingModuleConfiguration.shared.getUserED25519KeyPair()!!
|
||||
val blindedKeyPair = SodiumUtilities.blindedKeyPair(openGroup?.publicKey!!, userEdKeyPair)
|
||||
message.sender = if (openGroup.capabilities.contains("blind") && blindedKeyPair != null) {
|
||||
val blindedKeyPair = SodiumUtilities.blindedKeyPair(openGroup.publicKey, userEdKeyPair)
|
||||
message.sender = if (serverCapabilities.contains("blind") && blindedKeyPair != null) {
|
||||
SessionId(IdPrefix.BLINDED, blindedKeyPair.publicKey.asBytes).hexString
|
||||
} else {
|
||||
SessionId(IdPrefix.UN_BLINDED, userEdKeyPair.publicKey.asBytes).hexString
|
||||
@ -230,29 +231,9 @@ object MessageSender {
|
||||
}
|
||||
}
|
||||
when (destination) {
|
||||
is Destination.LegacyOpenGroup -> {
|
||||
message.recipient = "${destination.server}.${destination.roomToken}"
|
||||
// Validate the message
|
||||
if (message !is VisibleMessage || !message.isValid()) {
|
||||
throw Error.InvalidMessage
|
||||
}
|
||||
val messageBody = message.toProto()?.toByteArray()!!
|
||||
val plaintext = PushTransportDetails.getPaddedMessageBody(messageBody)
|
||||
val openGroupMessage = OpenGroupMessage(
|
||||
sender = message.sender,
|
||||
sentTimestamp = message.sentTimestamp!!,
|
||||
base64EncodedData = Base64.encodeBytes(plaintext),
|
||||
)
|
||||
OpenGroupApi.send(openGroupMessage, destination.roomToken, destination.server).success {
|
||||
message.openGroupServerMessageID = it.serverID
|
||||
handleSuccessfulMessageSend(message, destination, openGroupSentTimestamp = it.sentTimestamp)
|
||||
deferred.resolve(Unit)
|
||||
}.fail {
|
||||
handleFailure(it)
|
||||
}
|
||||
}
|
||||
is Destination.OpenGroup -> {
|
||||
message.recipient = "${destination.server}.${destination.roomToken}"
|
||||
val whisperMods = if (destination.whisperTo.isNullOrEmpty() && destination.whisperMods) "mods" else null
|
||||
message.recipient = "${destination.server}.${destination.roomToken}.${destination.whisperTo}.$whisperMods"
|
||||
// Validate the message
|
||||
if (message !is VisibleMessage || !message.isValid()) {
|
||||
throw Error.InvalidMessage
|
||||
@ -264,7 +245,7 @@ object MessageSender {
|
||||
sentTimestamp = message.sentTimestamp!!,
|
||||
base64EncodedData = Base64.encodeBytes(plaintext),
|
||||
)
|
||||
OpenGroupApi.send(openGroupMessage, destination.roomToken, destination.server).success {
|
||||
OpenGroupApi.send(openGroupMessage, destination.roomToken, destination.server, destination.whisperTo, destination.whisperMods, destination.fileIds).success {
|
||||
message.openGroupServerMessageID = it.serverID
|
||||
handleSuccessfulMessageSend(message, destination, openGroupSentTimestamp = it.sentTimestamp)
|
||||
deferred.resolve(Unit)
|
||||
|
@ -86,7 +86,7 @@ class OpenGroupPoller(private val server: String, private val executorService: S
|
||||
|
||||
private fun handleCapabilities(server: String, capabilities: OpenGroupApi.Capabilities) {
|
||||
val storage = MessagingModuleConfiguration.shared.storage
|
||||
storage.updateOpenGroupCapabilities(server, capabilities.capabilities)
|
||||
storage.setServerCapabilities(server, capabilities.capabilities)
|
||||
}
|
||||
|
||||
private fun handleRoomPollInfo(
|
||||
@ -105,7 +105,6 @@ class OpenGroupPoller(private val server: String, private val executorService: S
|
||||
name = pollInfo.details?.name ?: "",
|
||||
infoUpdates = pollInfo.details?.infoUpdates ?: 0,
|
||||
publicKey = publicKey,
|
||||
capabilities = listOf()
|
||||
)
|
||||
// - Open Group changes
|
||||
storage.updateOpenGroup(openGroup)
|
||||
|
Loading…
x
Reference in New Issue
Block a user