From 712effe34f55cefee3c8d0e9a71bc7327fadf67a Mon Sep 17 00:00:00 2001 From: Evgeny Date: Sat, 4 Jul 2026 12:16:45 +0100 Subject: [PATCH] core: use concurrency-safe agent async api (#7035) * core: use concurrency-safe agent async api * simplexmq * simplexmq * combine transactions (build fails, names need to be merged first) * fix build * update simplexmq * remove uncurry * remove createAgentConnectionAsync * remove prepareJoinMemberContact * mapM * fix tests --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> --- cabal.project | 2 +- scripts/nix/sha256map.nix | 2 +- src/Simplex/Chat/Library/Internal.hs | 70 +++++++++++++++++--------- src/Simplex/Chat/Library/Subscriber.hs | 39 ++++++++------ tests/Bots/DirectoryTests.hs | 4 +- 5 files changed, 73 insertions(+), 44 deletions(-) diff --git a/cabal.project b/cabal.project index bd8e4ac384..fb8ac872d1 100644 --- a/cabal.project +++ b/cabal.project @@ -21,7 +21,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: 836254a4c6bd5e17b40acbcaf77a83802d44919e + tag: a45d764eaa82fb4f26f8dfeb537c1464c8dad972 source-repository-package type: git diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index 4ca9cefc7a..080a1ec635 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."836254a4c6bd5e17b40acbcaf77a83802d44919e" = "1g4385sv7i1h63yk3ch2kw8hprak1dzvr34v9mn6xz0fy10g8xlg"; + "https://github.com/simplex-chat/simplexmq.git"."a45d764eaa82fb4f26f8dfeb537c1464c8dad972" = "1f76904lwhxz9jkix0viaj715a88dlhr0x9v7jz79javcz57zcxp"; "https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38"; "https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d"; "https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl"; diff --git a/src/Simplex/Chat/Library/Internal.hs b/src/Simplex/Chat/Library/Internal.hs index 62ce525cbd..20d0d10292 100644 --- a/src/Simplex/Chat/Library/Internal.hs +++ b/src/Simplex/Chat/Library/Internal.hs @@ -929,13 +929,15 @@ acceptContactRequestAsync profileToSend <- presentUserBadge user incognitoProfile $ userProfileDirect user (fromIncognitoProfile <$> incognitoProfile) (Just ct) True cxt <- chatStoreCxt let chatV = vr cxt `peerConnChatVersion` cReqChatVRange - (cmdId, acId) <- agentAcceptContactAsync user True cReqInvId (XInfo profileToSend) subMode cReqPQSup chatV + (cmdId, acId) <- prepareAgentAccept user True cReqInvId cReqPQSup currentTs <- liftIO getCurrentTime - withStore $ \db -> do + ct' <- withStore $ \db -> do forM_ xContactId $ \xcId -> liftIO $ setContactAcceptedXContactId db ct xcId Connection {connId} <- liftIO $ createAcceptedContactConn db user (Just uclId) contactId acId chatV cReqChatVRange cReqPQSup incognitoProfile subMode currentTs liftIO $ setCommandConnId db user cmdId connId getContact db cxt user contactId + agentAcceptContactAsync cmdId acId True cReqInvId (XInfo profileToSend) cReqPQSup chatV subMode + pure ct' acceptGroupJoinRequestAsync :: User -> Int64 -> GroupInfo -> InvitationId -> VersionRangeChat -> Profile -> Maybe XContactId -> Maybe MemberId -> Maybe SharedMsgId -> GroupAcceptance -> GroupMemberRole -> Maybe IncognitoProfile -> Maybe MemberKey -> Maybe GroupMember -> CM GroupMember acceptGroupJoinRequestAsync @@ -983,10 +985,12 @@ acceptGroupJoinRequestAsync } subMode <- chatReadVar subscriptionMode let chatV = vr cxt `peerConnChatVersion` cReqChatVRange - connIds <- agentAcceptContactAsync user True cReqInvId msg subMode PQSupportOff chatV - withStore $ \db -> do - liftIO $ createJoiningMemberConnection db user uclId connIds chatV cReqChatVRange groupMemberId subMode + (cmdId, acId) <- prepareAgentAccept user True cReqInvId PQSupportOff + m <- withStore $ \db -> do + liftIO $ createJoiningMemberConnection db user uclId (cmdId, acId) chatV cReqChatVRange groupMemberId subMode getGroupMemberById db cxt user groupMemberId + agentAcceptContactAsync cmdId acId True cReqInvId msg PQSupportOff chatV subMode + pure m acceptGroupJoinSendRejectAsync :: User -> Int64 -> GroupInfo -> InvitationId -> VersionRangeChat -> Profile -> Maybe XContactId -> GroupRejectionReason -> CM GroupMember acceptGroupJoinSendRejectAsync @@ -1013,10 +1017,12 @@ acceptGroupJoinSendRejectAsync } subMode <- chatReadVar subscriptionMode let chatV = vr cxt `peerConnChatVersion` cReqChatVRange - connIds <- agentAcceptContactAsync user False cReqInvId msg subMode PQSupportOff chatV - withStore $ \db -> do - liftIO $ createJoiningMemberConnection db user uclId connIds chatV cReqChatVRange groupMemberId subMode + (cmdId, acId) <- prepareAgentAccept user False cReqInvId PQSupportOff + m <- withStore $ \db -> do + liftIO $ createJoiningMemberConnection db user uclId (cmdId, acId) chatV cReqChatVRange groupMemberId subMode getGroupMemberById db cxt user groupMemberId + agentAcceptContactAsync cmdId acId False cReqInvId msg PQSupportOff chatV subMode + pure m acceptBusinessJoinRequestAsync :: User -> Int64 -> GroupInfo -> GroupMember -> UserContactRequest -> CM (GroupInfo, GroupMember) acceptBusinessJoinRequestAsync @@ -1045,10 +1051,11 @@ acceptBusinessJoinRequestAsync } subMode <- chatReadVar subscriptionMode let chatV = vr cxt `peerConnChatVersion` cReqChatVRange - connIds <- agentAcceptContactAsync user True cReqInvId msg subMode PQSupportOff chatV + (cmdId, acId) <- prepareAgentAccept user True cReqInvId PQSupportOff withStore' $ \db -> do forM_ xContactId $ \xcId -> setBusinessChatAcceptedXContactId db gInfo xcId - createJoiningMemberConnection db user uclId connIds chatV cReqChatVRange groupMemberId subMode + createJoiningMemberConnection db user uclId (cmdId, acId) chatV cReqChatVRange groupMemberId subMode + agentAcceptContactAsync cmdId acId True cReqInvId msg PQSupportOff chatV subMode let cd = CDGroupSnd gInfo Nothing -- TODO [short links] move to profileContactRequest? createInternalChatItem user cd (CISndGroupE2EEInfo $ e2eInfoGroup gInfo) Nothing @@ -1071,12 +1078,14 @@ acceptRelayJoinRequestAsync subMode <- chatReadVar subscriptionMode cxt <- chatStoreCxt let chatV = vr cxt `peerConnChatVersion` cReqChatVRange - connIds <- agentAcceptContactAsync user True cReqInvId msg subMode PQSupportOff chatV - withStore $ \db -> do - liftIO $ createJoiningMemberConnection db user uclId connIds chatV cReqChatVRange groupMemberId subMode + (cmdId, acId) <- prepareAgentAccept user True cReqInvId PQSupportOff + r <- withStore $ \db -> do + liftIO $ createJoiningMemberConnection db user uclId (cmdId, acId) chatV cReqChatVRange groupMemberId subMode gInfo' <- liftIO $ updateRelayOwnStatusFromTo db gInfo RSInvited RSAccepted ownerMember' <- getGroupMemberById db cxt user groupMemberId pure (gInfo', ownerMember') + agentAcceptContactAsync cmdId acId True cReqInvId msg PQSupportOff chatV subMode + pure r rejectRelayInvitationAsync :: User @@ -1096,9 +1105,10 @@ rejectRelayInvitationAsync user uclId cxt groupRelayInv invId reqChatVRange init subMode <- chatReadVar subscriptionMode chatVR <- chatVersionRange let chatV = chatVR `peerConnChatVersion` reqChatVRange - connIds <- agentAcceptContactAsync user False invId msg subMode PQSupportOff chatV + (cmdId, acId) <- prepareAgentAccept user False invId PQSupportOff withStore' $ \db -> - createJoiningMemberConnection db user uclId connIds chatV reqChatVRange groupMemberId subMode + createJoiningMemberConnection db user uclId (cmdId, acId) chatV reqChatVRange groupMemberId subMode + agentAcceptContactAsync cmdId acId False invId msg PQSupportOff chatV subMode businessGroupProfile :: Profile -> GroupPreferences -> GroupProfile businessGroupProfile Profile {displayName, fullName, shortDescr, image} groupPreferences = @@ -2752,18 +2762,24 @@ msgContentHasLink mc ft_ = case msgContentTag mc of MCLink_ -> True _ -> maybe False hasLinks ft_ -createAgentConnectionAsync :: ConnectionModeI c => User -> CommandFunction -> Bool -> SConnectionMode c -> SubscriptionMode -> CM (CommandId, ConnId) -createAgentConnectionAsync user cmdFunction enableNtfs cMode subMode = do +prepareAgentCreation :: ConnectionModeI c => User -> CommandFunction -> Bool -> SConnectionMode c -> CM (CommandId, ConnId) +prepareAgentCreation user cmdFunction enableNtfs cMode = do cmdId <- withStore' $ \db -> createCommand db user Nothing cmdFunction - connId <- withAgent $ \a -> createConnectionAsync a (aUserId user) (aCorrId cmdId) enableNtfs cMode IKPQOff subMode + connId <- withAgent $ \a -> prepareConnectionToCreate a (aUserId user) enableNtfs cMode PQSupportOff pure (cmdId, connId) -joinAgentConnectionAsync :: User -> Maybe Connection -> Bool -> ConnectionRequestUri c -> ConnInfo -> SubscriptionMode -> CM (CommandId, ConnId) -joinAgentConnectionAsync user conn_ enableNtfs cReqUri cInfo subMode = do +prepareAgentJoin :: User -> Maybe Connection -> Bool -> ConnectionRequestUri c -> CM (CommandId, ConnId) +prepareAgentJoin user conn_ enableNtfs cReqUri = do cmdId <- withStore' $ \db -> createCommand db user (dbConnId <$> conn_) CFJoinConn - connId <- withAgent $ \a -> joinConnectionAsync a (aUserId user) (aCorrId cmdId) (aConnId <$> conn_) enableNtfs cReqUri cInfo PQSupportOff subMode + connId <- case conn_ of + Just conn -> pure $ aConnId conn + Nothing -> withAgent $ \a -> prepareConnectionToJoin a (aUserId user) enableNtfs cReqUri PQSupportOff pure (cmdId, connId) +joinAgentConnectionAsync :: ConnectionModeI c => CommandId -> Bool -> ConnId -> Bool -> ConnectionRequestUri c -> ConnInfo -> SubscriptionMode -> CM () +joinAgentConnectionAsync cmdId updateConn connId enableNtfs cReqUri cInfo subMode = + withAgent $ \a -> joinConnectionAsync a (aCorrId cmdId) updateConn connId enableNtfs cReqUri cInfo PQSupportOff subMode + allowAgentConnectionAsync :: MsgEncodingI e => User -> Connection -> ConfirmationId -> ChatMsgEvent e -> CM () allowAgentConnectionAsync user conn@Connection {connId, pqSupport, connChatVersion} confId msg = do cmdId <- withStore' $ \db -> createCommand db user (Just connId) CFAllowConn @@ -2771,13 +2787,17 @@ allowAgentConnectionAsync user conn@Connection {connId, pqSupport, connChatVersi withAgent $ \a -> allowConnectionAsync a (aCorrId cmdId) (aConnId conn) confId dm withStore' $ \db -> updateConnectionStatus db conn ConnAccepted -agentAcceptContactAsync :: MsgEncodingI e => User -> Bool -> InvitationId -> ChatMsgEvent e -> SubscriptionMode -> PQSupport -> VersionChat -> CM (CommandId, ConnId) -agentAcceptContactAsync user enableNtfs invId msg subMode pqSup chatV = do +prepareAgentAccept :: User -> Bool -> InvitationId -> PQSupport -> CM (CommandId, ConnId) +prepareAgentAccept user enableNtfs invId pqSup = do cmdId <- withStore' $ \db -> createCommand db user Nothing CFAcceptContact - dm <- encodeConnInfoPQ pqSup chatV msg - connId <- withAgent $ \a -> acceptContactAsync a (aUserId user) (aCorrId cmdId) enableNtfs invId dm pqSup subMode + connId <- withAgent $ \a -> prepareConnectionToAccept a (aUserId user) enableNtfs invId pqSup pure (cmdId, connId) +agentAcceptContactAsync :: MsgEncodingI e => CommandId -> ConnId -> Bool -> InvitationId -> ChatMsgEvent e -> PQSupport -> VersionChat -> SubscriptionMode -> CM () +agentAcceptContactAsync cmdId connId enableNtfs invId msg pqSup chatV subMode = do + dm <- encodeConnInfoPQ pqSup chatV msg + withAgent $ \a -> acceptContactAsync a (aCorrId cmdId) connId enableNtfs invId dm pqSup subMode + deleteAgentConnectionAsync :: ConnId -> CM () deleteAgentConnectionAsync acId = deleteAgentConnectionAsync' acId False {-# INLINE deleteAgentConnectionAsync #-} diff --git a/src/Simplex/Chat/Library/Subscriber.hs b/src/Simplex/Chat/Library/Subscriber.hs index 408fae8105..52d70f1fae 100644 --- a/src/Simplex/Chat/Library/Subscriber.hs +++ b/src/Simplex/Chat/Library/Subscriber.hs @@ -639,9 +639,10 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage = forM_ gli_ $ \GroupLinkInfo {groupId, memberRole = gLinkMemRole} -> do groupInfo <- withStore $ \db -> getGroupInfo db cxt user groupId subMode <- chatReadVar subscriptionMode - groupConnIds <- createAgentConnectionAsync user CFCreateConnGrpInv True SCMInvitation subMode + groupConnIds@(cmdId, connId) <- prepareAgentCreation user CFCreateConnGrpInv True SCMInvitation gVar <- asks random withStore $ \db -> createNewContactMemberAsync db gVar user groupInfo ct' gLinkMemRole groupConnIds connChatVersion peerChatVRange subMode + withAgent $ \a -> createConnectionAsync a (aCorrId cmdId) connId True SCMInvitation CR.IKPQOff subMode -- TODO REMOVE LEGACY ^^^ SENT msgId proxy -> do void $ continueSending connEntity conn @@ -1242,7 +1243,8 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage = profileToSend <- presentUserBadge user incognitoProfile $ userProfileInGroup user gInfo incognitoProfile dm <- encodeXMemberConnInfo gInfo relayMemberId profileToSend subMode <- chatReadVar subscriptionMode - void $ joinAgentConnectionAsync user (Just conn) True cReq dm subMode + (cmdId, connId') <- prepareAgentJoin user (Just conn) True cReq + joinAgentConnectionAsync cmdId True connId' True cReq dm subMode CFGetRelayDataAccept -> do let GroupMember {memberId = MemberId expectedMemberId} = m if linkEntityId == Just expectedMemberId @@ -1639,12 +1641,12 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage = let sig = C.signatureBytes $ C.sign' privKey challenge msg = XGrpRelayTest challenge (Just sig) subMode <- chatReadVar subscriptionMode - chatVR <- chatVersionRange - let chatV = chatVR `peerConnChatVersion` chatVRange - (cmdId, acId) <- agentAcceptContactAsync user True invId msg subMode PQSupportOff chatV + let chatV = vr cxt `peerConnChatVersion` chatVRange + (cmdId, acId) <- prepareAgentAccept user True invId PQSupportOff withStore $ \db -> do Connection {connId = testCId} <- createRelayTestConnection db cxt user acId ConnAccepted chatV subMode liftIO $ setCommandConnId db user cmdId testCId + agentAcceptContactAsync cmdId acId True invId msg PQSupportOff chatV subMode | otherwise = messageError "relay test sent to non-relay link" where User {userChatRelay} = user @@ -2610,12 +2612,13 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage = then do subMode <- chatReadVar subscriptionMode dm <- encodeConnInfo $ XGrpAcpt membershipMemId - connIds <- joinAgentConnectionAsync user Nothing True connRequest dm subMode + connIds@(cmdId, acId) <- prepareAgentJoin user Nothing True connRequest withStore' $ \db -> do setViaGroupLinkUri db groupId connId createMemberConnectionAsync db user hostId connIds connChatVersion peerChatVRange subMode updateGroupMemberStatusById db userId hostId GSMemAccepted updateGroupMemberStatus db userId membership GSMemAccepted + joinAgentConnectionAsync cmdId False acId True connRequest dm subMode toView $ CEvtUserAcceptedGroupSent user gInfo {membership = membership {memberStatus = GSMemAccepted}} (Just ct) else do let content = CIRcvGroupInvitation (CIGroupInvitation {groupId, groupMemberId, localDisplayName, groupProfile, status = CIGISPending}) memRole @@ -3176,16 +3179,14 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage = Just (ChatVersionRange mcvr) | maxVersion mcvr >= groupDirectInvVersion -> do subMode <- chatReadVar subscriptionMode - -- [async agent commands] commands should be asynchronous, continuation is to send XGrpMemInv - have to remember one has completed and process on second - groupConnIds <- createConn subMode + groupConnIds@(cmdId, connId) <- prepareAgentCreation user CFCreateConnGrpMemInv (chatHasNtfs chatSettings) SCMInvitation let chatV = maybe (minVersion (vr cxt)) (\peerVR -> vr cxt `peerConnChatVersion` fromChatVRange peerVR) memChatVRange void $ withStore $ \db -> do reMember <- createIntroReMember db cxt user gInfo memInfo memRestrictions createIntroReMemberConn db user m reMember chatV memInfo groupConnIds subMode + withAgent $ \a -> createConnectionAsync a (aCorrId cmdId) connId (chatHasNtfs chatSettings) SCMInvitation CR.IKPQOff subMode | otherwise -> messageError "x.grp.mem.intro: member chat version range incompatible" _ -> messageError "x.grp.mem.intro can be only sent by host member" - where - createConn subMode = createAgentConnectionAsync user CFCreateConnGrpMemInv (chatHasNtfs chatSettings) SCMInvitation subMode sendXGrpMemInv :: Int64 -> Maybe ConnReqInvitation -> XGrpMemIntroCont -> CM () sendXGrpMemInv hostConnId directConnReq XGrpMemIntroCont {groupId, groupMemberId, memberId, groupConnReq} = do @@ -3228,12 +3229,16 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage = membershipProfile <- presentUserBadge user (incognitoMembershipProfile gInfo) $ redactedMemberProfile gInfo membership $ fromLocalProfile $ memberProfile membership dm <- encodeConnInfo $ XGrpMemInfo membershipMemId membershipProfile -- [async agent commands] no continuation needed, but commands should be asynchronous for stability - groupConnIds <- joinAgentConnectionAsync user Nothing (chatHasNtfs chatSettings) groupConnReq dm subMode - directConnIds <- forM directConnReq $ \dcr -> joinAgentConnectionAsync user Nothing True dcr dm subMode + let enableNtfsGrp = chatHasNtfs chatSettings + groupConnIds@(gCmdId, gAcId) <- prepareAgentJoin user Nothing enableNtfsGrp groupConnReq + directConnIds <- mapM (prepareAgentJoin user Nothing True) directConnReq let customUserProfileId = localProfileId <$> incognitoMembershipProfile gInfo mcvr = maybe chatInitialVRange fromChatVRange memChatVRange chatV = vr cxt `peerConnChatVersion` mcvr withStore' $ \db -> createIntroToMemberContact db user m toMember chatV mcvr groupConnIds directConnIds customUserProfileId subMode + joinAgentConnectionAsync gCmdId False gAcId enableNtfsGrp groupConnReq dm subMode + forM_ ((,) <$> directConnIds <*> directConnReq) $ \((dCmdId, dAcId), dcr) -> + joinAgentConnectionAsync dCmdId False dAcId True dcr dm subMode -- rollback defense (channels): apply an owner-signed role/removal only at a version >= the persisted -- roster_version (not the batch-constant gInfo, which a relay can stale by reordering events in one @@ -3753,11 +3758,12 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage = } joinExistingContact subMode mCt@Contact {contactId = mContactId} | autoAcceptMemberContacts user = do - (cmdId, acId) <- joinConn subMode + (cmdId, acId) <- prepareAgentJoin user Nothing True connReq mCt' <- withStore $ \db -> do updateMemberContactInvited db user mCt groupDirectInv void $ liftIO $ createMemberContactConn db user acId (Just cmdId) g mConn ConnJoined mContactId subMode getContact db cxt user mContactId + joinMemberContactAsync cmdId acId subMode securityCodeChanged mCt' createItems mCt' m | otherwise = do @@ -3771,13 +3777,14 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage = createItems mCt' m createNewContact subMode | autoAcceptMemberContacts user = do - (cmdId, acId) <- joinConn subMode + (cmdId, acId) <- prepareAgentJoin user Nothing True connReq -- [incognito] reuse membership incognito profile (mCt, m') <- withStore $ \db -> do (mContactId, m') <- liftIO $ createMemberContactInvited db user g m groupDirectInv void $ liftIO $ createMemberContactConn db user acId (Just cmdId) g mConn ConnJoined mContactId subMode mCt <- getContact db cxt user mContactId pure (mCt, m') + joinMemberContactAsync cmdId acId subMode createInternalChatItem user (CDDirectSnd mCt) CIChatBanner (Just epochStart) createItems mCt m' | otherwise = do @@ -3790,12 +3797,12 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage = createInternalChatItem user (CDDirectSnd mCt) CIChatBanner (Just epochStart) createInternalChatItem user (CDDirectRcv mCt) (CIRcvDirectEvent $ RDEGroupInvLinkReceived gp) Nothing createItems mCt m' - joinConn subMode = do + joinMemberContactAsync cmdId acId subMode = do -- [incognito] send membership incognito profile p <- presentUserBadge user (incognitoMembershipProfile g) $ userProfileDirect user (fromLocalProfile <$> incognitoMembershipProfile g) Nothing True -- TODO PQ should negotitate contact connection with PQSupportOn? (use encodeConnInfoPQ) dm <- encodeConnInfo $ XInfo p - joinAgentConnectionAsync user Nothing True connReq dm subMode + joinAgentConnectionAsync cmdId False acId True connReq dm subMode createItems mCt' m' = do (g', m'', scopeInfo) <- mkGroupChatScope g m' createInternalChatItem user (CDGroupRcv g' scopeInfo m'') (CIRcvGroupEvent RGEMemberCreatedContact) Nothing diff --git a/tests/Bots/DirectoryTests.hs b/tests/Bots/DirectoryTests.hs index 1c6da2e1ed..025bfe4f66 100644 --- a/tests/Bots/DirectoryTests.hs +++ b/tests/Bots/DirectoryTests.hs @@ -449,9 +449,9 @@ testJoinGroup ps = cath <## "" cath <## "Send captcha text to join the group privacy." captcha <- dropStrPrefix "#privacy (support) 'SimpleX Directory_1'> " . dropTime <$> getTermLine cath - cath #> ("#privacy (support) " <> captcha) cath <## "contact and member are merged: 'SimpleX Directory', #privacy 'SimpleX Directory_1'" cath <## "use @'SimpleX Directory' to send messages" + cath #> ("#privacy (support) " <> captcha) cath <# ("#privacy (support) 'SimpleX Directory'!> > cath " <> captcha) cath <## " Correct, you joined the group privacy" cath <## "#privacy: you joined the group" @@ -1277,6 +1277,8 @@ testKnocking ps = cath <## "#privacy: you joined the group, connecting to group moderators for admission to group" cath <## "#privacy: 'SimpleX Directory' accepted you to the group, pending review" bob <## "#privacy: 'SimpleX Directory' added cath (Catherine) to the group (connecting and pending review...), use /_accept member #1 3 to accept member" + cath <## "#privacy: member bob (Bob) is connected" + bob <## "#privacy: new member cath is connected and pending review, use /_accept member #1 3 to accept member" testCaptchaByDefault :: HasCallStack => TestParams -> IO () testCaptchaByDefault ps =