mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-12 00:17:42 +00:00
feat: more opengroup in chat manager, poller and API. refactor mentions to libsession
This commit is contained in:
@@ -71,8 +71,8 @@ interface StorageProtocol {
|
||||
fun setOpenGroupPublicKey(server: String, newValue: String)
|
||||
|
||||
// Open Group User Info
|
||||
fun setOpenGroupDisplayName(publicKey: String, channel: Long, server: String, displayName: String)
|
||||
fun getOpenGroupDisplayName(publicKey: String, channel: Long, server: String): String?
|
||||
fun setOpenGroupDisplayName(publicKey: String, room: String, server: String, displayName: String)
|
||||
fun getOpenGroupDisplayName(publicKey: String, room: String, server: String): String?
|
||||
|
||||
// Open Group Metadata
|
||||
|
||||
@@ -180,4 +180,7 @@ interface StorageProtocol {
|
||||
fun setOpenGroupProfilePictureURL(group: Long, server: String, newValue: String)
|
||||
fun getOpenGroupProfilePictureURL(group: Long, server: String): String?
|
||||
|
||||
fun setOpenGroupDisplayName(publicKey: String, channel: Long, server: String, displayName: String)
|
||||
fun getOpenGroupDisplayName(publicKey: String, channel: Long, server: String): String?
|
||||
|
||||
}
|
@@ -1,32 +1,153 @@
|
||||
package org.session.libsession.messaging.opengroups
|
||||
|
||||
import nl.komponents.kovenant.Promise
|
||||
import nl.komponents.kovenant.functional.bind
|
||||
import okhttp3.HttpUrl
|
||||
import org.session.libsession.messaging.MessagingConfiguration
|
||||
import org.session.libsession.messaging.opengroups.OpenGroupAPIV2.Error
|
||||
import org.session.libsession.messaging.utilities.DotNetAPI
|
||||
import org.session.libsignal.service.loki.api.utilities.HTTP
|
||||
import java.util.*
|
||||
|
||||
object OpenGroupAPIV2: DotNetAPI() {
|
||||
|
||||
enum class Error {
|
||||
GENERIC,
|
||||
PARSING_FAILED,
|
||||
DECRYPTION_FAILED,
|
||||
SIGNING_FAILED,
|
||||
INVALID_URL,
|
||||
NO_PUBLIC_KEY
|
||||
}
|
||||
object OpenGroupAPIV2 {
|
||||
|
||||
private val moderators: HashMap<String, HashMap<String, Set<String>>> = hashMapOf() // Server URL to (channel ID to set of moderator IDs)
|
||||
const val DEFAULT_SERVER = "https://sessionopengroup.com"
|
||||
const val DEFAULT_SERVER_PUBLIC_KEY = "658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231b"
|
||||
|
||||
fun getMessages(room: String, server: String): Promise<List<OpenGroupV2Message>, Exception> {
|
||||
sealed class Error : Exception() {
|
||||
object GENERIC : Error()
|
||||
object PARSING_FAILED : Error()
|
||||
object DECRYPTION_FAILED : Error()
|
||||
object SIGNING_FAILED : Error()
|
||||
object INVALID_URL : Error()
|
||||
object NO_PUBLIC_KEY : Error()
|
||||
}
|
||||
|
||||
data class Info(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val imageID: String
|
||||
)
|
||||
|
||||
data class Request(
|
||||
val verb: HTTP.Verb,
|
||||
val room: String?,
|
||||
val server: String,
|
||||
val endpoint: String,
|
||||
val queryParameters: Map<String, String>,
|
||||
val parameters: Any,
|
||||
val headers: Map<String, String>,
|
||||
val isAuthRequired: Boolean,
|
||||
// Always `true` under normal circumstances. You might want to disable
|
||||
// this when running over Lokinet.
|
||||
val useOnionRouting: Boolean
|
||||
)
|
||||
|
||||
private fun send(request: Request): Promise<Any, Exception> {
|
||||
val parsed = HttpUrl.parse(request.server) ?: return Promise.ofFail(Error.INVALID_URL)
|
||||
val urlBuilder = HttpUrl.Builder()
|
||||
.scheme(parsed.scheme())
|
||||
.host(parsed.host())
|
||||
.addPathSegment(request.endpoint)
|
||||
|
||||
for ((key, value) in request.queryParameters) {
|
||||
urlBuilder.addQueryParameter(key, value)
|
||||
}
|
||||
|
||||
fun execute(token: String?): Promise<Map<*, *>, Exception> {
|
||||
}
|
||||
return if (request.isAuthRequired) {
|
||||
getAuthToken(request.room!!, request.server).bind(::execute)
|
||||
} else {
|
||||
execute(null)
|
||||
}
|
||||
}
|
||||
|
||||
fun getAuthToken(room: String, server: String): Promise<String, Exception> {
|
||||
val storage = MessagingConfiguration.shared.storage
|
||||
return storage.getAuthToken(room, server)?.let {
|
||||
Promise.of(it)
|
||||
} ?: run {
|
||||
requestNewAuthToken(room, server)
|
||||
.bind { claimAuthToken(it, room, server) }
|
||||
.success { authToken ->
|
||||
storage.setAuthToken(room, server, authToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun requestNewAuthToken(room: String, server: String): Promise<String, Exception> {
|
||||
val (publicKey, _) = MessagingConfiguration.shared.storage.getUserKeyPair()
|
||||
?: return Promise.ofFail(Error.GENERIC)
|
||||
val queryParameters = mutableMapOf("public_key" to publicKey)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
fun claimAuthToken(authToken: String, room: String, server: String): Promise<String, Exception> {
|
||||
TODO("implement")
|
||||
}
|
||||
|
||||
data class Info(val id: String, val name: String, val imageId: String?)
|
||||
fun deleteAuthToken(room: String, server: String): Promise<Long, Exception> {
|
||||
TODO("implement")
|
||||
}
|
||||
|
||||
fun upload(file: ByteArray, room: String, server: String): Promise<Long, Exception> {
|
||||
TODO("implement")
|
||||
}
|
||||
|
||||
fun download(file: Long, room: String, server: String): Promise<ByteArray, Exception> {
|
||||
TODO("implement")
|
||||
}
|
||||
|
||||
fun send(message: OpenGroupMessageV2, room: String, server: String): Promise<OpenGroupMessageV2, Exception> {
|
||||
TODO("implement")
|
||||
}
|
||||
|
||||
fun getMessages(room: String, server: String): Promise<List<OpenGroupMessageV2>, Exception> {
|
||||
TODO("implement")
|
||||
}
|
||||
|
||||
fun deleteMessage(serverID: Long, room: String, server: String): Promise<Unit, Exception> {
|
||||
TODO("implement")
|
||||
}
|
||||
|
||||
fun getDeletedMessages(room: String, server: String): Promise<List<Long>, Exception> {
|
||||
TODO("implement")
|
||||
}
|
||||
|
||||
fun getModerators(room: String, server: String): Promise<List<String>, Exception> {
|
||||
TODO("implement")
|
||||
}
|
||||
|
||||
fun ban(publicKey: String, room: String, server: String): Promise<Unit, Exception> {
|
||||
TODO("implement")
|
||||
}
|
||||
|
||||
fun unban(publicKey: String, room: String, server: String): Promise<Unit, Exception> {
|
||||
TODO("implement")
|
||||
}
|
||||
|
||||
fun isUserModerator(publicKey: String, room: String, server: String): Promise<Boolean, Exception> {
|
||||
TODO("implement")
|
||||
}
|
||||
|
||||
fun getDefaultRoomsIfNeeded() {
|
||||
TODO("implement")
|
||||
}
|
||||
|
||||
fun getInfo(room: String, server: String): Promise<Info, Exception> {
|
||||
TODO("implement")
|
||||
}
|
||||
|
||||
fun getAllRooms(server: String): Promise<List<Info>, Exception> {
|
||||
TODO("implement")
|
||||
}
|
||||
|
||||
fun getMemberCount(room: String, server: String): Promise<Long, Exception> {
|
||||
TODO("implement")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun Error.errorDescription() = when (this) {
|
||||
Error.GENERIC -> "An error occurred."
|
||||
|
@@ -5,7 +5,7 @@ import org.session.libsignal.utilities.Base64
|
||||
import org.session.libsignal.utilities.logging.Log
|
||||
import org.whispersystems.curve25519.Curve25519
|
||||
|
||||
data class OpenGroupV2Message(
|
||||
data class OpenGroupMessageV2(
|
||||
val serverID: Long?,
|
||||
val sender: String?,
|
||||
val sentTimestamp: Long,
|
||||
@@ -20,7 +20,7 @@ data class OpenGroupV2Message(
|
||||
private val curve = Curve25519.getInstance(Curve25519.BEST)
|
||||
}
|
||||
|
||||
fun sign(): OpenGroupV2Message? {
|
||||
fun sign(): OpenGroupMessageV2? {
|
||||
if (base64EncodedData.isEmpty()) return null
|
||||
val (publicKey, privateKey) = MessagingConfiguration.shared.storage.getUserKeyPair() ?: return null
|
||||
|
||||
@@ -41,11 +41,21 @@ data class OpenGroupV2Message(
|
||||
serverID?.let { jsonMap["server_id"] = serverID }
|
||||
sender?.let { jsonMap["public_key"] = sender }
|
||||
base64EncodedSignature?.let { jsonMap["signature"] = base64EncodedSignature }
|
||||
return jsonMap
|
||||
}
|
||||
|
||||
fun fromJSON(json: Map<String, Any>): OpenGroupV2Message? {
|
||||
if (!json.containsKey("data") || !json.containsKey("timestamp")) return null
|
||||
|
||||
fun fromJSON(json: Map<String, Any>): OpenGroupMessageV2? {
|
||||
val base64EncodedData = json["data"] as? String ?: return null
|
||||
val sentTimestamp = json["timestamp"] as? Long ?: return null
|
||||
val serverID = json["server_id"] as? Long
|
||||
val sender = json["public_key"] as? String
|
||||
val base64EncodedSignature = json["signature"] as? String
|
||||
return OpenGroupMessageV2(serverID = serverID,
|
||||
sender = sender,
|
||||
sentTimestamp = sentTimestamp,
|
||||
base64EncodedData = base64EncodedData,
|
||||
base64EncodedSignature = base64EncodedSignature
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,3 @@
|
||||
package org.session.libsession.utilities.mentions
|
||||
|
||||
data class Mention(val publicKey: String, val displayName: String)
|
@@ -0,0 +1,62 @@
|
||||
package org.session.libsession.utilities.mentions
|
||||
|
||||
import org.session.libsession.messaging.MessagingConfiguration
|
||||
import org.session.libsignal.service.loki.database.LokiThreadDatabaseProtocol
|
||||
import org.session.libsignal.service.loki.database.LokiUserDatabaseProtocol
|
||||
|
||||
class MentionsManager(private val userPublicKey: String, private val threadDatabase: LokiThreadDatabaseProtocol,
|
||||
private val userDatabase: LokiUserDatabaseProtocol) {
|
||||
var userPublicKeyCache = mutableMapOf<Long, Set<String>>() // Thread ID to set of user hex encoded public keys
|
||||
|
||||
companion object {
|
||||
|
||||
lateinit var shared: MentionsManager
|
||||
|
||||
fun configureIfNeeded(userPublicKey: String, threadDatabase: LokiThreadDatabaseProtocol, userDatabase: LokiUserDatabaseProtocol) {
|
||||
if (::shared.isInitialized) { return; }
|
||||
shared = MentionsManager(userPublicKey, threadDatabase, userDatabase)
|
||||
}
|
||||
}
|
||||
|
||||
fun cache(publicKey: String, threadID: Long) {
|
||||
val cache = userPublicKeyCache[threadID]
|
||||
if (cache != null) {
|
||||
userPublicKeyCache[threadID] = cache.plus(publicKey)
|
||||
} else {
|
||||
userPublicKeyCache[threadID] = setOf( publicKey )
|
||||
}
|
||||
}
|
||||
|
||||
fun getMentionCandidates(query: String, threadID: Long): List<Mention> {
|
||||
// Prepare
|
||||
val cache = userPublicKeyCache[threadID] ?: return listOf()
|
||||
// Gather candidates
|
||||
val storage = MessagingConfiguration.shared.storage
|
||||
val publicChat = threadDatabase.getPublicChat(threadID)
|
||||
val openGroupV2 = storage.getV2OpenGroup(threadID.toString())
|
||||
var candidates: List<Mention> = cache.mapNotNull { publicKey ->
|
||||
val displayName: String?
|
||||
if (publicChat != null) {
|
||||
displayName = userDatabase.getServerDisplayName(publicChat.id, publicKey)
|
||||
} else if (openGroupV2 != null) {
|
||||
displayName = userDatabase.getServerDisplayName(openGroupV2.id, publicKey)
|
||||
} else {
|
||||
displayName = userDatabase.getDisplayName(publicKey)
|
||||
}
|
||||
if (displayName == null) { return@mapNotNull null }
|
||||
if (displayName.startsWith("Anonymous")) { return@mapNotNull null }
|
||||
Mention(publicKey, displayName)
|
||||
}
|
||||
candidates = candidates.filter { it.publicKey != userPublicKey }
|
||||
// Sort alphabetically first
|
||||
candidates.sortedBy { it.displayName }
|
||||
if (query.length >= 2) {
|
||||
// Filter out any non-matching candidates
|
||||
candidates = candidates.filter { it.displayName.toLowerCase().contains(query.toLowerCase()) }
|
||||
// Sort based on where in the candidate the query occurs
|
||||
candidates.sortedBy { it.displayName.toLowerCase().indexOf(query.toLowerCase()) }
|
||||
}
|
||||
// Return
|
||||
return candidates
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user