mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-23 04:22:13 +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:
1 parent
052b9ad628
commit
cf8bd7f6ac
32 files changed
+8447
-131
No files matched your search
@@ -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,")
|
||||
|
||||
Reference in new issue
Block a user