mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-12 19:35:00 +00:00
docs: bots API (#6091)
* docs: bot API commands * generate API commands doc * generate commands docs with parameters and responses * add API types * more types * document all types (with some deviations from JSON encodings) * rename types * interface objects * separator * command syntax * more syntax * API events * event types * fix all type definitions * pre-process types outside of rendering * pre-process event types * overview * pre-process commands * param syntax WIP * syntax for types in command parameters * API error response and chat event * remove unsupported/deprecated command parameters * reorder * syntax for choice * show command errors * event descriptions * python syntax for commands and types (#6099) * python syntax for commands and types * python snippets: convert numbers to string * fixes * update readme, enable all tests * fix operators test * update plans --------- Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
This commit is contained in:
@@ -41,7 +41,6 @@ import Data.List.NonEmpty (NonEmpty)
|
||||
import Data.Map.Strict (Map)
|
||||
import qualified Data.Map.Strict as M
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Set (Set)
|
||||
import Data.String
|
||||
import Data.Text (Text)
|
||||
import Data.Text.Encoding (decodeLatin1)
|
||||
@@ -259,9 +258,9 @@ data HelpSection = HSMain | HSFiles | HSGroups | HSContacts | HSMyAddress | HSIn
|
||||
|
||||
data ChatCommand
|
||||
= ShowActiveUser
|
||||
| CreateActiveUser NewUser
|
||||
| CreateActiveUser {newUser :: NewUser}
|
||||
| ListUsers
|
||||
| APISetActiveUser UserId (Maybe UserPwd)
|
||||
| APISetActiveUser {userId :: UserId, viewPwd :: Maybe UserPwd}
|
||||
| SetActiveUser UserName (Maybe UserPwd)
|
||||
| SetAllContactReceipts Bool
|
||||
| APISetUserContactReceipts UserId UserMsgReceiptSettings
|
||||
@@ -276,7 +275,7 @@ data ChatCommand
|
||||
| UnhideUser UserPwd
|
||||
| MuteUser
|
||||
| UnmuteUser
|
||||
| APIDeleteUser UserId Bool (Maybe UserPwd)
|
||||
| APIDeleteUser {userId :: UserId, delSMPQueues :: Bool, viewPwd :: Maybe UserPwd}
|
||||
| DeleteUser UserName Bool (Maybe UserPwd)
|
||||
| StartChat {mainApp :: Bool, enableSndFiles :: Bool, largeLinkData :: Bool} -- enableSndFiles has no effect when mainApp is True
|
||||
| CheckChatRunning
|
||||
@@ -305,9 +304,9 @@ data ChatCommand
|
||||
| APIGetAppSettings (Maybe AppSettings)
|
||||
| APIGetChatTags UserId
|
||||
| APIGetChats {userId :: UserId, pendingConnections :: Bool, pagination :: PaginationByTime, query :: ChatListQuery}
|
||||
| APIGetChat ChatRef (Maybe MsgContentTag) ChatPagination (Maybe String)
|
||||
| APIGetChatItems ChatPagination (Maybe String)
|
||||
| APIGetChatItemInfo ChatRef ChatItemId
|
||||
| APIGetChat {chatRef :: ChatRef, contentTag :: Maybe MsgContentTag, chatPagination :: ChatPagination, search :: Maybe String}
|
||||
| APIGetChatItems {chatPagination :: ChatPagination, search :: Maybe String}
|
||||
| APIGetChatItemInfo {chatRef :: ChatRef, chatItemId :: ChatItemId}
|
||||
| APISendMessages {sendRef :: SendRef, liveMessage :: Bool, ttl :: Maybe Int, composedMessages :: NonEmpty ComposedMessage}
|
||||
| APICreateChatTag ChatTagData
|
||||
| APISetChatTags ChatRef (Maybe (NonEmpty ChatTagId))
|
||||
@@ -318,23 +317,23 @@ data ChatCommand
|
||||
| APIReportMessage {groupId :: GroupId, chatItemId :: ChatItemId, reportReason :: ReportReason, reportText :: Text}
|
||||
| ReportMessage {groupName :: GroupName, contactName_ :: Maybe ContactName, reportReason :: ReportReason, reportedMessage :: Text}
|
||||
| APIUpdateChatItem {chatRef :: ChatRef, chatItemId :: ChatItemId, liveMessage :: Bool, updatedMessage :: UpdatedMessage}
|
||||
| APIDeleteChatItem ChatRef (NonEmpty ChatItemId) CIDeleteMode
|
||||
| APIDeleteMemberChatItem GroupId (NonEmpty ChatItemId)
|
||||
| APIDeleteChatItem {chatRef :: ChatRef, chatItemIds :: NonEmpty ChatItemId, deleteMode :: CIDeleteMode}
|
||||
| APIDeleteMemberChatItem {groupId :: GroupId, chatItemIds :: NonEmpty ChatItemId}
|
||||
| APIArchiveReceivedReports GroupId
|
||||
| APIDeleteReceivedReports GroupId (NonEmpty ChatItemId) CIDeleteMode
|
||||
| APIChatItemReaction {chatRef :: ChatRef, chatItemId :: ChatItemId, add :: Bool, reaction :: MsgReaction}
|
||||
| APIGetReactionMembers UserId GroupId ChatItemId MsgReaction
|
||||
| APIGetReactionMembers {userId :: UserId, groupId :: GroupId, chatItemId :: ChatItemId, reaction :: MsgReaction}
|
||||
| APIPlanForwardChatItems {fromChatRef :: ChatRef, chatItemIds :: NonEmpty ChatItemId}
|
||||
| APIForwardChatItems {toChatRef :: ChatRef, fromChatRef :: ChatRef, chatItemIds :: NonEmpty ChatItemId, ttl :: Maybe Int}
|
||||
| APIUserRead UserId
|
||||
| UserRead
|
||||
| APIChatRead ChatRef
|
||||
| APIChatItemsRead ChatRef (NonEmpty ChatItemId)
|
||||
| APIChatUnread ChatRef Bool
|
||||
| APIDeleteChat ChatRef ChatDeleteMode -- currently delete mode settings are only applied to direct chats
|
||||
| APIClearChat ChatRef
|
||||
| APIAcceptContact IncognitoEnabled Int64
|
||||
| APIRejectContact Int64
|
||||
| APIChatRead {chatRef :: ChatRef}
|
||||
| APIChatItemsRead {chatRef :: ChatRef, chatItemIds :: NonEmpty ChatItemId}
|
||||
| APIChatUnread {chatRef :: ChatRef, unreadChat :: Bool}
|
||||
| APIDeleteChat {chatRef :: ChatRef, chatDeleteMode :: ChatDeleteMode} -- currently delete mode settings are only applied to direct chats
|
||||
| APIClearChat {chatRef :: ChatRef}
|
||||
| APIAcceptContact {incognito :: IncognitoEnabled, contactReqId :: Int64}
|
||||
| APIRejectContact {contactReqId :: Int64}
|
||||
| APISendCallInvitation ContactId CallType
|
||||
| SendCallInvitation ContactName CallType
|
||||
| APIRejectCall ContactId
|
||||
@@ -345,11 +344,11 @@ data ChatCommand
|
||||
| APIGetCallInvitations
|
||||
| APICallStatus ContactId WebRTCCallStatus
|
||||
| APIGetNetworkStatuses
|
||||
| APIUpdateProfile UserId Profile
|
||||
| APISetContactPrefs ContactId Preferences
|
||||
| APISetContactAlias ContactId LocalAlias
|
||||
| APISetGroupAlias GroupId LocalAlias
|
||||
| APISetConnectionAlias Int64 LocalAlias
|
||||
| APIUpdateProfile {userId :: UserId, profile :: Profile}
|
||||
| APISetContactPrefs {contactId :: ContactId, preferences :: Preferences}
|
||||
| APISetContactAlias {contactId :: ContactId, localAlias :: LocalAlias}
|
||||
| APISetGroupAlias {groupId :: GroupId, localAlias :: LocalAlias}
|
||||
| APISetConnectionAlias {connectionId :: Int64, localAlias :: LocalAlias}
|
||||
| APISetUserUIThemes UserId (Maybe UIThemeEntityOverrides)
|
||||
| APISetChatUIThemes ChatRef (Maybe UIThemeEntityOverrides)
|
||||
| APIGetNtfToken
|
||||
@@ -359,22 +358,20 @@ data ChatCommand
|
||||
| APIDeleteToken DeviceToken
|
||||
| APIGetNtfConns {nonce :: C.CbNonce, encNtfInfo :: ByteString}
|
||||
| APIGetConnNtfMessages (NonEmpty ConnMsgReq)
|
||||
| APIAddMember GroupId ContactId GroupMemberRole
|
||||
| APIAddMember {groupId :: GroupId, contactId :: ContactId, memberRole :: GroupMemberRole}
|
||||
| APIJoinGroup {groupId :: GroupId, enableNtfs :: MsgFilter}
|
||||
| APIAcceptMember GroupId GroupMemberId GroupMemberRole
|
||||
| APIAcceptMember {groupId :: GroupId, groupMemberId :: GroupMemberId, memberRole :: GroupMemberRole}
|
||||
| APIDeleteMemberSupportChat GroupId GroupMemberId
|
||||
| APIMembersRole GroupId (NonEmpty GroupMemberId) GroupMemberRole
|
||||
| APIBlockMembersForAll GroupId (NonEmpty GroupMemberId) Bool
|
||||
| APIRemoveMembers {groupId :: GroupId, groupMemberIds :: Set GroupMemberId, withMessages :: Bool}
|
||||
| APILeaveGroup GroupId
|
||||
| APIListMembers GroupId
|
||||
-- | APIDeleteGroupConversations GroupId (NonEmpty GroupConversationId)
|
||||
-- | APIArchiveGroupConversations GroupId (NonEmpty GroupConversationId)
|
||||
| APIUpdateGroupProfile GroupId GroupProfile
|
||||
| APICreateGroupLink GroupId GroupMemberRole
|
||||
| APIGroupLinkMemberRole GroupId GroupMemberRole
|
||||
| APIDeleteGroupLink GroupId
|
||||
| APIGetGroupLink GroupId
|
||||
| APIMembersRole {groupId :: GroupId, groupMemberIds :: NonEmpty GroupMemberId, memberRole :: GroupMemberRole}
|
||||
| APIBlockMembersForAll {groupId :: GroupId, groupMemberIds :: NonEmpty GroupMemberId, blocked :: Bool}
|
||||
| APIRemoveMembers {groupId :: GroupId, groupMemberIds :: NonEmpty GroupMemberId, withMessages :: Bool}
|
||||
| APILeaveGroup {groupId :: GroupId}
|
||||
| APIListMembers {groupId :: GroupId}
|
||||
| APIUpdateGroupProfile {groupId :: GroupId, groupProfile :: GroupProfile}
|
||||
| APICreateGroupLink {groupId :: GroupId, memberRole :: GroupMemberRole}
|
||||
| APIGroupLinkMemberRole {groupId :: GroupId, memberRole :: GroupMemberRole}
|
||||
| APIDeleteGroupLink {groupId :: GroupId}
|
||||
| APIGetGroupLink {groupId :: GroupId}
|
||||
| APIAddGroupShortLink GroupId
|
||||
| APICreateMemberContact GroupId GroupMemberId
|
||||
| APISendMemberContactInvitation {contactId :: ContactId, msgContent_ :: Maybe MsgContent}
|
||||
@@ -395,7 +392,7 @@ data ChatCommand
|
||||
| SetChatItemTTL Int64
|
||||
| APIGetChatItemTTL UserId
|
||||
| GetChatItemTTL
|
||||
| APISetChatTTL UserId ChatRef (Maybe Int64)
|
||||
| APISetChatTTL {userId :: UserId, chatRef :: ChatRef, seconds :: Maybe Int64}
|
||||
| SetChatTTL ChatName (Maybe Int64)
|
||||
| GetChatTTL ChatName
|
||||
| APISetNetworkConfig NetworkConfig
|
||||
@@ -404,7 +401,7 @@ data ChatCommand
|
||||
| APISetNetworkInfo UserNetworkInfo
|
||||
| ReconnectAllServers
|
||||
| ReconnectServer UserId SMPServer
|
||||
| APISetChatSettings ChatRef ChatSettings
|
||||
| APISetChatSettings {chatRef :: ChatRef, chatSettings :: ChatSettings}
|
||||
| APISetMemberSettings GroupId GroupMemberId GroupMemberSettings
|
||||
| APIContactInfo ContactId
|
||||
| APIGroupInfo GroupId
|
||||
@@ -415,8 +412,8 @@ data ChatCommand
|
||||
| APISwitchGroupMember GroupId GroupMemberId
|
||||
| APIAbortSwitchContact ContactId
|
||||
| APIAbortSwitchGroupMember GroupId GroupMemberId
|
||||
| APISyncContactRatchet ContactId Bool
|
||||
| APISyncGroupMemberRatchet GroupId GroupMemberId Bool
|
||||
| APISyncContactRatchet {contactId :: ContactId, force :: Bool}
|
||||
| APISyncGroupMemberRatchet {groupId :: GroupId, groupMemberId :: GroupMemberId, force :: Bool}
|
||||
| APIGetContactCode ContactId
|
||||
| APIGetGroupMemberCode GroupId GroupMemberId
|
||||
| APIVerifyContact ContactId (Maybe Text)
|
||||
@@ -445,35 +442,35 @@ data ChatCommand
|
||||
| EnableGroupMember GroupName ContactName
|
||||
| ChatHelp HelpSection
|
||||
| Welcome
|
||||
| APIAddContact UserId IncognitoEnabled
|
||||
| APIAddContact {userId :: UserId, incognito :: IncognitoEnabled}
|
||||
| AddContact IncognitoEnabled
|
||||
| APISetConnectionIncognito Int64 IncognitoEnabled
|
||||
| APIChangeConnectionUser Int64 UserId -- new user id to switch connection to
|
||||
| APIConnectPlan UserId AConnectionLink
|
||||
| APIConnectPlan {userId :: UserId, connectionLink :: Maybe AConnectionLink} -- Maybe is used to report link parsing failure as special error
|
||||
| APIPrepareContact UserId ACreatedConnLink ContactShortLinkData
|
||||
| APIPrepareGroup UserId CreatedLinkContact GroupShortLinkData
|
||||
| APIChangePreparedContactUser ContactId UserId
|
||||
| APIChangePreparedGroupUser GroupId UserId
|
||||
| APIConnectPreparedContact {contactId :: ContactId, incognito :: IncognitoEnabled, msgContent_ :: Maybe MsgContent}
|
||||
| APIConnectPreparedGroup GroupId IncognitoEnabled (Maybe MsgContent)
|
||||
| APIConnect UserId IncognitoEnabled ACreatedConnLink
|
||||
| APIConnect {userId :: UserId, incognito :: IncognitoEnabled, connLink_ :: Maybe ACreatedConnLink} -- Maybe is used to report link parsing failure as special error
|
||||
| Connect IncognitoEnabled (Maybe AConnectionLink)
|
||||
| APIConnectContactViaAddress UserId IncognitoEnabled ContactId
|
||||
| ConnectSimplex IncognitoEnabled -- UserId (not used in UI)
|
||||
| DeleteContact ContactName ChatDeleteMode
|
||||
| ClearContact ContactName
|
||||
| APIListContacts UserId
|
||||
| APIListContacts {userId :: UserId}
|
||||
| ListContacts
|
||||
| APICreateMyAddress UserId
|
||||
| APICreateMyAddress {userId :: UserId}
|
||||
| CreateMyAddress
|
||||
| APIDeleteMyAddress UserId
|
||||
| APIDeleteMyAddress {userId :: UserId}
|
||||
| DeleteMyAddress
|
||||
| APIShowMyAddress UserId
|
||||
| APIShowMyAddress {userId :: UserId}
|
||||
| ShowMyAddress
|
||||
| APIAddMyAddressShortLink UserId
|
||||
| APISetProfileAddress UserId Bool
|
||||
| APISetProfileAddress {userId :: UserId, enable :: Bool}
|
||||
| SetProfileAddress Bool
|
||||
| APISetAddressSettings UserId AddressSettings
|
||||
| APISetAddressSettings {userId :: UserId, settings :: AddressSettings}
|
||||
| SetAddressSettings AddressSettings
|
||||
| AcceptContact IncognitoEnabled ContactName
|
||||
| RejectContact ContactName
|
||||
@@ -490,20 +487,20 @@ data ChatCommand
|
||||
| EditMessage {chatName :: ChatName, editedMsg :: Text, message :: Text}
|
||||
| UpdateLiveMessage {chatName :: ChatName, chatItemId :: ChatItemId, liveMessage :: Bool, message :: Text}
|
||||
| ReactToMessage {add :: Bool, reaction :: MsgReaction, chatName :: ChatName, reactToMessage :: Text}
|
||||
| APINewGroup UserId IncognitoEnabled GroupProfile
|
||||
| APINewGroup {userId :: UserId, incognito :: IncognitoEnabled, groupProfile :: GroupProfile}
|
||||
| NewGroup IncognitoEnabled GroupProfile
|
||||
| AddMember GroupName ContactName GroupMemberRole
|
||||
| JoinGroup {groupName :: GroupName, enableNtfs :: MsgFilter}
|
||||
| AcceptMember GroupName ContactName GroupMemberRole
|
||||
| MemberRole GroupName ContactName GroupMemberRole
|
||||
| BlockForAll GroupName ContactName Bool
|
||||
| RemoveMembers {groupName :: GroupName, members :: Set ContactName, withMessages :: Bool}
|
||||
| RemoveMembers {groupName :: GroupName, members :: NonEmpty ContactName, withMessages :: Bool}
|
||||
| LeaveGroup GroupName
|
||||
| DeleteGroup GroupName
|
||||
| ClearGroup GroupName
|
||||
| ListMembers GroupName
|
||||
| ListMemberSupportChats GroupName
|
||||
| APIListGroups UserId (Maybe ContactId) (Maybe String)
|
||||
| APIListGroups {userId :: UserId, contactId_ :: Maybe ContactId, search :: Maybe String}
|
||||
| ListGroups (Maybe ContactName) (Maybe String)
|
||||
| UpdateGroupNames GroupName GroupProfile
|
||||
| ShowGroupProfile GroupName
|
||||
@@ -528,7 +525,7 @@ data ChatCommand
|
||||
| SendFileDescription ChatName FilePath
|
||||
| ReceiveFile {fileId :: FileTransferId, userApprovedRelays :: Bool, storeEncrypted :: Maybe Bool, fileInline :: Maybe Bool, filePath :: Maybe FilePath}
|
||||
| SetFileToReceive {fileId :: FileTransferId, userApprovedRelays :: Bool, storeEncrypted :: Maybe Bool}
|
||||
| CancelFile FileTransferId
|
||||
| CancelFile {fileId :: FileTransferId}
|
||||
| FileStatus FileTransferId
|
||||
| ShowProfile -- UserId (not used in UI)
|
||||
| UpdateProfile ContactName (Maybe Text) -- UserId (not used in UI)
|
||||
@@ -642,9 +639,9 @@ data ChatResponse
|
||||
| CRGroupMemberInfo {user :: User, groupInfo :: GroupInfo, member :: GroupMember, connectionStats_ :: Maybe ConnectionStats}
|
||||
| CRQueueInfo {user :: User, rcvMsgInfo :: Maybe RcvMsgInfo, queueInfo :: ServerQueueInfo}
|
||||
| CRContactSwitchStarted {user :: User, contact :: Contact, connectionStats :: ConnectionStats}
|
||||
| CEvtGroupMemberSwitchStarted {user :: User, groupInfo :: GroupInfo, member :: GroupMember, connectionStats :: ConnectionStats}
|
||||
| CRGroupMemberSwitchStarted {user :: User, groupInfo :: GroupInfo, member :: GroupMember, connectionStats :: ConnectionStats}
|
||||
| CRContactSwitchAborted {user :: User, contact :: Contact, connectionStats :: ConnectionStats}
|
||||
| CEvtGroupMemberSwitchAborted {user :: User, groupInfo :: GroupInfo, member :: GroupMember, connectionStats :: ConnectionStats}
|
||||
| CRGroupMemberSwitchAborted {user :: User, groupInfo :: GroupInfo, member :: GroupMember, connectionStats :: ConnectionStats}
|
||||
| CRContactRatchetSyncStarted {user :: User, contact :: Contact, connectionStats :: ConnectionStats}
|
||||
| CRGroupMemberRatchetSyncStarted {user :: User, groupInfo :: GroupInfo, member :: GroupMember, connectionStats :: ConnectionStats}
|
||||
| CRContactCode {user :: User, contact :: Contact, connectionCode :: Text}
|
||||
@@ -673,7 +670,7 @@ data ChatResponse
|
||||
| CRContactRequestRejected {user :: User, contactRequest :: UserContactRequest, contact_ :: Maybe Contact}
|
||||
| CRUserAcceptedGroupSent {user :: User, groupInfo :: GroupInfo, hostContact :: Maybe Contact}
|
||||
| CRUserDeletedMembers {user :: User, groupInfo :: GroupInfo, members :: [GroupMember], withMessages :: Bool}
|
||||
| CRGroupsList {user :: User, groups :: [(GroupInfo, GroupSummary)]}
|
||||
| CRGroupsList {user :: User, groups :: [GroupInfoSummary]}
|
||||
| CRSentGroupInvitation {user :: User, groupInfo :: GroupInfo, contact :: Contact, member :: GroupMember}
|
||||
| CRFileTransferStatus User (FileTransfer, [Integer]) -- TODO refactor this type to FileTransferStatus
|
||||
| CRFileTransferStatusXFTP User AChatItem
|
||||
@@ -804,12 +801,10 @@ data ChatEvent
|
||||
| CEvtSndFileStart {user :: User, chatItem :: AChatItem, sndFileTransfer :: SndFileTransfer}
|
||||
| CEvtSndFileComplete {user :: User, chatItem :: AChatItem, sndFileTransfer :: SndFileTransfer}
|
||||
| CEvtSndFileRcvCancelled {user :: User, chatItem_ :: Maybe AChatItem, sndFileTransfer :: SndFileTransfer}
|
||||
| CEvtSndFileStartXFTP {user :: User, chatItem :: AChatItem, fileTransferMeta :: FileTransferMeta} -- not used
|
||||
| CEvtSndFileProgressXFTP {user :: User, chatItem_ :: Maybe AChatItem, fileTransferMeta :: FileTransferMeta, sentSize :: Int64, totalSize :: Int64}
|
||||
| CEvtSndFileRedirectStartXFTP {user :: User, fileTransferMeta :: FileTransferMeta, redirectMeta :: FileTransferMeta}
|
||||
| CEvtSndFileCompleteXFTP {user :: User, chatItem :: AChatItem, fileTransferMeta :: FileTransferMeta}
|
||||
| CEvtSndStandaloneFileComplete {user :: User, fileTransferMeta :: FileTransferMeta, rcvURIs :: [Text]}
|
||||
| CEvtSndFileCancelledXFTP {user :: User, chatItem_ :: Maybe AChatItem, fileTransferMeta :: FileTransferMeta}
|
||||
| CEvtSndFileError {user :: User, chatItem_ :: Maybe AChatItem, fileTransferMeta :: FileTransferMeta, errorMessage :: Text}
|
||||
| CEvtSndFileWarning {user :: User, chatItem_ :: Maybe AChatItem, fileTransferMeta :: FileTransferMeta, errorMessage :: Text}
|
||||
| CEvtContactConnecting {user :: User, contact :: Contact}
|
||||
|
||||
@@ -693,6 +693,7 @@ processChatCommand vr nm = \case
|
||||
let msgIds = itemsMsgIds items
|
||||
events = L.nonEmpty $ map (\msgId -> XMsgDel msgId Nothing $ toMsgScope gInfo <$> chatScopeInfo) msgIds
|
||||
mapM_ (sendGroupMessages user gInfo Nothing recipients) events
|
||||
-- TODO delGroupChatItems sends deletion events too. Are they needed?
|
||||
delGroupChatItems user gInfo chatScopeInfo items False
|
||||
pure $ CRChatItemsDeleted user deletions True False
|
||||
CTLocal -> do
|
||||
@@ -1580,7 +1581,7 @@ processChatCommand vr nm = \case
|
||||
case memberConnId m of
|
||||
Just connId -> do
|
||||
connectionStats <- withAgent (\a -> switchConnectionAsync a "" connId)
|
||||
pure $ CEvtGroupMemberSwitchStarted user g m connectionStats
|
||||
pure $ CRGroupMemberSwitchStarted user g m connectionStats
|
||||
_ -> throwChatError CEGroupMemberNotActive
|
||||
APIAbortSwitchContact contactId -> withUser $ \user -> do
|
||||
ct <- withFastStore $ \db -> getContact db vr user contactId
|
||||
@@ -1594,7 +1595,7 @@ processChatCommand vr nm = \case
|
||||
case memberConnId m of
|
||||
Just connId -> do
|
||||
connectionStats <- withAgent $ \a -> abortConnectionSwitch a connId
|
||||
pure $ CEvtGroupMemberSwitchAborted user g m connectionStats
|
||||
pure $ CRGroupMemberSwitchAborted user g m connectionStats
|
||||
_ -> throwChatError CEGroupMemberNotActive
|
||||
APISyncContactRatchet contactId force -> withUser $ \user -> withContactLock "syncContactRatchet" contactId $ do
|
||||
ct <- withFastStore $ \db -> getContact db vr user contactId
|
||||
@@ -1754,8 +1755,9 @@ processChatCommand vr nm = \case
|
||||
createDirectConnection db newUser agConnId ccLink' Nothing ConnNew Nothing subMode initialChatVersion PQSupportOn
|
||||
deleteAgentConnectionAsync (aConnId' conn)
|
||||
pure conn'
|
||||
APIConnectPlan userId cLink -> withUserId userId $ \user ->
|
||||
APIConnectPlan userId (Just cLink) -> withUserId userId $ \user ->
|
||||
uncurry (CRConnectionPlan user) <$> connectPlan user cLink
|
||||
APIConnectPlan _ Nothing -> throwChatError CEInvalidConnReq
|
||||
APIPrepareContact userId accLink contactSLinkData -> withUserId userId $ \user -> do
|
||||
let ContactShortLinkData {profile, message, business} = contactSLinkData
|
||||
welcomeSharedMsgId <- forM message $ \_ -> getSharedMsgId
|
||||
@@ -1885,7 +1887,7 @@ processChatCommand vr nm = \case
|
||||
toView $ CEvtNewChatItems user [ci]
|
||||
pure $ CRStartedConnectionToGroup user gInfo' customUserProfile
|
||||
CVRConnectedContact _ct -> throwChatError $ CEException "contact already exists when connecting to group"
|
||||
APIConnect userId incognito acl -> withUserId userId $ \user -> case acl of
|
||||
APIConnect userId incognito (Just acl) -> withUserId userId $ \user -> case acl of
|
||||
ACCL SCMInvitation ccLink -> do
|
||||
(conn, incognitoProfile) <- connectViaInvitation user incognito ccLink Nothing
|
||||
let pcc = mkPendingContactConnection conn $ Just ccLink
|
||||
@@ -1894,6 +1896,7 @@ processChatCommand vr nm = \case
|
||||
connectViaContact user Nothing incognito ccLink Nothing Nothing >>= \case
|
||||
CVRConnectedContact ct -> pure $ CRContactAlreadyExists user ct
|
||||
CVRSentInvitation conn incognitoProfile -> pure $ CRSentInvitation user (mkPendingContactConnection conn Nothing) incognitoProfile
|
||||
APIConnect _ _ Nothing -> throwChatError CEInvalidConnReq
|
||||
Connect incognito (Just cLink@(ACL m cLink')) -> withUser $ \user -> do
|
||||
(ccLink, plan) <- connectPlan user cLink `catchChatError` \e -> case cLink' of CLFull cReq -> pure (ACCL m (CCLink cReq Nothing), CPInvitationLink (ILPOk Nothing)); _ -> throwError e
|
||||
connectWithPlan user incognito ccLink plan
|
||||
@@ -1918,6 +1921,10 @@ processChatCommand vr nm = \case
|
||||
ListContacts -> withUser $ \User {userId} ->
|
||||
processChatCommand vr nm $ APIListContacts userId
|
||||
APICreateMyAddress userId -> withUserId userId $ \user -> do
|
||||
withFastStore' (\db -> runExceptT $ getUserAddress db user) >>= \case
|
||||
Left SEUserContactLinkNotFound -> pure ()
|
||||
Left e -> throwError $ ChatErrorStore e
|
||||
Right _ -> throwError $ ChatErrorStore SEDuplicateContactLink
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
userData <- contactShortLinkData (userProfileToSend user Nothing Nothing False) Nothing
|
||||
-- TODO [certs rcv]
|
||||
@@ -2186,6 +2193,7 @@ processChatCommand vr nm = \case
|
||||
Nothing -> throwChatError $ CEContactNotActive ct
|
||||
APIAcceptMember groupId gmId role -> withUser $ \user@User {userId} -> do
|
||||
(gInfo, m) <- withFastStore $ \db -> (,) <$> getGroupInfo db vr user groupId <*> getGroupMemberById db vr user gmId
|
||||
-- TODO check that user's role is > role, possibly restrict role to only observer and member
|
||||
assertUserGroupRole gInfo GRModerator
|
||||
case memberStatus m of
|
||||
GSMemPendingApproval | memberCategory m == GCInviteeMember -> do -- only host can approve
|
||||
@@ -2365,8 +2373,9 @@ processChatCommand vr nm = \case
|
||||
APIRemoveMembers {groupId, groupMemberIds, withMessages} -> withUser $ \user ->
|
||||
withGroupLock "removeMembers" groupId $ do
|
||||
Group gInfo members <- withFastStore $ \db -> getGroup db vr user groupId
|
||||
let (count, invitedMems, pendingApprvMems, pendingRvwMems, currentMems, maxRole, anyAdmin) = selectMembers members
|
||||
memCount = S.size groupMemberIds
|
||||
let (count, invitedMems, pendingApprvMems, pendingRvwMems, currentMems, maxRole, anyAdmin) = selectMembers gmIds members
|
||||
gmIds = S.fromList $ L.toList groupMemberIds
|
||||
memCount = length groupMemberIds
|
||||
when (count /= memCount) $ throwChatError CEGroupMemberNotFound
|
||||
when (memCount > 1 && anyAdmin) $ throwCmdError "can't remove multiple members when admins selected"
|
||||
assertUserGroupRole gInfo $ max GRAdmin maxRole
|
||||
@@ -2389,11 +2398,11 @@ processChatCommand vr nm = \case
|
||||
when withMessages $ deleteMessages user gInfo' deleted
|
||||
pure $ CRUserDeletedMembers user gInfo' deleted withMessages -- same order is not guaranteed
|
||||
where
|
||||
selectMembers :: [GroupMember] -> (Int, [GroupMember], [GroupMember], [GroupMember], [GroupMember], GroupMemberRole, Bool)
|
||||
selectMembers = foldl' addMember (0, [], [], [], [], GRObserver, False)
|
||||
selectMembers :: S.Set GroupMemberId -> [GroupMember] -> (Int, [GroupMember], [GroupMember], [GroupMember], [GroupMember], GroupMemberRole, Bool)
|
||||
selectMembers gmIds = foldl' addMember (0, [], [], [], [], GRObserver, False)
|
||||
where
|
||||
addMember acc@(n, invited, pendingApprv, pendingRvw, current, maxRole, anyAdmin) m@GroupMember {groupMemberId, memberStatus, memberRole}
|
||||
| groupMemberId `S.member` groupMemberIds =
|
||||
| groupMemberId `S.member` gmIds =
|
||||
let maxRole' = max maxRole memberRole
|
||||
anyAdmin' = anyAdmin || memberRole >= GRAdmin
|
||||
n' = n + 1
|
||||
@@ -2483,7 +2492,7 @@ processChatCommand vr nm = \case
|
||||
RemoveMembers gName gMemberNames withMessages -> withUser $ \user -> do
|
||||
(gId, gMemberIds) <- withStore $ \db -> do
|
||||
gId <- getGroupIdByName db user gName
|
||||
gMemberIds <- S.fromList <$> mapM (getGroupMemberIdByName db user gId) (S.toList gMemberNames)
|
||||
gMemberIds <- mapM (getGroupMemberIdByName db user gId) gMemberNames
|
||||
pure (gId, gMemberIds)
|
||||
processChatCommand vr nm $ APIRemoveMembers gId gMemberIds withMessages
|
||||
LeaveGroup gName -> withUser $ \user -> do
|
||||
@@ -3452,7 +3461,7 @@ processChatCommand vr nm = \case
|
||||
case plan of
|
||||
CPContactAddress (CAPContactViaAddress Contact {contactId}) ->
|
||||
processChatCommand vr nm $ APIConnectContactViaAddress userId incognito contactId
|
||||
_ -> processChatCommand vr nm $ APIConnect userId incognito ccLink
|
||||
_ -> processChatCommand vr nm $ APIConnect userId incognito $ Just ccLink
|
||||
| otherwise = pure $ CRConnectionPlan user ccLink plan
|
||||
invitationRequestPlan :: User -> ConnReqInvitation -> Maybe ContactShortLinkData -> CM ConnectionPlan
|
||||
invitationRequestPlan user cReq contactSLinkData_ = do
|
||||
@@ -4518,7 +4527,7 @@ chatCommandP =
|
||||
("/member role " <|> "/mr ") *> char_ '#' *> (MemberRole <$> displayNameP <* A.space <* char_ '@' <*> displayNameP <*> memberRole),
|
||||
"/block for all #" *> (BlockForAll <$> displayNameP <* A.space <*> (char_ '@' *> displayNameP) <*> pure True),
|
||||
"/unblock for all #" *> (BlockForAll <$> displayNameP <* A.space <*> (char_ '@' *> displayNameP) <*> pure False),
|
||||
("/remove " <|> "/rm ") *> char_ '#' *> (RemoveMembers <$> displayNameP <* A.space <*> (S.fromList <$> (char_ '@' *> displayNameP) `A.sepBy1'` A.char ',') <*> (" messages=" *> onOffP <|> pure False)),
|
||||
("/remove " <|> "/rm ") *> char_ '#' *> (RemoveMembers <$> displayNameP <* A.space <*> (L.fromList <$> (char_ '@' *> displayNameP) `A.sepBy1'` A.char ',') <*> (" messages=" *> onOffP <|> pure False)),
|
||||
("/leave " <|> "/l ") *> char_ '#' *> (LeaveGroup <$> displayNameP),
|
||||
("/delete #" <|> "/d #") *> (DeleteGroup <$> displayNameP),
|
||||
("/delete " <|> "/d ") *> char_ '@' *> (DeleteContact <$> displayNameP <*> chatDeleteMode),
|
||||
@@ -4551,7 +4560,7 @@ chatCommandP =
|
||||
(">#" <|> "> #") *> (SendGroupMessageQuote <$> displayNameP <* A.space <* char_ '@' <*> (Just <$> displayNameP) <* A.space <*> quotedMsg <*> msgTextP),
|
||||
"/_contacts " *> (APIListContacts <$> A.decimal),
|
||||
"/contacts" $> ListContacts,
|
||||
"/_connect plan " *> (APIConnectPlan <$> A.decimal <* A.space <*> strP),
|
||||
"/_connect plan " *> (APIConnectPlan <$> A.decimal <* A.space <*> ((Just <$> strP) <|> A.takeTill (== ' ') $> Nothing)),
|
||||
"/_prepare contact " *> (APIPrepareContact <$> A.decimal <* A.space <*> connLinkP <* A.space <*> jsonP),
|
||||
"/_prepare group " *> (APIPrepareGroup <$> A.decimal <* A.space <*> connLinkP' <* A.space <*> jsonP),
|
||||
"/_set contact user @" *> (APIChangePreparedContactUser <$> A.decimal <* A.space <*> A.decimal),
|
||||
@@ -4559,7 +4568,7 @@ chatCommandP =
|
||||
"/_connect contact @" *> (APIConnectPreparedContact <$> A.decimal <*> incognitoOnOffP <*> optional (A.space *> msgContentP)),
|
||||
"/_connect group #" *> (APIConnectPreparedGroup <$> A.decimal <*> incognitoOnOffP <*> optional (A.space *> msgContentP)),
|
||||
"/_connect " *> (APIAddContact <$> A.decimal <*> incognitoOnOffP),
|
||||
"/_connect " *> (APIConnect <$> A.decimal <*> incognitoOnOffP <* A.space <*> connLinkP),
|
||||
"/_connect " *> (APIConnect <$> A.decimal <*> incognitoOnOffP <* A.space <*> connLinkP_),
|
||||
"/_set incognito :" *> (APISetConnectionIncognito <$> A.decimal <* A.space <*> onOffP),
|
||||
"/_set conn user :" *> (APIChangeConnectionUser <$> A.decimal <* A.space <*> A.decimal),
|
||||
("/connect" <|> "/c") *> (AddContact <$> incognitoP),
|
||||
@@ -4677,6 +4686,8 @@ chatCommandP =
|
||||
cReq <- strP
|
||||
sLink_ <- optional (A.space *> strP)
|
||||
pure $ CCLink cReq sLink_
|
||||
connLinkP_ =
|
||||
((Just <$> connLinkP) <|> A.takeTill (== ' ') $> Nothing)
|
||||
incognitoP = (A.space *> ("incognito" <|> "i")) $> True <|> pure False
|
||||
incognitoOnOffP = (A.space *> "incognito=" *> onOffP) <|> pure False
|
||||
imagePrefix = (<>) <$> "data:" <*> ("image/png;base64," <|> "image/jpg;base64,")
|
||||
|
||||
@@ -311,7 +311,7 @@ markdownText (FormattedText f_ t) = case f_ of
|
||||
Just cStr -> cStr <> t `T.snoc` '!'
|
||||
Nothing -> t
|
||||
colorStr = \case
|
||||
Red -> Just "!1 "
|
||||
Red -> Just "!1 "
|
||||
Green -> Just "!2 "
|
||||
Blue -> Just "!3 "
|
||||
Yellow -> Just "!4 "
|
||||
|
||||
@@ -104,7 +104,7 @@ chatTypeStr = \case
|
||||
chatNameStr :: ChatName -> String
|
||||
chatNameStr (ChatName cType name) = T.unpack $ chatTypeStr cType <> if T.any isSpace name then "'" <> name <> "'" else name
|
||||
|
||||
data ChatRef = ChatRef ChatType Int64 (Maybe GroupChatScope)
|
||||
data ChatRef = ChatRef {chatType :: ChatType, chatId :: Int64, chatScope :: Maybe GroupChatScope}
|
||||
deriving (Eq, Show, Ord)
|
||||
|
||||
data ChatInfo (c :: ChatType) where
|
||||
@@ -1499,6 +1499,7 @@ instance (ChatTypeI c, MsgDirectionI d) => ToJSON (JSONAnyChatItem c d) where
|
||||
toJSON = $(JQ.mkToJSON defaultJSON ''JSONAnyChatItem)
|
||||
toEncoding = $(JQ.mkToEncoding defaultJSON ''JSONAnyChatItem)
|
||||
|
||||
-- if JSON encoding changes, update AChatItem type definition in bots/src/API/Docs/Types.hs
|
||||
instance FromJSON AChatItem where
|
||||
parseJSON = J.withObject "AChatItem" $ \o -> do
|
||||
AChatInfo c chatInfo <- o .: "chatInfo"
|
||||
@@ -1560,6 +1561,7 @@ instance ChatTypeI c => ToJSON (JSONCIReaction c d) where
|
||||
toJSON = $(JQ.mkToJSON defaultJSON ''JSONCIReaction)
|
||||
toEncoding = $(JQ.mkToEncoding defaultJSON ''JSONCIReaction)
|
||||
|
||||
-- if JSON encoding changes, update ACIReaction type definition in bots/src/API/Docs/Types.hs
|
||||
instance FromJSON ACIReaction where
|
||||
parseJSON = J.withObject "ACIReaction" $ \o -> do
|
||||
ACIR c d reaction <- o .: "chatReaction"
|
||||
|
||||
@@ -968,10 +968,10 @@ getUserGroupDetails db vr User {userId, userContactId} _contactId_ search_ = do
|
||||
where
|
||||
search = maybe "" (map toLower) search_
|
||||
|
||||
getUserGroupsWithSummary :: DB.Connection -> VersionRangeChat -> User -> Maybe ContactId -> Maybe String -> IO [(GroupInfo, GroupSummary)]
|
||||
getUserGroupsWithSummary :: DB.Connection -> VersionRangeChat -> User -> Maybe ContactId -> Maybe String -> IO [GroupInfoSummary]
|
||||
getUserGroupsWithSummary db vr user _contactId_ search_ =
|
||||
getUserGroupDetails db vr user _contactId_ search_
|
||||
>>= mapM (\g@GroupInfo {groupId} -> (g,) <$> getGroupSummary db user groupId)
|
||||
>>= mapM (\g@GroupInfo {groupId} -> GIS g <$> getGroupSummary db user groupId)
|
||||
|
||||
-- the statuses on non-current members should match memberCurrent' function
|
||||
getGroupSummary :: DB.Connection -> User -> GroupId -> IO GroupSummary
|
||||
|
||||
@@ -952,10 +952,6 @@ Plan:
|
||||
Query: INSERT INTO xftp_servers (xftp_host, xftp_port, xftp_key_hash) VALUES (?,?,?)
|
||||
Plan:
|
||||
|
||||
Query: SELECT 1 FROM connections WHERE conn_id = ? AND deleted_at_wait_delivery < ? LIMIT 1
|
||||
Plan:
|
||||
SEARCH connections USING PRIMARY KEY (conn_id=?)
|
||||
|
||||
Query: SELECT 1 FROM encrypted_rcv_message_hashes WHERE conn_id = ? AND hash = ? LIMIT 1
|
||||
Plan:
|
||||
SEARCH encrypted_rcv_message_hashes USING COVERING INDEX idx_encrypted_rcv_message_hashes_hash (conn_id=? AND hash=?)
|
||||
|
||||
@@ -509,6 +509,9 @@ data GroupSummary = GroupSummary
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
data GroupInfoSummary = GIS {groupInfo :: GroupInfo, groupSummary :: GroupSummary}
|
||||
deriving (Show)
|
||||
|
||||
data ContactOrGroup = CGContact Contact | CGGroup GroupInfo [GroupMember]
|
||||
|
||||
data PreparedChatEntity = PCEContact Contact | PCEGroup {groupInfo :: GroupInfo, hostMember :: GroupMember}
|
||||
@@ -1350,10 +1353,10 @@ data RcvFileDescr = RcvFileDescr
|
||||
|
||||
data RcvFileStatus
|
||||
= RFSNew
|
||||
| RFSAccepted RcvFileInfo
|
||||
| RFSConnected RcvFileInfo
|
||||
| RFSComplete RcvFileInfo
|
||||
| RFSCancelled (Maybe RcvFileInfo)
|
||||
| RFSAccepted {fileInfo :: RcvFileInfo}
|
||||
| RFSConnected {fileInfo :: RcvFileInfo}
|
||||
| RFSComplete {fileInfo :: RcvFileInfo}
|
||||
| RFSCancelled {fileInfo_ :: Maybe RcvFileInfo}
|
||||
deriving (Eq, Show)
|
||||
|
||||
rcvFileComplete :: RcvFileStatus -> Bool
|
||||
@@ -2005,6 +2008,8 @@ $(JQ.deriveJSON defaultJSON ''Group)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''GroupSummary)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''GroupInfoSummary)
|
||||
|
||||
instance FromField MsgFilter where fromField = fromIntField_ msgFilterIntP
|
||||
|
||||
instance ToField MsgFilter where toField = toField . msgFilterInt
|
||||
|
||||
@@ -135,9 +135,9 @@ chatResponseToView hu cfg@ChatConfig {logLevel, showReactions, testView} liveIte
|
||||
"server queue info: " <> viewJSON qInfo
|
||||
]
|
||||
CRContactSwitchStarted {} -> ["switch started"]
|
||||
CEvtGroupMemberSwitchStarted {} -> ["switch started"]
|
||||
CRGroupMemberSwitchStarted {} -> ["switch started"]
|
||||
CRContactSwitchAborted {} -> ["switch aborted"]
|
||||
CEvtGroupMemberSwitchAborted {} -> ["switch aborted"]
|
||||
CRGroupMemberSwitchAborted {} -> ["switch aborted"]
|
||||
CRContactRatchetSyncStarted {} -> ["connection synchronization started"]
|
||||
CRGroupMemberRatchetSyncStarted {} -> ["connection synchronization started"]
|
||||
CRConnectionVerified u verified code -> ttyUser u [plain $ if verified then "connection verified" else "connection not verified, current code is " <> code]
|
||||
@@ -432,12 +432,10 @@ chatEventToView hu ChatConfig {logLevel, showReactions, showReceipts, testView}
|
||||
CEvtRcvFileWarning u Nothing e ft -> ttyUser u $ receivingFileStandalone "warning: " ft <> [sShow e]
|
||||
CEvtSndFileStart u _ ft -> ttyUser u $ sendingFile_ "started" ft
|
||||
CEvtSndFileComplete u _ ft -> ttyUser u $ sendingFile_ "completed" ft
|
||||
CEvtSndFileStartXFTP {} -> []
|
||||
CEvtSndFileProgressXFTP {} -> []
|
||||
CEvtSndFileRedirectStartXFTP u ft ftRedirect -> ttyUser u $ standaloneUploadRedirect ft ftRedirect
|
||||
CEvtSndStandaloneFileComplete u ft uris -> ttyUser u $ standaloneUploadComplete ft uris
|
||||
CEvtSndFileCompleteXFTP u ci _ -> ttyUser u $ uploadingFile "completed" ci
|
||||
CEvtSndFileCancelledXFTP {} -> []
|
||||
CEvtSndFileError u Nothing ft e -> ttyUser u $ uploadingFileStandalone "error" ft <> [plain e]
|
||||
CEvtSndFileError u (Just ci) _ e -> ttyUser u $ uploadingFile "error" ci <> [plain e]
|
||||
CEvtSndFileWarning u Nothing ft e -> ttyUser u $ uploadingFileStandalone "warning: " ft <> [plain e]
|
||||
@@ -1343,13 +1341,13 @@ viewContactConnected ct userIncognitoProfile testView =
|
||||
Nothing ->
|
||||
[ttyFullContact ct <> ": contact is connected"]
|
||||
|
||||
viewGroupsList :: [(GroupInfo, GroupSummary)] -> [StyledString]
|
||||
viewGroupsList :: [GroupInfoSummary] -> [StyledString]
|
||||
viewGroupsList [] = ["you have no groups!", "to create: " <> highlight' "/g <name>"]
|
||||
viewGroupsList gs = map groupSS $ sortOn (ldn_ . fst) gs
|
||||
viewGroupsList gs = map groupSS $ sortOn ldn_ gs
|
||||
where
|
||||
ldn_ :: GroupInfo -> Text
|
||||
ldn_ GroupInfo {localDisplayName} = T.toLower localDisplayName
|
||||
groupSS (g@GroupInfo {membership, chatSettings = ChatSettings {enableNtfs}}, GroupSummary {currentMembers}) =
|
||||
ldn_ :: GroupInfoSummary -> Text
|
||||
ldn_ (GIS GroupInfo {localDisplayName} _) = T.toLower localDisplayName
|
||||
groupSS (GIS g@GroupInfo {membership, chatSettings = ChatSettings {enableNtfs}} GroupSummary {currentMembers}) =
|
||||
case memberStatus membership of
|
||||
GSMemInvited -> groupInvitation' g
|
||||
s -> membershipIncognito g <> ttyFullGroup g <> viewMemberStatus s <> alias g
|
||||
|
||||
Reference in New Issue
Block a user