mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-28 20:08:16 +00:00
Merge branch 'master' into chat-relays
This commit is contained in:
@@ -12,7 +12,7 @@ import Control.Concurrent.Async
|
||||
import Control.Concurrent.STM
|
||||
import Control.Monad
|
||||
import qualified Data.ByteString.Char8 as B
|
||||
import Data.List.NonEmpty (NonEmpty)
|
||||
import Data.List.NonEmpty (NonEmpty (..))
|
||||
import qualified Data.List.NonEmpty as L
|
||||
import qualified Data.Map.Strict as M
|
||||
import Data.Maybe (isJust)
|
||||
@@ -26,6 +26,7 @@ import Simplex.Chat.Protocol (MsgContent (..))
|
||||
import Simplex.Chat.Store
|
||||
import Simplex.Chat.Types (Contact (..), ContactId, IsContact (..), User (..))
|
||||
import Simplex.Messaging.Agent.Protocol (CreatedConnLink (..))
|
||||
import Simplex.Messaging.Crypto.File (CryptoFile)
|
||||
import Simplex.Messaging.Encoding.String (strEncode)
|
||||
import System.Exit (exitFailure)
|
||||
|
||||
@@ -89,6 +90,13 @@ sendComposedMessages_ cc sendRef qmcs = do
|
||||
Right (CRNewChatItems {}) -> printLog cc CLLInfo $ "sent " <> show (length cms) <> " messages to " <> show sendRef
|
||||
r -> putStrLn $ "unexpected send message response: " <> show r
|
||||
|
||||
sendComposedMessageFile :: ChatController -> SendRef -> Maybe ChatItemId -> MsgContent -> CryptoFile -> IO ()
|
||||
sendComposedMessageFile cc sendRef qiId mc file = do
|
||||
let cm = ComposedMessage {fileSource = Just file, quotedItemId = qiId, msgContent = mc, mentions = M.empty}
|
||||
sendChatCmd cc (APISendMessages sendRef False Nothing (cm :| [])) >>= \case
|
||||
Right (CRNewChatItems {}) -> printLog cc CLLInfo $ "sent file message to " <> show sendRef
|
||||
r -> putStrLn $ "unexpected send message response: " <> show r
|
||||
|
||||
deleteMessage :: ChatController -> Contact -> ChatItemId -> IO ()
|
||||
deleteMessage cc ct chatItemId = do
|
||||
let cmd = APIDeleteChatItem (contactRef ct) [chatItemId] CIDMInternal
|
||||
|
||||
@@ -3656,8 +3656,8 @@ processChatCommand vr nm = \case
|
||||
pure gInfo
|
||||
sendGrpInvitation :: User -> Contact -> GroupInfo -> GroupMember -> ConnReqInvitation -> CM ()
|
||||
sendGrpInvitation user ct@Contact {contactId, localDisplayName} gInfo@GroupInfo {groupId, groupProfile, membership, businessChat} GroupMember {groupMemberId, memberId, memberRole = memRole} cReq = do
|
||||
currentMemCount <- withStore' $ \db -> getGroupCurrentMembersCount db user gInfo
|
||||
let GroupMember {memberRole = userRole, memberId = userMemberId} = membership
|
||||
let currentMemCount = fromIntegral $ currentMembers $ groupSummary gInfo
|
||||
GroupMember {memberRole = userRole, memberId = userMemberId} = membership
|
||||
groupInv =
|
||||
GroupInvitation
|
||||
{ fromMember = MemberIdRole userMemberId userRole,
|
||||
|
||||
@@ -338,13 +338,22 @@ quoteContent mc qmc ciFile_
|
||||
qTextOrFile = if T.null qText then qFileName else qText
|
||||
|
||||
prohibitedGroupContent :: GroupInfo -> GroupMember -> Maybe GroupChatScopeInfo -> MsgContent -> Maybe MarkdownList -> Maybe f -> Bool -> Maybe GroupFeature
|
||||
prohibitedGroupContent gInfo@GroupInfo {membership = GroupMember {memberRole = userRole}} m scopeInfo mc ft file_ sent
|
||||
| isVoice mc && not (groupFeatureMemberAllowed SGFVoice m gInfo) = Just GFVoice
|
||||
prohibitedGroupContent gInfo@GroupInfo {membership = mem@GroupMember {memberRole = userRole}} m scopeInfo mc ft file_ sent
|
||||
| isVoice mc && not (groupFeatureMemberAllowed SGFVoice m gInfo) && not hostApprovalVoice = Just GFVoice
|
||||
| isNothing scopeInfo && not (isVoice mc) && isJust file_ && not (groupFeatureMemberAllowed SGFFiles m gInfo) = Just GFFiles
|
||||
| isNothing scopeInfo && isReport mc && (badReportUser || not (groupFeatureAllowed SGFReports gInfo)) = Just GFReports
|
||||
| isNothing scopeInfo && prohibitedSimplexLinks gInfo m ft = Just GFSimplexLinks
|
||||
| otherwise = Nothing
|
||||
where
|
||||
hostApprovalVoice
|
||||
| sent = userRole >= GRAdmin && sendApprovalPhase
|
||||
| otherwise = memberCategory m == GCHostMember && hostApprovalPhase
|
||||
hostApprovalPhase = case scopeInfo of
|
||||
Just (GCSIMemberSupport Nothing) -> memberStatus mem == GSMemPendingApproval
|
||||
_ -> False
|
||||
sendApprovalPhase = case scopeInfo of
|
||||
Just (GCSIMemberSupport (Just scopeMem)) -> memberStatus scopeMem == GSMemPendingApproval
|
||||
_ -> False
|
||||
-- admins cannot send reports, non-admins cannot receive reports
|
||||
badReportUser
|
||||
| sent = userRole >= GRModerator
|
||||
@@ -929,11 +938,9 @@ acceptGroupJoinRequestAsync
|
||||
incognitoProfile = do
|
||||
gVar <- asks random
|
||||
let initialStatus = acceptanceToStatus (memberAdmission groupProfile) gAccepted
|
||||
((groupMemberId, memberId), currentMemCount) <- withStore $ \db ->
|
||||
liftM2
|
||||
(,)
|
||||
(createJoiningMember db gVar user gInfo cReqChatVRange cReqProfile cReqXContactId_ cReqMemberId_ welcomeMsgId_ gLinkMemRole initialStatus)
|
||||
(liftIO $ getGroupCurrentMembersCount db user gInfo)
|
||||
(groupMemberId, memberId) <- withStore $ \db ->
|
||||
createJoiningMember db gVar user gInfo cReqChatVRange cReqProfile cReqXContactId_ cReqMemberId_ welcomeMsgId_ gLinkMemRole initialStatus
|
||||
let currentMemCount = fromIntegral $ currentMembers $ groupSummary gInfo
|
||||
let Profile {displayName} = userProfileInGroup user gInfo (fromIncognitoProfile <$> incognitoProfile)
|
||||
GroupMember {memberRole = userRole, memberId = userMemberId} = membership
|
||||
msg =
|
||||
|
||||
@@ -703,8 +703,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
where
|
||||
sendGrpInvitation :: Contact -> GroupMember -> Maybe GroupLinkId -> CM ()
|
||||
sendGrpInvitation ct GroupMember {memberId, memberRole = memRole} groupLinkId = do
|
||||
currentMemCount <- withStore' $ \db -> getGroupCurrentMembersCount db user gInfo
|
||||
let GroupMember {memberRole = userRole, memberId = userMemberId} = membership
|
||||
let currentMemCount = fromIntegral $ currentMembers $ groupSummary gInfo
|
||||
GroupMember {memberRole = userRole, memberId = userMemberId} = membership
|
||||
groupInv =
|
||||
GroupInvitation
|
||||
{ fromMember = MemberIdRole userMemberId userRole,
|
||||
@@ -986,8 +986,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
pure $ NewMessageDeliveryTask {messageId = msgId, taskContext}
|
||||
checkSendRcpt :: [AChatMessage] -> CM Bool
|
||||
checkSendRcpt aMsgs = do
|
||||
currentMemCount <- withStore' $ \db -> getGroupCurrentMembersCount db user gInfo
|
||||
let GroupInfo {chatSettings = ChatSettings {sendRcpts}} = gInfo
|
||||
let currentMemCount = fromIntegral $ currentMembers $ groupSummary gInfo
|
||||
GroupInfo {chatSettings = ChatSettings {sendRcpts}} = gInfo
|
||||
pure $
|
||||
fromMaybe (sendRcptsSmallGroups user) sendRcpts
|
||||
&& any aChatMsgHasReceipt aMsgs
|
||||
|
||||
@@ -77,12 +77,13 @@ import Simplex.Messaging.Version hiding (version)
|
||||
-- 14 - support sending and receiving group join rejection (2025-02-24)
|
||||
-- 15 - support specifying message scopes for group messages (2025-03-12)
|
||||
-- 16 - support short link data (2025-06-10)
|
||||
-- 17 - allow host voice messages during member approval regardless of group voice setting (2026-02-10)
|
||||
|
||||
-- This should not be used directly in code, instead use `maxVersion chatVRange` from ChatConfig.
|
||||
-- This indirection is needed for backward/forward compatibility testing.
|
||||
-- Testing with real app versions is still needed, as tests use the current code with different version ranges, not the old code.
|
||||
currentChatVersion :: VersionChat
|
||||
currentChatVersion = VersionChat 16
|
||||
currentChatVersion = VersionChat 17
|
||||
|
||||
-- This should not be used directly in code, instead use `chatVRange` from ChatConfig (see comment above)
|
||||
supportedChatVRange :: VersionRangeChat
|
||||
@@ -145,6 +146,10 @@ groupKnockingVersion = VersionChat 15
|
||||
shortLinkDataVersion :: VersionChat
|
||||
shortLinkDataVersion = VersionChat 16
|
||||
|
||||
-- support host voice messages during member approval regardless of group voice setting
|
||||
memberSupportVoiceVersion :: VersionChat
|
||||
memberSupportVoiceVersion = VersionChat 17
|
||||
|
||||
agentToChatVersion :: VersionSMPA -> VersionChat
|
||||
agentToChatVersion v
|
||||
| v < pqdrSMPAgentVersion = initialChatVersion
|
||||
|
||||
@@ -68,7 +68,6 @@ module Simplex.Chat.Store.Groups
|
||||
getGroupModerators,
|
||||
getGroupRelayMembers,
|
||||
getGroupMembersForExpiration,
|
||||
getGroupCurrentMembersCount,
|
||||
deleteGroupChatItems,
|
||||
deleteGroupMembers,
|
||||
cleanupHostGroupLinkConn,
|
||||
@@ -1164,20 +1163,6 @@ getGroupMembersForExpiration db vr user@User {userId, userContactId} GroupInfo {
|
||||
)
|
||||
(groupId, userId, userContactId, GSMemRemoved, GSMemLeft, GSMemGroupDeleted, GSMemUnknown)
|
||||
|
||||
getGroupCurrentMembersCount :: DB.Connection -> User -> GroupInfo -> IO Int
|
||||
getGroupCurrentMembersCount db User {userId} GroupInfo {groupId} = do
|
||||
statuses :: [GroupMemberStatus] <-
|
||||
map fromOnly
|
||||
<$> DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT member_status
|
||||
FROM group_members
|
||||
WHERE group_id = ? AND user_id = ?
|
||||
|]
|
||||
(groupId, userId)
|
||||
pure $ length $ filter memberCurrent' statuses
|
||||
|
||||
getGroupInvitation :: DB.Connection -> VersionRangeChat -> User -> GroupId -> ExceptT StoreError IO ReceivedGroupInvitation
|
||||
getGroupInvitation db vr user groupId =
|
||||
getConnRec_ user >>= \case
|
||||
|
||||
@@ -1197,6 +1197,10 @@ Query: UPDATE connections SET smp_agent_version = ? WHERE conn_id = ?
|
||||
Plan:
|
||||
SEARCH connections USING PRIMARY KEY (conn_id=?)
|
||||
|
||||
Query: UPDATE deleted_snd_chunk_replicas SET delay = ?, retries = retries + 1, updated_at = ? WHERE deleted_snd_chunk_replica_id = ?
|
||||
Plan:
|
||||
SEARCH deleted_snd_chunk_replicas USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query: UPDATE messages SET msg_body = x'' WHERE conn_id = ? AND internal_id = ?
|
||||
Plan:
|
||||
SEARCH messages USING PRIMARY KEY (conn_id=? AND internal_id=?)
|
||||
@@ -1205,10 +1209,6 @@ Query: UPDATE ratchets SET ratchet_state = ? WHERE conn_id = ?
|
||||
Plan:
|
||||
SEARCH ratchets USING PRIMARY KEY (conn_id=?)
|
||||
|
||||
Query: UPDATE rcv_file_chunk_replicas SET delay = ?, retries = retries + 1, updated_at = ? WHERE rcv_file_chunk_replica_id = ?
|
||||
Plan:
|
||||
SEARCH rcv_file_chunk_replicas USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query: UPDATE rcv_file_chunk_replicas SET received = 1, updated_at = ? WHERE rcv_file_chunk_replica_id = ?
|
||||
Plan:
|
||||
SEARCH rcv_file_chunk_replicas USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
@@ -1588,14 +1588,6 @@ Plan:
|
||||
SEARCH g USING INTEGER PRIMARY KEY (rowid=?)
|
||||
SEARCH i USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
SELECT member_status
|
||||
FROM group_members
|
||||
WHERE group_id = ? AND user_id = ?
|
||||
|
||||
Plan:
|
||||
SEARCH group_members USING INDEX idx_group_members_group_id (user_id=? AND group_id=?)
|
||||
|
||||
Query:
|
||||
SELECT r.file_id
|
||||
FROM rcv_files r
|
||||
@@ -5000,14 +4992,6 @@ Query:
|
||||
Plan:
|
||||
SEARCH protocol_servers USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
UPDATE rcv_files
|
||||
SET to_receive = 1, user_approved_relays = ?, updated_at = ?
|
||||
WHERE file_id = ?
|
||||
|
||||
Plan:
|
||||
SEARCH rcv_files USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
UPDATE remote_controllers
|
||||
SET ctrl_device_name = ?, dh_priv_key = ?, prev_dh_priv_key = dh_priv_key
|
||||
|
||||
Reference in New Issue
Block a user