mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2025-03-14 09:45:42 +00:00
core, ios, android: add UserId to api commands (#1696)
This commit is contained in:
parent
fa9e0086f6
commit
bb0482104c
11 changed files with 341 additions and 189 deletions
|
@ -33,8 +33,6 @@ import kotlinx.coroutines.*
|
|||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.builtins.ListSerializer
|
||||
import kotlinx.serialization.builtins.nullable
|
||||
import kotlinx.serialization.json.*
|
||||
import java.util.Date
|
||||
|
||||
|
@ -405,7 +403,11 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
|||
}
|
||||
|
||||
suspend fun apiGetChats(): List<Chat> {
|
||||
val r = sendCmd(CC.ApiGetChats())
|
||||
val userId = chatModel.currentUser.value?.userId ?: run {
|
||||
Log.e(TAG, "apiGetChats: no current user")
|
||||
return emptyList()
|
||||
}
|
||||
val r = sendCmd(CC.ApiGetChats(userId))
|
||||
if (r is CR.ApiChats) return r.chats
|
||||
Log.e(TAG, "failed getting the list of chats: ${r.responseType} ${r.details}")
|
||||
AlertManager.shared.showAlertMsg(generalGetString(R.string.failed_to_parse_chats_title), generalGetString(R.string.contact_developers))
|
||||
|
@ -449,14 +451,22 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
|||
}
|
||||
|
||||
private suspend fun getUserSMPServers(): Pair<List<ServerCfg>, List<String>>? {
|
||||
val r = sendCmd(CC.GetUserSMPServers())
|
||||
val userId = chatModel.currentUser.value?.userId ?: run {
|
||||
Log.e(TAG, "getUserSMPServers: no current user")
|
||||
return null
|
||||
}
|
||||
val r = sendCmd(CC.APIGetUserSMPServers(userId))
|
||||
if (r is CR.UserSMPServers) return r.smpServers to r.presetSMPServers
|
||||
Log.e(TAG, "getUserSMPServers bad response: ${r.responseType} ${r.details}")
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun setUserSMPServers(smpServers: List<ServerCfg>): Boolean {
|
||||
val r = sendCmd(CC.SetUserSMPServers(smpServers))
|
||||
val userId = chatModel.currentUser.value?.userId ?: run {
|
||||
Log.e(TAG, "setUserSMPServers: no current user")
|
||||
return false
|
||||
}
|
||||
val r = sendCmd(CC.APISetUserSMPServers(userId, smpServers))
|
||||
return when (r) {
|
||||
is CR.CmdOk -> true
|
||||
else -> {
|
||||
|
@ -482,13 +492,15 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
|||
}
|
||||
|
||||
suspend fun getChatItemTTL(): ChatItemTTL {
|
||||
val r = sendCmd(CC.APIGetChatItemTTL())
|
||||
val userId = chatModel.currentUser.value?.userId ?: run { throw Exception("getChatItemTTL: no current user") }
|
||||
val r = sendCmd(CC.APIGetChatItemTTL(userId))
|
||||
if (r is CR.ChatItemTTL) return ChatItemTTL.fromSeconds(r.chatItemTTL)
|
||||
throw Exception("failed to get chat item TTL: ${r.responseType} ${r.details}")
|
||||
}
|
||||
|
||||
suspend fun setChatItemTTL(chatItemTTL: ChatItemTTL) {
|
||||
val r = sendCmd(CC.APISetChatItemTTL(chatItemTTL.seconds))
|
||||
val userId = chatModel.currentUser.value?.userId ?: run { throw Exception("setChatItemTTL: no current user") }
|
||||
val r = sendCmd(CC.APISetChatItemTTL(userId, chatItemTTL.seconds))
|
||||
if (r is CR.CmdOk) return
|
||||
throw Exception("failed to set chat item TTL: ${r.responseType} ${r.details}")
|
||||
}
|
||||
|
@ -587,7 +599,11 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
|||
|
||||
|
||||
suspend fun apiAddContact(): String? {
|
||||
val r = sendCmd(CC.AddContact())
|
||||
val userId = chatModel.currentUser.value?.userId ?: run {
|
||||
Log.e(TAG, "apiAddContact: no current user")
|
||||
return null
|
||||
}
|
||||
val r = sendCmd(CC.APIAddContact(userId))
|
||||
return when (r) {
|
||||
is CR.Invitation -> r.connReqInvitation
|
||||
else -> {
|
||||
|
@ -600,7 +616,11 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
|||
}
|
||||
|
||||
suspend fun apiConnect(connReq: String): Boolean {
|
||||
val r = sendCmd(CC.Connect(connReq))
|
||||
val userId = chatModel.currentUser.value?.userId ?: run {
|
||||
Log.e(TAG, "apiConnect: no current user")
|
||||
return false
|
||||
}
|
||||
val r = sendCmd(CC.APIConnect(userId, connReq))
|
||||
when {
|
||||
r is CR.SentConfirmation || r is CR.SentInvitation -> return true
|
||||
r is CR.ContactAlreadyExists -> {
|
||||
|
@ -663,14 +683,22 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
|||
}
|
||||
|
||||
suspend fun apiListContacts(): List<Contact>? {
|
||||
val r = sendCmd(CC.ListContacts())
|
||||
val userId = chatModel.currentUser.value?.userId ?: run {
|
||||
Log.e(TAG, "apiListContacts: no current user")
|
||||
return null
|
||||
}
|
||||
val r = sendCmd(CC.ApiListContacts(userId))
|
||||
if (r is CR.ContactsList) return r.contacts
|
||||
Log.e(TAG, "apiListContacts bad response: ${r.responseType} ${r.details}")
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun apiUpdateProfile(profile: Profile): Profile? {
|
||||
val r = sendCmd(CC.ApiUpdateProfile(profile))
|
||||
val userId = chatModel.currentUser.value?.userId ?: run {
|
||||
Log.e(TAG, "apiUpdateProfile: no current user")
|
||||
return null
|
||||
}
|
||||
val r = sendCmd(CC.ApiUpdateProfile(userId, profile))
|
||||
if (r is CR.UserProfileNoChange) return profile
|
||||
if (r is CR.UserProfileUpdated) return r.toProfile
|
||||
Log.e(TAG, "apiUpdateProfile bad response: ${r.responseType} ${r.details}")
|
||||
|
@ -706,7 +734,11 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
|||
}
|
||||
|
||||
suspend fun apiCreateUserAddress(): String? {
|
||||
val r = sendCmd(CC.CreateMyAddress())
|
||||
val userId = chatModel.currentUser.value?.userId ?: run {
|
||||
Log.e(TAG, "apiCreateUserAddress: no current user")
|
||||
return null
|
||||
}
|
||||
val r = sendCmd(CC.ApiCreateMyAddress(userId))
|
||||
return when (r) {
|
||||
is CR.UserContactLinkCreated -> r.connReqContact
|
||||
else -> {
|
||||
|
@ -719,14 +751,22 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
|||
}
|
||||
|
||||
suspend fun apiDeleteUserAddress(): Boolean {
|
||||
val r = sendCmd(CC.DeleteMyAddress())
|
||||
val userId = chatModel.currentUser.value?.userId ?: run {
|
||||
Log.e(TAG, "apiDeleteUserAddress: no current user")
|
||||
return false
|
||||
}
|
||||
val r = sendCmd(CC.ApiDeleteMyAddress(userId))
|
||||
if (r is CR.UserContactLinkDeleted) return true
|
||||
Log.e(TAG, "apiDeleteUserAddress bad response: ${r.responseType} ${r.details}")
|
||||
return false
|
||||
}
|
||||
|
||||
private suspend fun apiGetUserAddress(): UserContactLinkRec? {
|
||||
val r = sendCmd(CC.ShowMyAddress())
|
||||
val userId = chatModel.currentUser.value?.userId ?: run {
|
||||
Log.e(TAG, "apiGetUserAddress: no current user")
|
||||
return null
|
||||
}
|
||||
val r = sendCmd(CC.ApiShowMyAddress(userId))
|
||||
if (r is CR.UserContactLink) return r.contactLink
|
||||
if (r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorStore
|
||||
&& r.chatError.storeError is StoreError.UserContactLinkNotFound) {
|
||||
|
@ -737,7 +777,11 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
|||
}
|
||||
|
||||
suspend fun userAddressAutoAccept(autoAccept: AutoAccept?): UserContactLinkRec? {
|
||||
val r = sendCmd(CC.AddressAutoAccept(autoAccept))
|
||||
val userId = chatModel.currentUser.value?.userId ?: run {
|
||||
Log.e(TAG, "userAddressAutoAccept: no current user")
|
||||
return null
|
||||
}
|
||||
val r = sendCmd(CC.ApiAddressAutoAccept(userId, autoAccept))
|
||||
if (r is CR.UserContactLinkUpdated) return r.contactLink
|
||||
if (r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorStore
|
||||
&& r.chatError.storeError is StoreError.UserContactLinkNotFound) {
|
||||
|
@ -856,7 +900,11 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
|||
}
|
||||
|
||||
suspend fun apiNewGroup(p: GroupProfile): GroupInfo? {
|
||||
val r = sendCmd(CC.NewGroup(p))
|
||||
val userId = chatModel.currentUser.value?.userId ?: run {
|
||||
Log.e(TAG, "apiNewGroup: no current user")
|
||||
return null
|
||||
}
|
||||
val r = sendCmd(CC.ApiNewGroup(userId, p))
|
||||
if (r is CR.GroupCreated) return r.groupInfo
|
||||
Log.e(TAG, "apiNewGroup bad response: ${r.responseType} ${r.details}")
|
||||
return null
|
||||
|
@ -1549,12 +1597,12 @@ sealed class CC {
|
|||
class ApiImportArchive(val config: ArchiveConfig): CC()
|
||||
class ApiDeleteStorage: CC()
|
||||
class ApiStorageEncryption(val config: DBEncryptionConfig): CC()
|
||||
class ApiGetChats: CC()
|
||||
class ApiGetChats(val userId: Long): CC()
|
||||
class ApiGetChat(val type: ChatType, val id: Long, val pagination: ChatPagination, val search: String = ""): CC()
|
||||
class ApiSendMessage(val type: ChatType, val id: Long, val file: String?, val quotedItemId: Long?, val mc: MsgContent, val live: Boolean): CC()
|
||||
class ApiUpdateChatItem(val type: ChatType, val id: Long, val itemId: Long, val mc: MsgContent, val live: Boolean): CC()
|
||||
class ApiDeleteChatItem(val type: ChatType, val id: Long, val itemId: Long, val mode: CIDeleteMode): CC()
|
||||
class NewGroup(val groupProfile: GroupProfile): CC()
|
||||
class ApiNewGroup(val userId: Long, val groupProfile: GroupProfile): CC()
|
||||
class ApiAddMember(val groupId: Long, val contactId: Long, val memberRole: GroupMemberRole): CC()
|
||||
class ApiJoinGroup(val groupId: Long): CC()
|
||||
class ApiMemberRole(val groupId: Long, val memberId: Long, val memberRole: GroupMemberRole): CC()
|
||||
|
@ -1565,11 +1613,11 @@ sealed class CC {
|
|||
class APICreateGroupLink(val groupId: Long): CC()
|
||||
class APIDeleteGroupLink(val groupId: Long): CC()
|
||||
class APIGetGroupLink(val groupId: Long): CC()
|
||||
class GetUserSMPServers: CC()
|
||||
class SetUserSMPServers(val smpServers: List<ServerCfg>): CC()
|
||||
class APIGetUserSMPServers(val userId: Long): CC()
|
||||
class APISetUserSMPServers(val userId: Long, val smpServers: List<ServerCfg>): CC()
|
||||
class TestSMPServer(val smpServer: String): CC()
|
||||
class APISetChatItemTTL(val seconds: Long?): CC()
|
||||
class APIGetChatItemTTL: CC()
|
||||
class APISetChatItemTTL(val userId: Long, val seconds: Long?): CC()
|
||||
class APIGetChatItemTTL(val userId: Long): CC()
|
||||
class APISetNetworkConfig(val networkConfig: NetCfg): CC()
|
||||
class APIGetNetworkConfig: CC()
|
||||
class APISetChatSettings(val type: ChatType, val id: Long, val chatSettings: ChatSettings): CC()
|
||||
|
@ -1581,20 +1629,20 @@ sealed class CC {
|
|||
class APIGetGroupMemberCode(val groupId: Long, val groupMemberId: Long): CC()
|
||||
class APIVerifyContact(val contactId: Long, val connectionCode: String?): CC()
|
||||
class APIVerifyGroupMember(val groupId: Long, val groupMemberId: Long, val connectionCode: String?): CC()
|
||||
class AddContact: CC()
|
||||
class Connect(val connReq: String): CC()
|
||||
class APIAddContact(val userId: Long): CC()
|
||||
class APIConnect(val userId: Long, val connReq: String): CC()
|
||||
class ApiDeleteChat(val type: ChatType, val id: Long): CC()
|
||||
class ApiClearChat(val type: ChatType, val id: Long): CC()
|
||||
class ListContacts: CC()
|
||||
class ApiUpdateProfile(val profile: Profile): CC()
|
||||
class ApiListContacts(val userId: Long): CC()
|
||||
class ApiUpdateProfile(val userId: Long, val profile: Profile): CC()
|
||||
class ApiSetContactPrefs(val contactId: Long, val prefs: ChatPreferences): CC()
|
||||
class ApiParseMarkdown(val text: String): CC()
|
||||
class ApiSetContactAlias(val contactId: Long, val localAlias: String): CC()
|
||||
class ApiSetConnectionAlias(val connId: Long, val localAlias: String): CC()
|
||||
class CreateMyAddress: CC()
|
||||
class DeleteMyAddress: CC()
|
||||
class ShowMyAddress: CC()
|
||||
class AddressAutoAccept(val autoAccept: AutoAccept?): CC()
|
||||
class ApiCreateMyAddress(val userId: Long): CC()
|
||||
class ApiDeleteMyAddress(val userId: Long): CC()
|
||||
class ApiShowMyAddress(val userId: Long): CC()
|
||||
class ApiAddressAutoAccept(val userId: Long, val autoAccept: AutoAccept?): CC()
|
||||
class ApiSendCallInvitation(val contact: Contact, val callType: CallType): CC()
|
||||
class ApiRejectCall(val contact: Contact): CC()
|
||||
class ApiSendCallOffer(val contact: Contact, val callOffer: WebRTCCallOffer): CC()
|
||||
|
@ -1620,12 +1668,12 @@ sealed class CC {
|
|||
is ApiImportArchive -> "/_db import ${json.encodeToString(config)}"
|
||||
is ApiDeleteStorage -> "/_db delete"
|
||||
is ApiStorageEncryption -> "/_db encryption ${json.encodeToString(config)}"
|
||||
is ApiGetChats -> "/_get chats pcc=on"
|
||||
is ApiGetChats -> "/_get $userId chats pcc=on"
|
||||
is ApiGetChat -> "/_get chat ${chatRef(type, id)} ${pagination.cmdString}" + (if (search == "") "" else " search=$search")
|
||||
is ApiSendMessage -> "/_send ${chatRef(type, id)} live=${onOff(live)} json ${json.encodeToString(ComposedMessage(file, quotedItemId, mc))}"
|
||||
is ApiUpdateChatItem -> "/_update item ${chatRef(type, id)} $itemId live=${onOff(live)} ${mc.cmdString}"
|
||||
is ApiDeleteChatItem -> "/_delete item ${chatRef(type, id)} $itemId ${mode.deleteMode}"
|
||||
is NewGroup -> "/_group ${json.encodeToString(groupProfile)}"
|
||||
is ApiNewGroup -> "/_group $userId ${json.encodeToString(groupProfile)}"
|
||||
is ApiAddMember -> "/_add #$groupId $contactId ${memberRole.memberRole}"
|
||||
is ApiJoinGroup -> "/_join #$groupId"
|
||||
is ApiMemberRole -> "/_member role #$groupId $memberId ${memberRole.memberRole}"
|
||||
|
@ -1636,11 +1684,11 @@ sealed class CC {
|
|||
is APICreateGroupLink -> "/_create link #$groupId"
|
||||
is APIDeleteGroupLink -> "/_delete link #$groupId"
|
||||
is APIGetGroupLink -> "/_get link #$groupId"
|
||||
is GetUserSMPServers -> "/smp"
|
||||
is SetUserSMPServers -> "/_smp ${smpServersStr(smpServers)}"
|
||||
is APIGetUserSMPServers -> "/_smp $userId"
|
||||
is APISetUserSMPServers -> "/_smp $userId ${smpServersStr(smpServers)}"
|
||||
is TestSMPServer -> "/smp test $smpServer"
|
||||
is APISetChatItemTTL -> "/_ttl ${chatItemTTLStr(seconds)}"
|
||||
is APIGetChatItemTTL -> "/ttl"
|
||||
is APISetChatItemTTL -> "/_ttl $userId ${chatItemTTLStr(seconds)}"
|
||||
is APIGetChatItemTTL -> "/_ttl $userId"
|
||||
is APISetNetworkConfig -> "/_network ${json.encodeToString(networkConfig)}"
|
||||
is APIGetNetworkConfig -> "/network"
|
||||
is APISetChatSettings -> "/_settings ${chatRef(type, id)} ${json.encodeToString(chatSettings)}"
|
||||
|
@ -1652,20 +1700,20 @@ sealed class CC {
|
|||
is APIGetGroupMemberCode -> "/_get code #$groupId $groupMemberId"
|
||||
is APIVerifyContact -> "/_verify code @$contactId" + if (connectionCode != null) " $connectionCode" else ""
|
||||
is APIVerifyGroupMember -> "/_verify code #$groupId $groupMemberId" + if (connectionCode != null) " $connectionCode" else ""
|
||||
is AddContact -> "/connect"
|
||||
is Connect -> "/connect $connReq"
|
||||
is APIAddContact -> "/_connect $userId"
|
||||
is APIConnect -> "/_connect $userId $connReq"
|
||||
is ApiDeleteChat -> "/_delete ${chatRef(type, id)}"
|
||||
is ApiClearChat -> "/_clear chat ${chatRef(type, id)}"
|
||||
is ListContacts -> "/contacts"
|
||||
is ApiUpdateProfile -> "/_profile ${json.encodeToString(profile)}"
|
||||
is ApiListContacts -> "/_contacts $userId"
|
||||
is ApiUpdateProfile -> "/_profile $userId ${json.encodeToString(profile)}"
|
||||
is ApiSetContactPrefs -> "/_set prefs @$contactId ${json.encodeToString(prefs)}"
|
||||
is ApiParseMarkdown -> "/_parse $text"
|
||||
is ApiSetContactAlias -> "/_set alias @$contactId ${localAlias.trim()}"
|
||||
is ApiSetConnectionAlias -> "/_set alias :$connId ${localAlias.trim()}"
|
||||
is CreateMyAddress -> "/address"
|
||||
is DeleteMyAddress -> "/delete_address"
|
||||
is ShowMyAddress -> "/show_address"
|
||||
is AddressAutoAccept -> "/auto_accept ${AutoAccept.cmdString(autoAccept)}"
|
||||
is ApiCreateMyAddress -> "/_address $userId"
|
||||
is ApiDeleteMyAddress -> "/_delete_address $userId"
|
||||
is ApiShowMyAddress -> "/_show_address $userId"
|
||||
is ApiAddressAutoAccept -> "/_auto_accept $userId ${AutoAccept.cmdString(autoAccept)}"
|
||||
is ApiAcceptContact -> "/_accept $contactReqId"
|
||||
is ApiRejectContact -> "/_reject $contactReqId"
|
||||
is ApiSendCallInvitation -> "/_call invite @${contact.apiId} ${json.encodeToString(callType)}"
|
||||
|
@ -1697,7 +1745,7 @@ sealed class CC {
|
|||
is ApiSendMessage -> "apiSendMessage"
|
||||
is ApiUpdateChatItem -> "apiUpdateChatItem"
|
||||
is ApiDeleteChatItem -> "apiDeleteChatItem"
|
||||
is NewGroup -> "newGroup"
|
||||
is ApiNewGroup -> "apiNewGroup"
|
||||
is ApiAddMember -> "apiAddMember"
|
||||
is ApiJoinGroup -> "apiJoinGroup"
|
||||
is ApiMemberRole -> "apiMemberRole"
|
||||
|
@ -1708,8 +1756,8 @@ sealed class CC {
|
|||
is APICreateGroupLink -> "apiCreateGroupLink"
|
||||
is APIDeleteGroupLink -> "apiDeleteGroupLink"
|
||||
is APIGetGroupLink -> "apiGetGroupLink"
|
||||
is GetUserSMPServers -> "getUserSMPServers"
|
||||
is SetUserSMPServers -> "setUserSMPServers"
|
||||
is APIGetUserSMPServers -> "apiGetUserSMPServers"
|
||||
is APISetUserSMPServers -> "apiSetUserSMPServers"
|
||||
is TestSMPServer -> "testSMPServer"
|
||||
is APISetChatItemTTL -> "apiSetChatItemTTL"
|
||||
is APIGetChatItemTTL -> "apiGetChatItemTTL"
|
||||
|
@ -1724,20 +1772,20 @@ sealed class CC {
|
|||
is APIGetGroupMemberCode -> "apiGetGroupMemberCode"
|
||||
is APIVerifyContact -> "apiVerifyContact"
|
||||
is APIVerifyGroupMember -> "apiVerifyGroupMember"
|
||||
is AddContact -> "addContact"
|
||||
is Connect -> "connect"
|
||||
is APIAddContact -> "apiAddContact"
|
||||
is APIConnect -> "apiConnect"
|
||||
is ApiDeleteChat -> "apiDeleteChat"
|
||||
is ApiClearChat -> "apiClearChat"
|
||||
is ListContacts -> "listContacts"
|
||||
is ApiUpdateProfile -> "updateProfile"
|
||||
is ApiListContacts -> "apiListContacts"
|
||||
is ApiUpdateProfile -> "apiUpdateProfile"
|
||||
is ApiSetContactPrefs -> "apiSetContactPrefs"
|
||||
is ApiParseMarkdown -> "apiParseMarkdown"
|
||||
is ApiSetContactAlias -> "apiSetContactAlias"
|
||||
is ApiSetConnectionAlias -> "apiSetConnectionAlias"
|
||||
is CreateMyAddress -> "createMyAddress"
|
||||
is DeleteMyAddress -> "deleteMyAddress"
|
||||
is ShowMyAddress -> "showMyAddress"
|
||||
is AddressAutoAccept -> "addressAutoAccept"
|
||||
is ApiCreateMyAddress -> "apiCreateMyAddress"
|
||||
is ApiDeleteMyAddress -> "apiDeleteMyAddress"
|
||||
is ApiShowMyAddress -> "apiShowMyAddress"
|
||||
is ApiAddressAutoAccept -> "apiAddressAutoAccept"
|
||||
is ApiAcceptContact -> "apiAcceptContact"
|
||||
is ApiRejectContact -> "apiRejectContact"
|
||||
is ApiSendCallInvitation -> "apiSendCallInvitation"
|
||||
|
|
|
@ -189,7 +189,8 @@ func apiStorageEncryption(currentKey: String = "", newKey: String = "") async th
|
|||
}
|
||||
|
||||
func apiGetChats() throws -> [ChatData] {
|
||||
let r = chatSendCmdSync(.apiGetChats)
|
||||
guard let userId = ChatModel.shared.currentUser?.userId else { throw RuntimeError("apiGetChats: no current user") }
|
||||
let r = chatSendCmdSync(.apiGetChats(userId: userId))
|
||||
if case let .apiChats(chats) = r { return chats }
|
||||
throw r
|
||||
}
|
||||
|
@ -310,13 +311,15 @@ func apiDeleteToken(token: DeviceToken) async throws {
|
|||
}
|
||||
|
||||
func getUserSMPServers() throws -> ([ServerCfg], [String]) {
|
||||
let r = chatSendCmdSync(.getUserSMPServers)
|
||||
guard let userId = ChatModel.shared.currentUser?.userId else { throw RuntimeError("getUserSMPServers: no current user") }
|
||||
let r = chatSendCmdSync(.apiGetUserSMPServers(userId: userId))
|
||||
if case let .userSMPServers(smpServers, presetServers) = r { return (smpServers, presetServers) }
|
||||
throw r
|
||||
}
|
||||
|
||||
func setUserSMPServers(smpServers: [ServerCfg]) async throws {
|
||||
try await sendCommandOkResp(.setUserSMPServers(smpServers: smpServers))
|
||||
guard let userId = ChatModel.shared.currentUser?.userId else { throw RuntimeError("setUserSMPServers: no current user") }
|
||||
try await sendCommandOkResp(.apiSetUserSMPServers(userId: userId, smpServers: smpServers))
|
||||
}
|
||||
|
||||
func testSMPServer(smpServer: String) async throws -> Result<(), SMPTestFailure> {
|
||||
|
@ -331,13 +334,15 @@ func testSMPServer(smpServer: String) async throws -> Result<(), SMPTestFailure>
|
|||
}
|
||||
|
||||
func getChatItemTTL() throws -> ChatItemTTL {
|
||||
let r = chatSendCmdSync(.apiGetChatItemTTL)
|
||||
guard let userId = ChatModel.shared.currentUser?.userId else { throw RuntimeError("getChatItemTTL: no current user") }
|
||||
let r = chatSendCmdSync(.apiGetChatItemTTL(userId: userId))
|
||||
if case let .chatItemTTL(chatItemTTL) = r { return ChatItemTTL(chatItemTTL) }
|
||||
throw r
|
||||
}
|
||||
|
||||
func setChatItemTTL(_ chatItemTTL: ChatItemTTL) async throws {
|
||||
try await sendCommandOkResp(.apiSetChatItemTTL(seconds: chatItemTTL.seconds))
|
||||
guard let userId = ChatModel.shared.currentUser?.userId else { throw RuntimeError("setChatItemTTL: no current user") }
|
||||
try await sendCommandOkResp(.apiSetChatItemTTL(userId: userId, seconds: chatItemTTL.seconds))
|
||||
}
|
||||
|
||||
func getNetworkConfig() async throws -> NetCfg? {
|
||||
|
@ -403,14 +408,22 @@ func apiVerifyGroupMember(_ groupId: Int64, _ groupMemberId: Int64, connectionCo
|
|||
}
|
||||
|
||||
func apiAddContact() async -> String? {
|
||||
let r = await chatSendCmd(.addContact, bgTask: false)
|
||||
guard let userId = ChatModel.shared.currentUser?.userId else {
|
||||
logger.error("apiAddContact: no current user")
|
||||
return nil
|
||||
}
|
||||
let r = await chatSendCmd(.apiAddContact(userId: userId), bgTask: false)
|
||||
if case let .invitation(connReqInvitation) = r { return connReqInvitation }
|
||||
connectionErrorAlert(r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func apiConnect(connReq: String) async -> ConnReqType? {
|
||||
let r = await chatSendCmd(.connect(connReq: connReq))
|
||||
guard let userId = ChatModel.shared.currentUser?.userId else {
|
||||
logger.error("apiConnect: no current user")
|
||||
return nil
|
||||
}
|
||||
let r = await chatSendCmd(.apiConnect(userId: userId, connReq: connReq))
|
||||
let am = AlertManager.shared
|
||||
switch r {
|
||||
case .sentConfirmation: return .invitation
|
||||
|
@ -499,13 +512,15 @@ func clearChat(_ chat: Chat) async {
|
|||
}
|
||||
|
||||
func apiListContacts() throws -> [Contact] {
|
||||
let r = chatSendCmdSync(.listContacts)
|
||||
guard let userId = ChatModel.shared.currentUser?.userId else { throw RuntimeError("apiListContacts: no current user") }
|
||||
let r = chatSendCmdSync(.apiListContacts(userId: userId))
|
||||
if case let .contactsList(contacts) = r { return contacts }
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiUpdateProfile(profile: Profile) async throws -> Profile? {
|
||||
let r = await chatSendCmd(.apiUpdateProfile(profile: profile))
|
||||
guard let userId = ChatModel.shared.currentUser?.userId else { throw RuntimeError("apiUpdateProfile: no current user") }
|
||||
let r = await chatSendCmd(.apiUpdateProfile(userId: userId, profile: profile))
|
||||
switch r {
|
||||
case .userProfileNoChange: return nil
|
||||
case let .userProfileUpdated(_, toProfile): return toProfile
|
||||
|
@ -532,19 +547,22 @@ func apiSetConnectionAlias(connId: Int64, localAlias: String) async throws -> Pe
|
|||
}
|
||||
|
||||
func apiCreateUserAddress() async throws -> String {
|
||||
let r = await chatSendCmd(.createMyAddress)
|
||||
guard let userId = ChatModel.shared.currentUser?.userId else { throw RuntimeError("apiCreateUserAddress: no current user") }
|
||||
let r = await chatSendCmd(.apiCreateMyAddress(userId: userId))
|
||||
if case let .userContactLinkCreated(connReq) = r { return connReq }
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiDeleteUserAddress() async throws {
|
||||
let r = await chatSendCmd(.deleteMyAddress)
|
||||
guard let userId = ChatModel.shared.currentUser?.userId else { throw RuntimeError("apiDeleteUserAddress: no current user") }
|
||||
let r = await chatSendCmd(.apiDeleteMyAddress(userId: userId))
|
||||
if case .userContactLinkDeleted = r { return }
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiGetUserAddress() throws -> UserContactLink? {
|
||||
let r = chatSendCmdSync(.showMyAddress)
|
||||
guard let userId = ChatModel.shared.currentUser?.userId else { throw RuntimeError("apiGetUserAddress: no current user") }
|
||||
let r = chatSendCmdSync(.apiShowMyAddress(userId: userId))
|
||||
switch r {
|
||||
case let .userContactLink(contactLink): return contactLink
|
||||
case .chatCmdError(chatError: .errorStore(storeError: .userContactLinkNotFound)): return nil
|
||||
|
@ -553,7 +571,8 @@ func apiGetUserAddress() throws -> UserContactLink? {
|
|||
}
|
||||
|
||||
func userAddressAutoAccept(_ autoAccept: AutoAccept?) async throws -> UserContactLink? {
|
||||
let r = await chatSendCmd(.addressAutoAccept(autoAccept: autoAccept))
|
||||
guard let userId = ChatModel.shared.currentUser?.userId else { throw RuntimeError("userAddressAutoAccept: no current user") }
|
||||
let r = await chatSendCmd(.apiAddressAutoAccept(userId: userId, autoAccept: autoAccept))
|
||||
switch r {
|
||||
case let .userContactLinkUpdated(contactLink): return contactLink
|
||||
case .chatCmdError(chatError: .errorStore(storeError: .userContactLinkNotFound)): return nil
|
||||
|
@ -691,7 +710,8 @@ func apiEndCall(_ contact: Contact) async throws {
|
|||
}
|
||||
|
||||
func apiGetCallInvitations() throws -> [RcvCallInvitation] {
|
||||
let r = chatSendCmdSync(.apiGetCallInvitations)
|
||||
guard let userId = ChatModel.shared.currentUser?.userId else { throw RuntimeError("apiGetCallInvitations: no current user") }
|
||||
let r = chatSendCmdSync(.apiGetCallInvitations(userId: userId))
|
||||
if case let .callInvitations(invs) = r { return invs }
|
||||
throw r
|
||||
}
|
||||
|
@ -748,7 +768,8 @@ private func sendCommandOkResp(_ cmd: ChatCommand) async throws {
|
|||
}
|
||||
|
||||
func apiNewGroup(_ p: GroupProfile) throws -> GroupInfo {
|
||||
let r = chatSendCmdSync(.newGroup(groupProfile: p))
|
||||
guard let userId = ChatModel.shared.currentUser?.userId else { throw RuntimeError("apiNewGroup: no current user") }
|
||||
let r = chatSendCmdSync(.apiNewGroup(userId: userId, groupProfile: p))
|
||||
if case let .groupCreated(groupInfo) = r { return groupInfo }
|
||||
throw r
|
||||
}
|
||||
|
@ -1209,3 +1230,15 @@ private struct UserResponse: Decodable {
|
|||
var user: User?
|
||||
var error: String?
|
||||
}
|
||||
|
||||
struct RuntimeError: Error {
|
||||
let message: String
|
||||
|
||||
init(_ message: String) {
|
||||
self.message = message
|
||||
}
|
||||
|
||||
public var localizedDescription: String {
|
||||
return message
|
||||
}
|
||||
}
|
||||
|
|
|
@ -275,7 +275,11 @@ func apiSetIncognito(incognito: Bool) throws {
|
|||
}
|
||||
|
||||
func apiGetNtfMessage(nonce: String, encNtfInfo: String) -> NtfMessages? {
|
||||
let r = sendSimpleXCmd(.apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo))
|
||||
guard let user = apiGetActiveUser() else {
|
||||
logger.debug("no active user")
|
||||
return nil
|
||||
}
|
||||
let r = sendSimpleXCmd(.apiGetNtfMessage(userId: user.userId, nonce: nonce, encNtfInfo: encNtfInfo))
|
||||
if case let .ntfMessages(connEntity, msgTs, ntfMessages) = r {
|
||||
return NtfMessages(connEntity: connEntity, msgTs: msgTs, ntfMessages: ntfMessages)
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ public enum ChatCommand {
|
|||
case apiImportArchive(config: ArchiveConfig)
|
||||
case apiDeleteStorage
|
||||
case apiStorageEncryption(config: DBEncryptionConfig)
|
||||
case apiGetChats
|
||||
case apiGetChats(userId: Int64)
|
||||
case apiGetChat(type: ChatType, id: Int64, pagination: ChatPagination, search: String)
|
||||
case apiSendMessage(type: ChatType, id: Int64, file: String?, quotedItemId: Int64?, msg: MsgContent, live: Bool)
|
||||
case apiUpdateChatItem(type: ChatType, id: Int64, itemId: Int64, msg: MsgContent, live: Bool)
|
||||
|
@ -34,8 +34,8 @@ public enum ChatCommand {
|
|||
case apiRegisterToken(token: DeviceToken, notificationMode: NotificationsMode)
|
||||
case apiVerifyToken(token: DeviceToken, nonce: String, code: String)
|
||||
case apiDeleteToken(token: DeviceToken)
|
||||
case apiGetNtfMessage(nonce: String, encNtfInfo: String)
|
||||
case newGroup(groupProfile: GroupProfile)
|
||||
case apiGetNtfMessage(userId: Int64, nonce: String, encNtfInfo: String)
|
||||
case apiNewGroup(userId: Int64, groupProfile: GroupProfile)
|
||||
case apiAddMember(groupId: Int64, contactId: Int64, memberRole: GroupMemberRole)
|
||||
case apiJoinGroup(groupId: Int64)
|
||||
case apiMemberRole(groupId: Int64, memberId: Int64, memberRole: GroupMemberRole)
|
||||
|
@ -46,11 +46,11 @@ public enum ChatCommand {
|
|||
case apiCreateGroupLink(groupId: Int64)
|
||||
case apiDeleteGroupLink(groupId: Int64)
|
||||
case apiGetGroupLink(groupId: Int64)
|
||||
case getUserSMPServers
|
||||
case setUserSMPServers(smpServers: [ServerCfg])
|
||||
case apiGetUserSMPServers(userId: Int64)
|
||||
case apiSetUserSMPServers(userId: Int64, smpServers: [ServerCfg])
|
||||
case testSMPServer(smpServer: String)
|
||||
case apiSetChatItemTTL(seconds: Int64?)
|
||||
case apiGetChatItemTTL
|
||||
case apiSetChatItemTTL(userId: Int64, seconds: Int64?)
|
||||
case apiGetChatItemTTL(userId: Int64)
|
||||
case apiSetNetworkConfig(networkConfig: NetCfg)
|
||||
case apiGetNetworkConfig
|
||||
case apiSetChatSettings(type: ChatType, id: Int64, chatSettings: ChatSettings)
|
||||
|
@ -62,19 +62,19 @@ public enum ChatCommand {
|
|||
case apiGetGroupMemberCode(groupId: Int64, groupMemberId: Int64)
|
||||
case apiVerifyContact(contactId: Int64, connectionCode: String?)
|
||||
case apiVerifyGroupMember(groupId: Int64, groupMemberId: Int64, connectionCode: String?)
|
||||
case addContact
|
||||
case connect(connReq: String)
|
||||
case apiAddContact(userId: Int64)
|
||||
case apiConnect(userId: Int64, connReq: String)
|
||||
case apiDeleteChat(type: ChatType, id: Int64)
|
||||
case apiClearChat(type: ChatType, id: Int64)
|
||||
case listContacts
|
||||
case apiUpdateProfile(profile: Profile)
|
||||
case apiListContacts(userId: Int64)
|
||||
case apiUpdateProfile(userId: Int64, profile: Profile)
|
||||
case apiSetContactPrefs(contactId: Int64, preferences: Preferences)
|
||||
case apiSetContactAlias(contactId: Int64, localAlias: String)
|
||||
case apiSetConnectionAlias(connId: Int64, localAlias: String)
|
||||
case createMyAddress
|
||||
case deleteMyAddress
|
||||
case showMyAddress
|
||||
case addressAutoAccept(autoAccept: AutoAccept?)
|
||||
case apiCreateMyAddress(userId: Int64)
|
||||
case apiDeleteMyAddress(userId: Int64)
|
||||
case apiShowMyAddress(userId: Int64)
|
||||
case apiAddressAutoAccept(userId: Int64, autoAccept: AutoAccept?)
|
||||
case apiAcceptContact(contactReqId: Int64)
|
||||
case apiRejectContact(contactReqId: Int64)
|
||||
// WebRTC calls
|
||||
|
@ -84,7 +84,7 @@ public enum ChatCommand {
|
|||
case apiSendCallAnswer(contact: Contact, answer: WebRTCSession)
|
||||
case apiSendCallExtraInfo(contact: Contact, extraInfo: WebRTCExtraInfo)
|
||||
case apiEndCall(contact: Contact)
|
||||
case apiGetCallInvitations
|
||||
case apiGetCallInvitations(userId: Int64)
|
||||
case apiCallStatus(contact: Contact, callStatus: WebRTCCallStatus)
|
||||
case apiChatRead(type: ChatType, id: Int64, itemRange: (Int64, Int64))
|
||||
case apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool)
|
||||
|
@ -106,7 +106,7 @@ public enum ChatCommand {
|
|||
case let .apiImportArchive(cfg): return "/_db import \(encodeJSON(cfg))"
|
||||
case .apiDeleteStorage: return "/_db delete"
|
||||
case let .apiStorageEncryption(cfg): return "/_db encryption \(encodeJSON(cfg))"
|
||||
case .apiGetChats: return "/_get chats pcc=on"
|
||||
case let .apiGetChats(userId): return "/_get chats \(userId) pcc=on"
|
||||
case let .apiGetChat(type, id, pagination, search): return "/_get chat \(ref(type, id)) \(pagination.cmdString)" +
|
||||
(search == "" ? "" : " search=\(search)")
|
||||
case let .apiSendMessage(type, id, file, quotedItemId, mc, live):
|
||||
|
@ -118,8 +118,8 @@ public enum ChatCommand {
|
|||
case let .apiRegisterToken(token, notificationMode): return "/_ntf register \(token.cmdString) \(notificationMode.rawValue)"
|
||||
case let .apiVerifyToken(token, nonce, code): return "/_ntf verify \(token.cmdString) \(nonce) \(code)"
|
||||
case let .apiDeleteToken(token): return "/_ntf delete \(token.cmdString)"
|
||||
case let .apiGetNtfMessage(nonce, encNtfInfo): return "/_ntf message \(nonce) \(encNtfInfo)"
|
||||
case let .newGroup(groupProfile): return "/_group \(encodeJSON(groupProfile))"
|
||||
case let .apiGetNtfMessage(userId, nonce, encNtfInfo): return "/_ntf message \(userId) \(nonce) \(encNtfInfo)"
|
||||
case let .apiNewGroup(userId, groupProfile): return "/_group \(userId) \(encodeJSON(groupProfile))"
|
||||
case let .apiAddMember(groupId, contactId, memberRole): return "/_add #\(groupId) \(contactId) \(memberRole)"
|
||||
case let .apiJoinGroup(groupId): return "/_join #\(groupId)"
|
||||
case let .apiMemberRole(groupId, memberId, memberRole): return "/_member role #\(groupId) \(memberId) \(memberRole.rawValue)"
|
||||
|
@ -130,11 +130,11 @@ public enum ChatCommand {
|
|||
case let .apiCreateGroupLink(groupId): return "/_create link #\(groupId)"
|
||||
case let .apiDeleteGroupLink(groupId): return "/_delete link #\(groupId)"
|
||||
case let .apiGetGroupLink(groupId): return "/_get link #\(groupId)"
|
||||
case .getUserSMPServers: return "/smp"
|
||||
case let .setUserSMPServers(smpServers): return "/_smp \(smpServersStr(smpServers: smpServers))"
|
||||
case let .apiGetUserSMPServers(userId): return "/_smp \(userId)"
|
||||
case let .apiSetUserSMPServers(userId, smpServers): return "/_smp \(userId) \(smpServersStr(smpServers: smpServers))"
|
||||
case let .testSMPServer(smpServer): return "/smp test \(smpServer)"
|
||||
case let .apiSetChatItemTTL(seconds): return "/_ttl \(chatItemTTLStr(seconds: seconds))"
|
||||
case .apiGetChatItemTTL: return "/ttl"
|
||||
case let .apiSetChatItemTTL(userId, seconds): return "/_ttl \(userId) \(chatItemTTLStr(seconds: seconds))"
|
||||
case let .apiGetChatItemTTL(userId): return "/_ttl \(userId)"
|
||||
case let .apiSetNetworkConfig(networkConfig): return "/_network \(encodeJSON(networkConfig))"
|
||||
case .apiGetNetworkConfig: return "/network"
|
||||
case let .apiSetChatSettings(type, id, chatSettings): return "/_settings \(ref(type, id)) \(encodeJSON(chatSettings))"
|
||||
|
@ -148,19 +148,19 @@ public enum ChatCommand {
|
|||
case let .apiVerifyContact(contactId, .none): return "/_verify code @\(contactId)"
|
||||
case let .apiVerifyGroupMember(groupId, groupMemberId, .some(connectionCode)): return "/_verify code #\(groupId) \(groupMemberId) \(connectionCode)"
|
||||
case let .apiVerifyGroupMember(groupId, groupMemberId, .none): return "/_verify code #\(groupId) \(groupMemberId)"
|
||||
case .addContact: return "/connect"
|
||||
case let .connect(connReq): return "/connect \(connReq)"
|
||||
case let .apiAddContact(userId): return "/_connect \(userId)"
|
||||
case let .apiConnect(userId, connReq): return "/_connect \(userId) \(connReq)"
|
||||
case let .apiDeleteChat(type, id): return "/_delete \(ref(type, id))"
|
||||
case let .apiClearChat(type, id): return "/_clear chat \(ref(type, id))"
|
||||
case .listContacts: return "/contacts"
|
||||
case let .apiUpdateProfile(profile): return "/_profile \(encodeJSON(profile))"
|
||||
case let .apiListContacts(userId): return "/_contacts \(userId)"
|
||||
case let .apiUpdateProfile(userId, profile): return "/_profile \(userId) \(encodeJSON(profile))"
|
||||
case let .apiSetContactPrefs(contactId, preferences): return "/_set prefs @\(contactId) \(encodeJSON(preferences))"
|
||||
case let .apiSetContactAlias(contactId, localAlias): return "/_set alias @\(contactId) \(localAlias.trimmingCharacters(in: .whitespaces))"
|
||||
case let .apiSetConnectionAlias(connId, localAlias): return "/_set alias :\(connId) \(localAlias.trimmingCharacters(in: .whitespaces))"
|
||||
case .createMyAddress: return "/address"
|
||||
case .deleteMyAddress: return "/delete_address"
|
||||
case .showMyAddress: return "/show_address"
|
||||
case let .addressAutoAccept(autoAccept): return "/auto_accept \(AutoAccept.cmdString(autoAccept))"
|
||||
case let .apiCreateMyAddress(userId): return "/_address \(userId)"
|
||||
case let .apiDeleteMyAddress(userId): return "/_delete_address \(userId)"
|
||||
case let .apiShowMyAddress(userId): return "/_show_address \(userId)"
|
||||
case let .apiAddressAutoAccept(userId, autoAccept): return "/_auto_accept \(userId) \(AutoAccept.cmdString(autoAccept))"
|
||||
case let .apiAcceptContact(contactReqId): return "/_accept \(contactReqId)"
|
||||
case let .apiRejectContact(contactReqId): return "/_reject \(contactReqId)"
|
||||
case let .apiSendCallInvitation(contact, callType): return "/_call invite @\(contact.apiId) \(encodeJSON(callType))"
|
||||
|
@ -169,7 +169,7 @@ public enum ChatCommand {
|
|||
case let .apiSendCallAnswer(contact, answer): return "/_call answer @\(contact.apiId) \(encodeJSON(answer))"
|
||||
case let .apiSendCallExtraInfo(contact, extraInfo): return "/_call extra @\(contact.apiId) \(encodeJSON(extraInfo))"
|
||||
case let .apiEndCall(contact): return "/_call end @\(contact.apiId)"
|
||||
case .apiGetCallInvitations: return "/_call get"
|
||||
case let .apiGetCallInvitations(userId): return "/_call get \(userId)"
|
||||
case let .apiCallStatus(contact, callStatus): return "/_call status @\(contact.apiId) \(callStatus.rawValue)"
|
||||
case let .apiChatRead(type, id, itemRange: (from, to)): return "/_read chat \(ref(type, id)) from=\(from) to=\(to)"
|
||||
case let .apiChatUnread(type, id, unreadChat): return "/_unread chat \(ref(type, id)) \(onOff(unreadChat))"
|
||||
|
@ -204,7 +204,7 @@ public enum ChatCommand {
|
|||
case .apiVerifyToken: return "apiVerifyToken"
|
||||
case .apiDeleteToken: return "apiDeleteToken"
|
||||
case .apiGetNtfMessage: return "apiGetNtfMessage"
|
||||
case .newGroup: return "newGroup"
|
||||
case .apiNewGroup: return "apiNewGroup"
|
||||
case .apiAddMember: return "apiAddMember"
|
||||
case .apiJoinGroup: return "apiJoinGroup"
|
||||
case .apiMemberRole: return "apiMemberRole"
|
||||
|
@ -215,8 +215,8 @@ public enum ChatCommand {
|
|||
case .apiCreateGroupLink: return "apiCreateGroupLink"
|
||||
case .apiDeleteGroupLink: return "apiDeleteGroupLink"
|
||||
case .apiGetGroupLink: return "apiGetGroupLink"
|
||||
case .getUserSMPServers: return "getUserSMPServers"
|
||||
case .setUserSMPServers: return "setUserSMPServers"
|
||||
case .apiGetUserSMPServers: return "apiGetUserSMPServers"
|
||||
case .apiSetUserSMPServers: return "apiSetUserSMPServers"
|
||||
case .testSMPServer: return "testSMPServer"
|
||||
case .apiSetChatItemTTL: return "apiSetChatItemTTL"
|
||||
case .apiGetChatItemTTL: return "apiGetChatItemTTL"
|
||||
|
@ -231,19 +231,19 @@ public enum ChatCommand {
|
|||
case .apiGetGroupMemberCode: return "apiGetGroupMemberCode"
|
||||
case .apiVerifyContact: return "apiVerifyContact"
|
||||
case .apiVerifyGroupMember: return "apiVerifyGroupMember"
|
||||
case .addContact: return "addContact"
|
||||
case .connect: return "connect"
|
||||
case .apiAddContact: return "apiAddContact"
|
||||
case .apiConnect: return "apiConnect"
|
||||
case .apiDeleteChat: return "apiDeleteChat"
|
||||
case .apiClearChat: return "apiClearChat"
|
||||
case .listContacts: return "listContacts"
|
||||
case .apiListContacts: return "apiListContacts"
|
||||
case .apiUpdateProfile: return "apiUpdateProfile"
|
||||
case .apiSetContactPrefs: return "apiSetContactPrefs"
|
||||
case .apiSetContactAlias: return "apiSetContactAlias"
|
||||
case .apiSetConnectionAlias: return "apiSetConnectionAlias"
|
||||
case .createMyAddress: return "createMyAddress"
|
||||
case .deleteMyAddress: return "deleteMyAddress"
|
||||
case .showMyAddress: return "showMyAddress"
|
||||
case .addressAutoAccept: return "addressAutoAccept"
|
||||
case .apiCreateMyAddress: return "apiCreateMyAddress"
|
||||
case .apiDeleteMyAddress: return "apiDeleteMyAddress"
|
||||
case .apiShowMyAddress: return "apiShowMyAddress"
|
||||
case .apiAddressAutoAccept: return "apiAddressAutoAccept"
|
||||
case .apiAcceptContact: return "apiAcceptContact"
|
||||
case .apiRejectContact: return "apiRejectContact"
|
||||
case .apiSendCallInvitation: return "apiSendCallInvitation"
|
||||
|
|
|
@ -10,7 +10,7 @@ import Foundation
|
|||
import SwiftUI
|
||||
|
||||
public struct User: Decodable, NamedChat {
|
||||
var userId: Int64
|
||||
public var userId: Int64
|
||||
var userContactId: Int64
|
||||
var localDisplayName: ContactName
|
||||
public var profile: LocalProfile
|
||||
|
|
|
@ -39,10 +39,10 @@ mySquaringBot _user cc = do
|
|||
race_ (forever $ void getLine) . forever $ do
|
||||
(_, resp) <- atomically . readTBQueue $ outputQ cc
|
||||
case resp of
|
||||
CRContactConnected contact _ -> do
|
||||
CRContactConnected _ contact _ -> do
|
||||
contactConnected contact
|
||||
void . sendMsg contact $ "Hello! I am a simple squaring bot - if you send me a number, I will calculate its square"
|
||||
CRNewChatItem (AChatItem _ SMDRcv (DirectChat contact) ChatItem {content}) -> do
|
||||
CRNewChatItem _ (AChatItem _ SMDRcv (DirectChat contact) ChatItem {content}) -> do
|
||||
let msg = T.unpack $ ciContentToText content
|
||||
number_ = readMaybe msg :: Maybe Integer
|
||||
void . sendMsg contact $ case number_ of
|
||||
|
|
|
@ -95,11 +95,11 @@ runChatServer ChatServerConfig {chatPort, clientQSize} cc = do
|
|||
Left e -> sendError (Just corrId) e
|
||||
Nothing -> sendError Nothing "invalid request"
|
||||
where
|
||||
sendError corrId e = atomically $ writeTBQueue sndQ ChatSrvResponse {corrId, resp = chatCmdError e}
|
||||
sendError corrId e = atomically $ writeTBQueue sndQ ChatSrvResponse {corrId, resp = chatCmdError Nothing e}
|
||||
processCommand (corrId, cmd) =
|
||||
runReaderT (runExceptT $ processChatCommand cmd) cc >>= \case
|
||||
Right resp -> response resp
|
||||
Left e -> response $ CRChatCmdError e
|
||||
Left e -> response $ CRChatCmdError Nothing e
|
||||
where
|
||||
response resp = pure ChatSrvResponse {corrId = Just corrId, resp}
|
||||
clientDisconnected _ = pure ()
|
||||
|
|
|
@ -308,7 +308,8 @@ processChatCommand = \case
|
|||
APIStorageEncryption cfg -> withStoreChanged $ sqlCipherExport cfg
|
||||
ExecChatStoreSQL query -> CRSQLResult <$> withStore' (`execSQL` query)
|
||||
ExecAgentStoreSQL query -> CRSQLResult <$> withAgent (`execAgentStoreSQL` query)
|
||||
APIGetChats withPCC -> withUser' $ \user -> do
|
||||
APIGetChats cmdUserId withPCC -> withUser' $ \user -> do
|
||||
checkCorrectCmdUser cmdUserId user
|
||||
chats <- withStore' $ \db -> getChatPreviews db user withPCC
|
||||
pure $ CRApiChats user chats
|
||||
APIGetChat (ChatRef cType cId) pagination search -> withUser $ \user -> case cType of
|
||||
|
@ -716,7 +717,8 @@ processChatCommand = \case
|
|||
(SndMessage {msgId}, _) <- sendDirectContactMessage ct (XCallEnd callId)
|
||||
updateCallItemStatus userId ct call WCSDisconnected $ Just msgId
|
||||
pure Nothing
|
||||
APIGetCallInvitations -> withUser $ \user -> do
|
||||
APIGetCallInvitations cmdUserId -> withUser $ \user -> do
|
||||
checkCorrectCmdUser cmdUserId user
|
||||
calls <- asks currentCalls >>= readTVarIO
|
||||
let invs = mapMaybe callInvitation $ M.elems calls
|
||||
rcvCallInvitations <- mapM (rcvCallInvitation user) invs
|
||||
|
@ -731,7 +733,9 @@ processChatCommand = \case
|
|||
APICallStatus contactId receivedStatus ->
|
||||
withCurrentCall contactId $ \userId ct call ->
|
||||
updateCallItemStatus userId ct call receivedStatus Nothing $> Just call
|
||||
APIUpdateProfile profile -> withUser (`updateProfile` profile)
|
||||
APIUpdateProfile cmdUserId profile -> withUser $ \user -> do
|
||||
checkCorrectCmdUser cmdUserId user
|
||||
updateProfile user profile
|
||||
APISetContactPrefs contactId prefs' -> withUser $ \user -> do
|
||||
ct <- withStore $ \db -> getContact db user contactId
|
||||
updateContactPrefs user ct prefs'
|
||||
|
@ -752,26 +756,34 @@ processChatCommand = \case
|
|||
pure $ CRNtfTokenStatus tokenStatus
|
||||
APIVerifyToken token nonce code -> withUser $ \_ -> withAgent (\a -> verifyNtfToken a token nonce code) $> CRCmdOk Nothing
|
||||
APIDeleteToken token -> withUser $ \_ -> withAgent (`deleteNtfToken` token) $> CRCmdOk Nothing
|
||||
APIGetNtfMessage nonce encNtfInfo -> withUser $ \user -> do
|
||||
APIGetNtfMessage cmdUserId nonce encNtfInfo -> withUser $ \user -> do
|
||||
checkCorrectCmdUser cmdUserId user
|
||||
(NotificationInfo {ntfConnId, ntfMsgMeta}, msgs) <- withAgent $ \a -> getNotificationMessage a nonce encNtfInfo
|
||||
let ntfMessages = map (\SMP.SMPMsgMeta {msgTs, msgFlags} -> NtfMsgInfo {msgTs = systemToUTCTime msgTs, msgFlags}) msgs
|
||||
msgTs' = systemToUTCTime . (SMP.msgTs :: SMP.NMsgMeta -> SystemTime) <$> ntfMsgMeta
|
||||
connEntity <- withStore (\db -> Just <$> getConnectionEntity db user (AgentConnId ntfConnId)) `catchError` \_ -> pure Nothing
|
||||
pure CRNtfMessages {user, connEntity, msgTs = msgTs', ntfMessages}
|
||||
GetUserSMPServers -> withUser $ \user -> do
|
||||
APIGetUserSMPServers cmdUserId -> withUser $ \user -> do
|
||||
checkCorrectCmdUser cmdUserId user
|
||||
ChatConfig {defaultServers = InitialAgentServers {smp = defaultSMPServers}} <- asks config
|
||||
smpServers <- withStore' (`getSMPServers` user)
|
||||
let smpServers' = fromMaybe (L.map toServerCfg defaultSMPServers) $ nonEmpty smpServers
|
||||
pure $ CRUserSMPServers user smpServers' defaultSMPServers
|
||||
where
|
||||
toServerCfg server = ServerCfg {server, preset = True, tested = Nothing, enabled = True}
|
||||
SetUserSMPServers (SMPServersConfig smpServers) -> withUser $ \user -> withChatLock "setUserSMPServers" $ do
|
||||
GetUserSMPServers -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APIGetUserSMPServers userId
|
||||
APISetUserSMPServers cmdUserId (SMPServersConfig smpServers) -> withUser $ \user -> withChatLock "setUserSMPServers" $ do
|
||||
checkCorrectCmdUser cmdUserId user
|
||||
withStore $ \db -> overwriteSMPServers db user smpServers
|
||||
cfg <- asks config
|
||||
withAgent $ \a -> setSMPServers a $ activeAgentServers cfg smpServers
|
||||
pure $ CRCmdOk (Just user)
|
||||
SetUserSMPServers smpServersConfig -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APISetUserSMPServers userId smpServersConfig
|
||||
TestSMPServer smpServer -> CRSmpTestResult <$> withAgent (`testSMPServerConnection` smpServer)
|
||||
APISetChatItemTTL newTTL_ -> withUser' $ \user ->
|
||||
APISetChatItemTTL cmdUserId newTTL_ -> withUser' $ \user -> do
|
||||
checkCorrectCmdUser cmdUserId user
|
||||
checkStoreNotChanged $
|
||||
withChatLock "setChatItemTTL" $ do
|
||||
case newTTL_ of
|
||||
|
@ -786,9 +798,14 @@ processChatCommand = \case
|
|||
withStore' $ \db -> setChatItemTTL db user newTTL_
|
||||
whenM chatStarted $ setExpireCIs True
|
||||
pure $ CRCmdOk (Just user)
|
||||
APIGetChatItemTTL -> withUser $ \user -> do
|
||||
SetChatItemTTL newTTL_ -> withUser' $ \User {userId} -> do
|
||||
processChatCommand $ APISetChatItemTTL userId newTTL_
|
||||
APIGetChatItemTTL cmdUserId -> withUser $ \user -> do
|
||||
checkCorrectCmdUser cmdUserId user
|
||||
ttl <- withStore' (`getChatItemTTL` user)
|
||||
pure $ CRChatItemTTL user ttl
|
||||
GetChatItemTTL -> withUser' $ \User {userId} -> do
|
||||
processChatCommand $ APIGetChatItemTTL userId
|
||||
APISetNetworkConfig cfg -> withUser' $ \_ -> withAgent (`setNetworkConfig` cfg) $> CRCmdOk Nothing
|
||||
APIGetNetworkConfig -> withUser' $ \_ -> do
|
||||
networkConfig <- withAgent getNetworkConfig
|
||||
|
@ -878,7 +895,8 @@ processChatCommand = \case
|
|||
VerifyGroupMember gName mName code -> withMemberName gName mName $ \gId mId -> APIVerifyGroupMember gId mId code
|
||||
ChatHelp section -> pure $ CRChatHelp section
|
||||
Welcome -> withUser $ pure . CRWelcome
|
||||
AddContact -> withUser $ \user@User {userId} -> withChatLock "addContact" . procCmd $ do
|
||||
APIAddContact cmdUserId -> withUser $ \user@User {userId} -> withChatLock "addContact" . procCmd $ do
|
||||
checkCorrectCmdUser cmdUserId user
|
||||
-- [incognito] generate profile for connection
|
||||
incognito <- readTVarIO =<< asks incognitoMode
|
||||
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
|
||||
|
@ -886,7 +904,10 @@ processChatCommand = \case
|
|||
conn <- withStore' $ \db -> createDirectConnection db userId connId cReq ConnNew incognitoProfile
|
||||
toView $ CRNewContactConnection user conn
|
||||
pure $ CRInvitation user cReq
|
||||
Connect (Just (ACR SCMInvitation cReq)) -> withUser $ \user@User {userId} -> withChatLock "connect" . procCmd $ do
|
||||
AddContact -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APIAddContact userId
|
||||
APIConnect cmdUserId (Just (ACR SCMInvitation cReq)) -> withUser $ \user@User {userId} -> withChatLock "connect" . procCmd $ do
|
||||
checkCorrectCmdUser cmdUserId user
|
||||
-- [incognito] generate profile to send
|
||||
incognito <- readTVarIO =<< asks incognitoMode
|
||||
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
|
||||
|
@ -895,34 +916,52 @@ processChatCommand = \case
|
|||
conn <- withStore' $ \db -> createDirectConnection db userId connId cReq ConnJoined $ incognitoProfile $> profileToSend
|
||||
toView $ CRNewContactConnection user conn
|
||||
pure $ CRSentConfirmation user
|
||||
Connect (Just (ACR SCMContact cReq)) -> withUser $ \user ->
|
||||
APIConnect cmdUserId (Just (ACR SCMContact cReq)) -> withUser $ \user -> do
|
||||
checkCorrectCmdUser cmdUserId user
|
||||
-- [incognito] generate profile to send
|
||||
connectViaContact user cReq
|
||||
Connect Nothing -> throwChatError CEInvalidConnReq
|
||||
APIConnect _ Nothing -> throwChatError CEInvalidConnReq
|
||||
Connect cReqUri -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APIConnect userId cReqUri
|
||||
ConnectSimplex -> withUser $ \user ->
|
||||
-- [incognito] generate profile to send
|
||||
connectViaContact user adminContactReq
|
||||
DeleteContact cName -> withContactName cName $ APIDeleteChat . ChatRef CTDirect
|
||||
ClearContact cName -> withContactName cName $ APIClearChat . ChatRef CTDirect
|
||||
ListContacts -> withUser $ \user -> do
|
||||
APIListContacts cmdUserId -> withUser $ \user -> do
|
||||
checkCorrectCmdUser cmdUserId user
|
||||
contacts <- withStore' (`getUserContacts` user)
|
||||
pure $ CRContactsList user contacts
|
||||
CreateMyAddress -> withUser $ \user@User {userId} -> withChatLock "createMyAddress" . procCmd $ do
|
||||
ListContacts -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APIListContacts userId
|
||||
APICreateMyAddress cmdUserId -> withUser $ \user@User {userId} -> withChatLock "createMyAddress" . procCmd $ do
|
||||
checkCorrectCmdUser cmdUserId user
|
||||
(connId, cReq) <- withAgent $ \a -> createConnection a True SCMContact Nothing
|
||||
withStore $ \db -> createUserContactLink db userId connId cReq
|
||||
pure $ CRUserContactLinkCreated user cReq
|
||||
DeleteMyAddress -> withUser $ \user -> withChatLock "deleteMyAddress" $ do
|
||||
CreateMyAddress -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APICreateMyAddress userId
|
||||
APIDeleteMyAddress cmdUserId -> withUser $ \user -> withChatLock "deleteMyAddress" $ do
|
||||
checkCorrectCmdUser cmdUserId user
|
||||
conns <- withStore (`getUserAddressConnections` user)
|
||||
procCmd $ do
|
||||
forM_ conns $ \conn -> deleteAgentConnectionAsync user conn `catchError` \_ -> pure ()
|
||||
withStore' (`deleteUserAddress` user)
|
||||
pure $ CRUserContactLinkDeleted user
|
||||
ShowMyAddress -> withUser $ \user@User {userId} -> do
|
||||
DeleteMyAddress -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APIDeleteMyAddress userId
|
||||
APIShowMyAddress cmdUserId -> withUser $ \user@User {userId} -> do
|
||||
checkCorrectCmdUser cmdUserId user
|
||||
contactLink <- withStore (`getUserAddress` userId)
|
||||
pure $ CRUserContactLink user contactLink
|
||||
AddressAutoAccept autoAccept_ -> withUser $ \user@User {userId} -> do
|
||||
ShowMyAddress -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APIShowMyAddress userId
|
||||
APIAddressAutoAccept cmdUserId autoAccept_ -> withUser $ \user@User {userId} -> do
|
||||
checkCorrectCmdUser cmdUserId user
|
||||
contactLink <- withStore (\db -> updateUserAddressAutoAccept db userId autoAccept_)
|
||||
pure $ CRUserContactLinkUpdated user contactLink
|
||||
AddressAutoAccept autoAccept_ -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APIAddressAutoAccept userId autoAccept_
|
||||
AcceptContact cName -> withUser $ \User {userId} -> do
|
||||
connReqId <- withStore $ \db -> getContactRequestIdByName db userId cName
|
||||
processChatCommand $ APIAcceptContact connReqId
|
||||
|
@ -962,10 +1001,13 @@ processChatCommand = \case
|
|||
chatRef <- getChatRef user chatName
|
||||
let mc = MCText $ safeDecodeUtf8 msg
|
||||
processChatCommand $ APIUpdateChatItem chatRef chatItemId live mc
|
||||
NewGroup gProfile -> withUser $ \user -> do
|
||||
APINewGroup cmdUserId gProfile -> withUser $ \user -> do
|
||||
checkCorrectCmdUser cmdUserId user
|
||||
gVar <- asks idsDrg
|
||||
groupInfo <- withStore (\db -> createNewGroup db gVar user gProfile)
|
||||
pure $ CRGroupCreated user groupInfo
|
||||
NewGroup gProfile -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APINewGroup userId gProfile
|
||||
APIAddMember groupId contactId memRole -> withUser $ \user -> withChatLock "addMember" $ do
|
||||
-- TODO for large groups: no need to load all members to determine if contact is a member
|
||||
(group, contact) <- withStore $ \db -> (,) <$> getGroup db user groupId <*> getContact db user contactId
|
||||
|
@ -1282,6 +1324,8 @@ processChatCommand = \case
|
|||
withStoreChanged a = checkChatStopped $ a >> setStoreChanged $> CRCmdOk Nothing
|
||||
checkStoreNotChanged :: m ChatResponse -> m ChatResponse
|
||||
checkStoreNotChanged = ifM (asks chatStoreChanged >>= readTVarIO) (throwChatError CEChatStoreChanged)
|
||||
checkCorrectCmdUser :: UserId -> User -> m ()
|
||||
checkCorrectCmdUser cmdUserId User {userId = activeUserId} = when (cmdUserId /= activeUserId) $ throwChatError (CEDifferentActiveUser cmdUserId activeUserId)
|
||||
withUserName :: UserName -> (UserId -> ChatCommand) -> m ChatResponse
|
||||
withUserName uName cmd = withStore (`getUserIdByName` uName) >>= processChatCommand . cmd
|
||||
withContactName :: ContactName -> (ContactId -> ChatCommand) -> m ChatResponse
|
||||
|
@ -3710,7 +3754,7 @@ chatCommandP =
|
|||
"/db decrypt " *> (APIStorageEncryption . (`DBEncryptionConfig` "") <$> dbKeyP),
|
||||
"/sql chat " *> (ExecChatStoreSQL <$> textP),
|
||||
"/sql agent " *> (ExecAgentStoreSQL <$> textP),
|
||||
"/_get chats" *> (APIGetChats <$> (" pcc=on" $> True <|> " pcc=off" $> False <|> pure False)),
|
||||
"/_get chats " *> (APIGetChats <$> A.decimal <*> (" pcc=on" $> True <|> " pcc=off" $> False <|> pure False)),
|
||||
"/_get chat " *> (APIGetChat <$> chatRefP <* A.space <*> chatPaginationP <*> optional (" search=" *> stringP)),
|
||||
"/_get items count=" *> (APIGetChatItems <$> A.decimal),
|
||||
"/_send " *> (APISendMessage <$> chatRefP <*> liveMessageP <*> (" json " *> jsonP <|> " text " *> (ComposedMessage Nothing Nothing <$> mcTextP))),
|
||||
|
@ -3730,8 +3774,8 @@ chatCommandP =
|
|||
"/_call extra @" *> (APISendCallExtraInfo <$> A.decimal <* A.space <*> jsonP),
|
||||
"/_call end @" *> (APIEndCall <$> A.decimal),
|
||||
"/_call status @" *> (APICallStatus <$> A.decimal <* A.space <*> strP),
|
||||
"/_call get" $> APIGetCallInvitations,
|
||||
"/_profile " *> (APIUpdateProfile <$> jsonP),
|
||||
"/_call get " *> (APIGetCallInvitations <$> A.decimal),
|
||||
"/_profile " *> (APIUpdateProfile <$> A.decimal <* A.space <*> jsonP),
|
||||
"/_set alias @" *> (APISetContactAlias <$> A.decimal <*> (A.space *> textP <|> pure "")),
|
||||
"/_set alias :" *> (APISetConnectionAlias <$> A.decimal <*> (A.space *> textP <|> pure "")),
|
||||
"/_set prefs @" *> (APISetContactPrefs <$> A.decimal <* A.space <*> jsonP),
|
||||
|
@ -3740,7 +3784,7 @@ chatCommandP =
|
|||
"/_ntf register " *> (APIRegisterToken <$> strP_ <*> strP),
|
||||
"/_ntf verify " *> (APIVerifyToken <$> strP <* A.space <*> strP <* A.space <*> strP),
|
||||
"/_ntf delete " *> (APIDeleteToken <$> strP),
|
||||
"/_ntf message " *> (APIGetNtfMessage <$> strP <* A.space <*> strP),
|
||||
"/_ntf message " *> (APIGetNtfMessage <$> A.decimal <* A.space <*> strP <* A.space <*> strP),
|
||||
"/_add #" *> (APIAddMember <$> A.decimal <* A.space <*> A.decimal <*> memberRole),
|
||||
"/_join #" *> (APIJoinGroup <$> A.decimal),
|
||||
"/_member role #" *> (APIMemberRole <$> A.decimal <* A.space <*> A.decimal <*> memberRole),
|
||||
|
@ -3753,12 +3797,14 @@ chatCommandP =
|
|||
"/smp_servers" $> GetUserSMPServers,
|
||||
"/smp default" $> SetUserSMPServers (SMPServersConfig []),
|
||||
"/smp test " *> (TestSMPServer <$> strP),
|
||||
"/_smp " *> (SetUserSMPServers <$> jsonP),
|
||||
"/_smp " *> (APISetUserSMPServers <$> A.decimal <* A.space <*> jsonP),
|
||||
"/smp " *> (SetUserSMPServers . SMPServersConfig . map toServerCfg <$> smpServersP),
|
||||
"/_smp " *> (APIGetUserSMPServers <$> A.decimal),
|
||||
"/smp" $> GetUserSMPServers,
|
||||
"/_ttl " *> (APISetChatItemTTL <$> ciTTLDecimal),
|
||||
"/ttl " *> (APISetChatItemTTL <$> ciTTL),
|
||||
"/ttl" $> APIGetChatItemTTL,
|
||||
"/_ttl " *> (APISetChatItemTTL <$> A.decimal <* A.space <*> ciTTLDecimal),
|
||||
"/ttl " *> (SetChatItemTTL <$> ciTTL),
|
||||
"/_ttl " *> (APIGetChatItemTTL <$> A.decimal),
|
||||
"/ttl" $> GetChatItemTTL,
|
||||
"/_network " *> (APISetNetworkConfig <$> jsonP),
|
||||
("/network " <|> "/net ") *> (APISetNetworkConfig <$> netCfgP),
|
||||
("/network" <|> "/net") $> APIGetNetworkConfig,
|
||||
|
@ -3786,7 +3832,7 @@ chatCommandP =
|
|||
("/help settings" <|> "/hs") $> ChatHelp HSSettings,
|
||||
("/help" <|> "/h") $> ChatHelp HSMain,
|
||||
("/group " <|> "/g ") *> char_ '#' *> (NewGroup <$> groupProfile),
|
||||
"/_group " *> (NewGroup <$> jsonP),
|
||||
"/_group " *> (APINewGroup <$> A.decimal <* A.space <*> jsonP),
|
||||
("/add " <|> "/a ") *> char_ '#' *> (AddMember <$> displayName <* A.space <* char_ '@' <*> displayName <*> memberRole),
|
||||
("/join " <|> "/j ") *> char_ '#' *> (JoinGroup <$> displayName),
|
||||
("/member role " <|> "/mr ") *> char_ '#' *> (MemberRole <$> displayName <* A.space <* char_ '@' <*> displayName <*> memberRole),
|
||||
|
@ -3810,7 +3856,10 @@ chatCommandP =
|
|||
"/show link #" *> (ShowGroupLink <$> displayName),
|
||||
(">#" <|> "> #") *> (SendGroupMessageQuote <$> displayName <* A.space <*> pure Nothing <*> quotedMsg <*> A.takeByteString),
|
||||
(">#" <|> "> #") *> (SendGroupMessageQuote <$> displayName <* A.space <* char_ '@' <*> (Just <$> displayName) <* A.space <*> quotedMsg <*> A.takeByteString),
|
||||
"/_contacts " *> (APIListContacts <$> A.decimal),
|
||||
("/contacts" <|> "/cs") $> ListContacts,
|
||||
"/_connect " *> (APIConnect <$> A.decimal <* A.space <*> ((Just <$> strP) <|> A.takeByteString $> Nothing)),
|
||||
"/_connect " *> (APIAddContact <$> A.decimal),
|
||||
("/connect " <|> "/c ") *> (Connect <$> ((Just <$> strP) <|> A.takeByteString $> Nothing)),
|
||||
("/connect" <|> "/c") $> AddContact,
|
||||
SendMessage <$> chatNameP <* A.space <*> A.takeByteString,
|
||||
|
@ -3833,9 +3882,13 @@ chatCommandP =
|
|||
("/fcancel " <|> "/fc ") *> (CancelFile <$> A.decimal),
|
||||
("/fstatus " <|> "/fs ") *> (FileStatus <$> A.decimal),
|
||||
"/simplex" $> ConnectSimplex,
|
||||
"/_address " *> (APICreateMyAddress <$> A.decimal),
|
||||
("/address" <|> "/ad") $> CreateMyAddress,
|
||||
"/_delete_address " *> (APIDeleteMyAddress <$> A.decimal),
|
||||
("/delete_address" <|> "/da") $> DeleteMyAddress,
|
||||
"/_show_address " *> (APIShowMyAddress <$> A.decimal),
|
||||
("/show_address" <|> "/sa") $> ShowMyAddress,
|
||||
"/_auto_accept " *> (APIAddressAutoAccept <$> A.decimal <* A.space <*> autoAcceptP),
|
||||
"/auto_accept " *> (AddressAutoAccept <$> autoAcceptP),
|
||||
("/accept " <|> "/ac ") *> char_ '@' *> (AcceptContact <$> displayName),
|
||||
("/reject " <|> "/rc ") *> char_ '@' *> (RejectContact <$> displayName),
|
||||
|
|
|
@ -158,7 +158,7 @@ data ChatCommand
|
|||
| APIStorageEncryption DBEncryptionConfig
|
||||
| ExecChatStoreSQL Text
|
||||
| ExecAgentStoreSQL Text
|
||||
| APIGetChats {pendingConnections :: Bool} -- UserId
|
||||
| APIGetChats {userId :: UserId, pendingConnections :: Bool}
|
||||
| APIGetChat ChatRef ChatPagination (Maybe String)
|
||||
| APIGetChatItems Int
|
||||
| APISendMessage {chatRef :: ChatRef, liveMessage :: Bool, composedMessage :: ComposedMessage}
|
||||
|
@ -177,9 +177,9 @@ data ChatCommand
|
|||
| APISendCallAnswer ContactId WebRTCSession
|
||||
| APISendCallExtraInfo ContactId WebRTCExtraInfo
|
||||
| APIEndCall ContactId
|
||||
| APIGetCallInvitations -- UserId
|
||||
| APIGetCallInvitations UserId
|
||||
| APICallStatus ContactId WebRTCCallStatus
|
||||
| APIUpdateProfile Profile -- UserId
|
||||
| APIUpdateProfile UserId Profile
|
||||
| APISetContactPrefs ContactId Preferences
|
||||
| APISetContactAlias ContactId LocalAlias
|
||||
| APISetConnectionAlias Int64 LocalAlias
|
||||
|
@ -188,7 +188,7 @@ data ChatCommand
|
|||
| APIRegisterToken DeviceToken NotificationsMode
|
||||
| APIVerifyToken DeviceToken C.CbNonce ByteString
|
||||
| APIDeleteToken DeviceToken
|
||||
| APIGetNtfMessage {nonce :: C.CbNonce, encNtfInfo :: ByteString} -- UserId
|
||||
| APIGetNtfMessage {userId :: UserId, nonce :: C.CbNonce, encNtfInfo :: ByteString}
|
||||
| APIAddMember GroupId ContactId GroupMemberRole
|
||||
| APIJoinGroup GroupId
|
||||
| APIMemberRole GroupId GroupMemberId GroupMemberRole
|
||||
|
@ -199,11 +199,15 @@ data ChatCommand
|
|||
| APICreateGroupLink GroupId
|
||||
| APIDeleteGroupLink GroupId
|
||||
| APIGetGroupLink GroupId
|
||||
| GetUserSMPServers -- UserId
|
||||
| SetUserSMPServers SMPServersConfig -- UserId
|
||||
| APIGetUserSMPServers UserId
|
||||
| GetUserSMPServers
|
||||
| APISetUserSMPServers UserId SMPServersConfig
|
||||
| SetUserSMPServers SMPServersConfig
|
||||
| TestSMPServer SMPServerWithAuth
|
||||
| APISetChatItemTTL (Maybe Int64) -- UserId
|
||||
| APIGetChatItemTTL -- UserId
|
||||
| APISetChatItemTTL UserId (Maybe Int64)
|
||||
| SetChatItemTTL (Maybe Int64)
|
||||
| APIGetChatItemTTL UserId
|
||||
| GetChatItemTTL
|
||||
| APISetNetworkConfig NetworkConfig
|
||||
| APIGetNetworkConfig
|
||||
| APISetChatSettings ChatRef ChatSettings
|
||||
|
@ -226,26 +230,34 @@ data ChatCommand
|
|||
| VerifyGroupMember GroupName ContactName (Maybe Text)
|
||||
| ChatHelp HelpSection
|
||||
| Welcome
|
||||
| AddContact -- UserId
|
||||
| Connect (Maybe AConnectionRequestUri) -- UserId
|
||||
| ConnectSimplex -- UserId
|
||||
| APIAddContact UserId
|
||||
| AddContact
|
||||
| APIConnect UserId (Maybe AConnectionRequestUri)
|
||||
| Connect (Maybe AConnectionRequestUri)
|
||||
| ConnectSimplex -- UserId (not used in UI)
|
||||
| DeleteContact ContactName
|
||||
| ClearContact ContactName
|
||||
| ListContacts -- UserId
|
||||
| CreateMyAddress -- UserId
|
||||
| DeleteMyAddress -- UserId
|
||||
| ShowMyAddress -- UserId
|
||||
| AddressAutoAccept (Maybe AutoAccept) -- UserId
|
||||
| APIListContacts UserId
|
||||
| ListContacts
|
||||
| APICreateMyAddress UserId
|
||||
| CreateMyAddress
|
||||
| APIDeleteMyAddress UserId
|
||||
| DeleteMyAddress
|
||||
| APIShowMyAddress UserId
|
||||
| ShowMyAddress
|
||||
| APIAddressAutoAccept UserId (Maybe AutoAccept)
|
||||
| AddressAutoAccept (Maybe AutoAccept)
|
||||
| AcceptContact ContactName
|
||||
| RejectContact ContactName
|
||||
| SendMessage ChatName ByteString
|
||||
| SendLiveMessage ChatName ByteString
|
||||
| SendMessageQuote {contactName :: ContactName, msgDir :: AMsgDirection, quotedMsg :: ByteString, message :: ByteString}
|
||||
| SendMessageBroadcast ByteString -- UserId
|
||||
| SendMessageBroadcast ByteString -- UserId (not used in UI)
|
||||
| DeleteMessage ChatName ByteString
|
||||
| EditMessage {chatName :: ChatName, editedMsg :: ByteString, message :: ByteString}
|
||||
| UpdateLiveMessage {chatName :: ChatName, chatItemId :: ChatItemId, liveMessage :: Bool, message :: ByteString}
|
||||
| NewGroup GroupProfile -- UserId
|
||||
| APINewGroup UserId GroupProfile
|
||||
| NewGroup GroupProfile
|
||||
| AddMember GroupName ContactName GroupMemberRole
|
||||
| JoinGroup GroupName
|
||||
| MemberRole GroupName ContactName GroupMemberRole
|
||||
|
@ -254,7 +266,7 @@ data ChatCommand
|
|||
| DeleteGroup GroupName
|
||||
| ClearGroup GroupName
|
||||
| ListMembers GroupName
|
||||
| ListGroups -- UserId
|
||||
| ListGroups -- UserId (not used in UI)
|
||||
| UpdateGroupNames GroupName GroupProfile
|
||||
| ShowGroupProfile GroupName
|
||||
| UpdateGroupDescription GroupName (Maybe Text)
|
||||
|
@ -262,9 +274,9 @@ data ChatCommand
|
|||
| DeleteGroupLink GroupName
|
||||
| ShowGroupLink GroupName
|
||||
| SendGroupMessageQuote {groupName :: GroupName, contactName_ :: Maybe ContactName, quotedMsg :: ByteString, message :: ByteString}
|
||||
| LastMessages (Maybe ChatName) Int (Maybe String) -- UserId
|
||||
| LastChatItemId (Maybe ChatName) Int -- UserId
|
||||
| ShowChatItem (Maybe ChatItemId) -- UserId
|
||||
| LastMessages (Maybe ChatName) Int (Maybe String) -- UserId (not used in UI)
|
||||
| LastChatItemId (Maybe ChatName) Int -- UserId (not used in UI)
|
||||
| ShowChatItem (Maybe ChatItemId) -- UserId (not used in UI)
|
||||
| ShowLiveItems Bool
|
||||
| SendFile ChatName FilePath
|
||||
| SendImage ChatName FilePath
|
||||
|
@ -273,13 +285,13 @@ data ChatCommand
|
|||
| ReceiveFile {fileId :: FileTransferId, fileInline :: Maybe Bool, filePath :: Maybe FilePath}
|
||||
| CancelFile FileTransferId
|
||||
| FileStatus FileTransferId
|
||||
| ShowProfile -- UserId
|
||||
| UpdateProfile ContactName Text -- UserId
|
||||
| UpdateProfileImage (Maybe ImageData) -- UserId
|
||||
| SetUserFeature AChatFeature FeatureAllowed -- UserId
|
||||
| ShowProfile -- UserId (not used in UI)
|
||||
| UpdateProfile ContactName Text -- UserId (not used in UI)
|
||||
| UpdateProfileImage (Maybe ImageData) -- UserId (not used in UI)
|
||||
| SetUserFeature AChatFeature FeatureAllowed -- UserId (not used in UI)
|
||||
| SetContactFeature AChatFeature ContactName (Maybe FeatureAllowed)
|
||||
| SetGroupFeature AGroupFeature GroupName GroupFeatureEnabled
|
||||
| SetUserTimedMessages Bool -- UserId
|
||||
| SetUserTimedMessages Bool -- UserId (not used in UI)
|
||||
| SetContactTimedMessages ContactName (Maybe TimedMessagesEnabled)
|
||||
| SetGroupTimedMessages GroupName (Maybe Int)
|
||||
| QuitChat
|
||||
|
@ -559,6 +571,7 @@ data ChatErrorType
|
|||
= CENoActiveUser
|
||||
| CENoConnectionUser {agentConnId :: AgentConnId}
|
||||
| CEActiveUserExists -- TODO delete
|
||||
| CEDifferentActiveUser {commandUserId :: UserId, activeUserId :: UserId}
|
||||
| CEChatNotStarted
|
||||
| CEChatNotStopped
|
||||
| CEChatStoreChanged
|
||||
|
|
|
@ -1140,6 +1140,7 @@ viewChatError = \case
|
|||
CENoActiveUser -> ["error: active user is required"]
|
||||
CENoConnectionUser _agentConnId -> [] -- ["error: connection has no user, conn id: " <> sShow agentConnId]
|
||||
CEActiveUserExists -> ["error: active user already exists"]
|
||||
CEDifferentActiveUser commandUserId activeUserId -> ["error: different active user, command user id: " <> sShow commandUserId <> ", active user id: " <> sShow activeUserId]
|
||||
CEChatNotStarted -> ["error: chat not started"]
|
||||
CEChatNotStopped -> ["error: chat not stopped"]
|
||||
CEChatStoreChanged -> ["error: chat store changed, please restart chat"]
|
||||
|
|
|
@ -241,9 +241,9 @@ testAddContact :: Spec
|
|||
testAddContact = versionTestMatrix2 runTestAddContact
|
||||
where
|
||||
runTestAddContact alice bob = do
|
||||
alice ##> "/c"
|
||||
alice ##> "/_connect 1"
|
||||
inv <- getInvitation alice
|
||||
bob ##> ("/c " <> inv)
|
||||
bob ##> ("/_connect 1 " <> inv)
|
||||
bob <## "confirmation sent!"
|
||||
concurrently_
|
||||
(bob <## "alice (Alice): contact is connected")
|
||||
|
@ -324,7 +324,7 @@ testDeleteContactDeletesProfile =
|
|||
-- alice deletes contact, profile is deleted
|
||||
alice ##> "/d bob"
|
||||
alice <## "bob: contact is deleted"
|
||||
alice ##> "/cs"
|
||||
alice ##> "/_contacts 1"
|
||||
(alice </)
|
||||
alice `hasContactProfiles` ["alice"]
|
||||
-- bob deletes contact, profile is deleted
|
||||
|
@ -1791,7 +1791,7 @@ testUpdateProfileImage =
|
|||
alice <## "profile image updated"
|
||||
alice ##> "/profile_image"
|
||||
alice <## "profile image removed"
|
||||
alice ##> "/_profile {\"displayName\": \"alice2\", \"fullName\": \"\"}"
|
||||
alice ##> "/_profile 1 {\"displayName\": \"alice2\", \"fullName\": \"\"}"
|
||||
alice <## "user profile is changed to alice2 (your contacts are notified)"
|
||||
bob <## "contact alice changed to alice2"
|
||||
bob <## "use @alice2 <message> to send messages"
|
||||
|
@ -2700,7 +2700,7 @@ testDeduplicateContactRequestsProfileChange = testChat3 aliceProfile bobProfile
|
|||
testRejectContactAndDeleteUserContact :: IO ()
|
||||
testRejectContactAndDeleteUserContact = testChat3 aliceProfile bobProfile cathProfile $
|
||||
\alice bob cath -> do
|
||||
alice ##> "/ad"
|
||||
alice ##> "/_address 1"
|
||||
cLink <- getContactLink alice True
|
||||
bob ##> ("/c " <> cLink)
|
||||
alice <#? bob
|
||||
|
@ -2708,12 +2708,12 @@ testRejectContactAndDeleteUserContact = testChat3 aliceProfile bobProfile cathPr
|
|||
alice <## "bob: contact request rejected"
|
||||
(bob </)
|
||||
|
||||
alice ##> "/sa"
|
||||
alice ##> "/_show_address 1"
|
||||
cLink' <- getContactLink alice False
|
||||
alice <## "auto_accept off"
|
||||
cLink' `shouldBe` cLink
|
||||
|
||||
alice ##> "/da"
|
||||
alice ##> "/_delete_address 1"
|
||||
alice <## "Your chat address is deleted - accepted contacts will remain connected."
|
||||
alice <## "To create a new chat address use /ad"
|
||||
|
||||
|
@ -2747,7 +2747,7 @@ testAutoReplyMessage = testChat2 aliceProfile bobProfile $
|
|||
\alice bob -> do
|
||||
alice ##> "/ad"
|
||||
cLink <- getContactLink alice True
|
||||
alice ##> "/auto_accept on incognito=off text hello!"
|
||||
alice ##> "/_auto_accept 1 on incognito=off text hello!"
|
||||
alice <## "auto_accept on"
|
||||
alice <## "auto reply:"
|
||||
alice <## "hello!"
|
||||
|
@ -3182,7 +3182,7 @@ testCantSeeGlobalPrefsUpdateIncognito = testChat3 aliceProfile bobProfile cathPr
|
|||
cath <## "alice (Alice): contact is connected"
|
||||
]
|
||||
alice <## "cath (Catherine): contact is connected"
|
||||
alice ##> "/_profile {\"displayName\": \"alice\", \"fullName\": \"\", \"preferences\": {\"fullDelete\": {\"allow\": \"always\"}}}"
|
||||
alice ##> "/_profile 1 {\"displayName\": \"alice\", \"fullName\": \"\", \"preferences\": {\"fullDelete\": {\"allow\": \"always\"}}}"
|
||||
alice <## "user full name removed (your contacts are notified)"
|
||||
alice <## "updated preferences:"
|
||||
alice <## "Full deletion allowed: always"
|
||||
|
@ -3353,7 +3353,7 @@ testSetContactPrefs = testChat2 aliceProfile bobProfile $
|
|||
createDirectoryIfMissing True "./tests/tmp/bob"
|
||||
copyFile "./tests/fixtures/test.txt" "./tests/tmp/alice/test.txt"
|
||||
copyFile "./tests/fixtures/test.txt" "./tests/tmp/bob/test.txt"
|
||||
bob ##> "/_profile {\"displayName\": \"bob\", \"fullName\": \"Bob\", \"preferences\": {\"voice\": {\"allow\": \"no\"}}}"
|
||||
bob ##> "/_profile 1 {\"displayName\": \"bob\", \"fullName\": \"Bob\", \"preferences\": {\"voice\": {\"allow\": \"no\"}}}"
|
||||
bob <## "profile image removed"
|
||||
bob <## "updated preferences:"
|
||||
bob <## "Voice messages allowed: no"
|
||||
|
@ -3390,7 +3390,7 @@ testSetContactPrefs = testChat2 aliceProfile bobProfile $
|
|||
alice <## "started receiving file 1 (test.txt) from bob"
|
||||
alice <## "completed receiving file 1 (test.txt) from bob"
|
||||
(bob </)
|
||||
-- alice ##> "/_profile {\"displayName\": \"alice\", \"fullName\": \"Alice\", \"preferences\": {\"voice\": {\"allow\": \"no\"}}}"
|
||||
-- alice ##> "/_profile 1 {\"displayName\": \"alice\", \"fullName\": \"Alice\", \"preferences\": {\"voice\": {\"allow\": \"no\"}}}"
|
||||
alice ##> "/set voice no"
|
||||
alice <## "updated preferences:"
|
||||
alice <## "Voice messages allowed: no"
|
||||
|
@ -3403,7 +3403,7 @@ testSetContactPrefs = testChat2 aliceProfile bobProfile $
|
|||
bob <## "Voice messages: off (you allow: default (no), contact allows: yes)"
|
||||
bob #$> ("/_get chat @2 count=100", chat, startFeatures <> [(0, "Voice messages: enabled for you"), (1, "voice message (00:10)"), (0, "Voice messages: off")])
|
||||
(bob </)
|
||||
bob ##> "/_profile {\"displayName\": \"bob\", \"fullName\": \"\", \"preferences\": {\"voice\": {\"allow\": \"yes\"}}}"
|
||||
bob ##> "/_profile 1 {\"displayName\": \"bob\", \"fullName\": \"\", \"preferences\": {\"voice\": {\"allow\": \"yes\"}}}"
|
||||
bob <## "user full name removed (your contacts are notified)"
|
||||
bob <## "updated preferences:"
|
||||
bob <## "Voice messages allowed: yes"
|
||||
|
@ -3716,7 +3716,7 @@ testGetSetSMPServers :: IO ()
|
|||
testGetSetSMPServers =
|
||||
testChat2 aliceProfile bobProfile $
|
||||
\alice _ -> do
|
||||
alice #$> ("/smp", id, "smp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=:server_password@localhost:5001")
|
||||
alice #$> ("/_smp 1", id, "smp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=:server_password@localhost:5001")
|
||||
alice #$> ("/smp smp://1234-w==@smp1.example.im", id, "ok")
|
||||
alice #$> ("/smp", id, "smp://1234-w==@smp1.example.im")
|
||||
alice #$> ("/smp smp://1234-w==:password@smp1.example.im", id, "ok")
|
||||
|
@ -4081,7 +4081,7 @@ testNegotiateCall =
|
|||
testChat2 aliceProfile bobProfile $ \alice bob -> do
|
||||
connectUsers alice bob
|
||||
-- just for testing db query
|
||||
alice ##> "/_call get"
|
||||
alice ##> "/_call get 1"
|
||||
-- alice invite bob to call
|
||||
alice ##> ("/_call invite @2 " <> serialize testCallType)
|
||||
alice <## "ok"
|
||||
|
@ -4308,10 +4308,10 @@ testSetChatItemTTL =
|
|||
alice <# "bob> 4"
|
||||
alice #$> ("/_get chat @2 count=100", chatF, chatFeaturesF <> [((1, "1"), Nothing), ((0, "2"), Nothing), ((1, ""), Just "test.jpg"), ((1, "3"), Nothing), ((0, "4"), Nothing)])
|
||||
checkActionDeletesFile "./tests/tmp/app_files/test.jpg" $
|
||||
alice #$> ("/_ttl 2", id, "ok")
|
||||
alice #$> ("/_ttl 1 2", id, "ok")
|
||||
alice #$> ("/_get chat @2 count=100", chat, [(1, "3"), (0, "4")]) -- when expiration is turned on, first cycle is synchronous
|
||||
bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "1"), (1, "2"), (0, ""), (0, "3"), (1, "4")])
|
||||
alice #$> ("/ttl", id, "old messages are set to be deleted after: 2 second(s)")
|
||||
alice #$> ("/_ttl 1", id, "old messages are set to be deleted after: 2 second(s)")
|
||||
alice #$> ("/ttl week", id, "ok")
|
||||
alice #$> ("/ttl", id, "old messages are set to be deleted after: one week")
|
||||
alice #$> ("/ttl none", id, "ok")
|
||||
|
@ -5042,7 +5042,7 @@ itemId i = show $ length chatFeatures + i
|
|||
|
||||
getChats :: (Eq a, Show a) => ([(String, String, Maybe ConnStatus)] -> [a]) -> TestCC -> [a] -> Expectation
|
||||
getChats f cc res = do
|
||||
cc ##> "/_get chats pcc=on"
|
||||
cc ##> "/_get chats 1 pcc=on"
|
||||
line <- getTermLine cc
|
||||
f (read line) `shouldMatchList` res
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue