core: fix support chat deletion (#6271)

This commit is contained in:
spaced4ndy
2025-12-11 17:29:31 +00:00
committed by GitHub
parent 86a053729b
commit caef012003
8 changed files with 177 additions and 36 deletions

View File

@@ -2347,7 +2347,13 @@ processChatCommand vr nm = \case
(gInfo, m) <- withFastStore $ \db -> (,) <$> getGroupInfo db vr user groupId <*> getGroupMemberById db vr user gmId
when (isNothing $ supportChat m) $ throwCmdError "member has no support chat"
when (memberPending m) $ throwCmdError "member is pending"
(gInfo', m') <- withFastStore' $ \db -> deleteGroupMemberSupportChat db user gInfo m
(gInfo', m') <- withFastStore' $ \db -> do
gInfo' <-
if gmRequiresAttention m
then decreaseGroupMembersRequireAttention db user gInfo
else pure gInfo
m' <- deleteGroupMemberSupportChat db m
pure (gInfo', m')
pure $ CRMemberSupportChatDeleted user gInfo' m'
APIMembersRole groupId memberIds newRole -> withUser $ \user ->
withGroupLock "memberRole" groupId $ do

View File

@@ -1677,19 +1677,35 @@ deleteMemberConnection' GroupMember {activeConn} waitDelivery = do
withStore' $ \db -> updateConnectionStatus db conn ConnDeleted
deleteOrUpdateMemberRecord :: User -> GroupInfo -> GroupMember -> CM GroupInfo
deleteOrUpdateMemberRecord user gInfo member =
withStore' $ \db -> deleteOrUpdateMemberRecordIO db user gInfo member
deleteOrUpdateMemberRecord user gInfo m =
withStore' $ \db -> deleteOrUpdateMemberRecordIO db user gInfo m
deleteOrUpdateMemberRecordIO :: DB.Connection -> User -> GroupInfo -> GroupMember -> IO GroupInfo
deleteOrUpdateMemberRecordIO db user@User {userId} gInfo member = do
deleteOrUpdateMemberRecordIO db user@User {userId} gInfo m = do
(gInfo', m') <- deleteSupportChatIfExists db user gInfo m
checkGroupMemberHasItems db user m' >>= \case
Just _ -> updateGroupMemberStatus db userId m' GSMemRemoved
Nothing -> deleteGroupMember db user m'
pure gInfo'
updateMemberRecordDeleted :: User -> GroupInfo -> GroupMember -> GroupMemberStatus -> CM GroupInfo
updateMemberRecordDeleted user@User {userId} gInfo m newStatus =
withStore' $ \db -> do
(gInfo', m') <- deleteSupportChatIfExists db user gInfo m
updateGroupMemberStatus db userId m' newStatus
pure gInfo'
deleteSupportChatIfExists :: DB.Connection -> User -> GroupInfo -> GroupMember -> IO (GroupInfo, GroupMember)
deleteSupportChatIfExists db user gInfo m = do
gInfo' <-
if gmRequiresAttention member
if gmRequiresAttention m
then decreaseGroupMembersRequireAttention db user gInfo
else pure gInfo
checkGroupMemberHasItems db user member >>= \case
Just _ -> updateGroupMemberStatus db userId member GSMemRemoved
Nothing -> deleteGroupMember db user member
pure gInfo'
m' <-
if isJust (supportChat m)
then deleteGroupMemberSupportChat db m
else pure m
pure (gInfo', m')
sendDirectContactMessages :: MsgEncodingI e => User -> Contact -> NonEmpty (ChatMsgEvent e) -> CM [Either ChatError SndMessage]
sendDirectContactMessages user ct events = do

View File

@@ -2735,7 +2735,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
withStore' $ \db -> updateGroupMemberStatus db userId membership GSMemRemoved
let membership' = membership {memberStatus = GSMemRemoved}
when withMessages $ deleteMessages gInfo membership' SMDSnd
deleteMemberItem RGEUserDeleted
deleteMemberItem gInfo RGEUserDeleted
toView $ CEvtDeletedMemberUser user gInfo {membership = membership'} m withMessages
pure $ Just DJSGroup {jobSpec = DJRelayRemoved}
else
@@ -2746,29 +2746,33 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
Right deletedMember@GroupMember {groupMemberId, memberProfile, memberStatus} ->
checkRole deletedMember $ do
-- ? prohibit deleting member if it's the sender - sender should use x.grp.leave
if isUserGrpFwdRelay gInfo && not forwarded
let shouldForward = isUserGrpFwdRelay gInfo && not forwarded
if shouldForward
then do
-- Special case: forward before deleting connection.
-- It allows us to avoid adding logic in forwardMsgs to circumvent member filtering.
forwardToMember deletedMember
deleteMemberConnection' deletedMember True
else deleteMemberConnection deletedMember
-- undeleted "member connected" chat item will prevent deletion of member record
gInfo' <- deleteOrUpdateMemberRecord user gInfo deletedMember
let deliveryScope = memberEventDeliveryScope deletedMember
gInfo' <- case deliveryScope of
-- Keep member record if it's support scope - it will be required for forwarding inside that scope.
Just (DJSMemberSupport _) | shouldForward -> updateMemberRecordDeleted user gInfo deletedMember GSMemRemoved
-- Undeleted "member connected" chat item will prevent deletion of member record.
_ -> deleteOrUpdateMemberRecord user gInfo deletedMember
let wasDeleted = memberStatus == GSMemRemoved || memberStatus == GSMemLeft
deletedMember' = deletedMember {memberStatus = GSMemRemoved}
when withMessages $ deleteMessages gInfo' deletedMember' SMDRcv
unless wasDeleted $ deleteMemberItem $ RGEMemberDeleted groupMemberId (fromLocalProfile memberProfile)
unless wasDeleted $ deleteMemberItem gInfo' $ RGEMemberDeleted groupMemberId (fromLocalProfile memberProfile)
toView $ CEvtDeletedMember user gInfo' m deletedMember' withMessages
pure $ memberEventDeliveryScope deletedMember
pure deliveryScope
where
checkRole GroupMember {memberRole} a
| senderRole < GRAdmin || senderRole < memberRole =
messageError "x.grp.mem.del with insufficient member permissions" $> Nothing
| otherwise = a
deleteMemberItem gEvent = do
(gInfo', m', scopeInfo) <- mkGroupChatScope gInfo m
(ci, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gInfo' scopeInfo m') msg brokerTs (CIRcvGroupEvent gEvent)
deleteMemberItem gi gEvent = do
(gi', m', scopeInfo) <- mkGroupChatScope gi m
(ci, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gi' scopeInfo m') msg brokerTs (CIRcvGroupEvent gEvent)
groupMsgToView cInfo ci
deleteMessages :: MsgDirectionI d => GroupInfo -> GroupMember -> SMsgDirection d -> CM ()
deleteMessages gInfo' delMem msgDir
@@ -2791,11 +2795,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
xGrpLeave gInfo m msg brokerTs = do
deleteMemberConnection m
-- member record is not deleted to allow creation of "member left" chat item
gInfo' <- withStore' $ \db -> do
updateGroupMemberStatus db userId m GSMemLeft
if gmRequiresAttention m
then decreaseGroupMembersRequireAttention db user gInfo
else pure gInfo
gInfo' <- updateMemberRecordDeleted user gInfo m GSMemLeft
(gInfo'', m', scopeInfo) <- mkGroupChatScope gInfo' m
(ci, cInfo) <- saveRcvChatItemNoParse user (CDGroupRcv gInfo'' scopeInfo m') msg brokerTs (CIRcvGroupEvent RGEMemberLeft)
groupMsgToView cInfo ci

View File

@@ -1415,9 +1415,8 @@ updateGroupMemberAccepted db User {userId} m@GroupMember {groupMemberId} status
(status, role, currentTs, userId, groupMemberId)
pure m {memberStatus = status, memberRole = role, updatedAt = currentTs}
deleteGroupMemberSupportChat :: DB.Connection -> User -> GroupInfo -> GroupMember -> IO (GroupInfo, GroupMember)
deleteGroupMemberSupportChat db user g m@GroupMember {groupMemberId} = do
let requiredAttention = gmRequiresAttention m
deleteGroupMemberSupportChat :: DB.Connection -> GroupMember -> IO GroupMember
deleteGroupMemberSupportChat db m@GroupMember {groupMemberId} = do
currentTs <- getCurrentTime
DB.execute
db
@@ -1439,11 +1438,7 @@ deleteGroupMemberSupportChat db user g m@GroupMember {groupMemberId} = do
WHERE group_member_id = ?
|]
(currentTs, groupMemberId)
let m' = m {supportChat = Nothing, updatedAt = currentTs}
g' <- if requiredAttention
then decreaseGroupMembersRequireAttention db user g
else pure g
pure (g', m')
pure m {supportChat = Nothing, updatedAt = currentTs}
updateGroupMembersRequireAttention :: DB.Connection -> User -> GroupInfo -> GroupMember -> GroupMember -> IO GroupInfo
updateGroupMembersRequireAttention db user g member member'

View File

@@ -1645,7 +1645,7 @@ getGroupUnreadCount_ :: DB.Connection -> User -> GroupInfo -> Maybe GroupChatSco
getGroupUnreadCount_ db user g scopeInfo_ contentFilter =
head <$> queryUnreadGroupItems db user g scopeInfo_ contentFilter baseQuery ""
where
baseQuery = "SELECT COUNT(1), COALESCE(SUM(user_mention), 0) FROM chat_items WHERE user_id = ? AND group_id = ? AND group_scope_tag IS NULL AND group_scope_group_member_id IS NULL "
baseQuery = "SELECT COUNT(1), COALESCE(SUM(user_mention), 0) FROM chat_items WHERE user_id = ? AND group_id = ? "
getGroupReportsCount_ :: DB.Connection -> User -> GroupInfo -> Bool -> IO Int
getGroupReportsCount_ db User {userId} GroupInfo {groupId} archived =

View File

@@ -6113,7 +6113,7 @@ Query: SELECT COUNT(1) FROM groups WHERE user_id = ? AND chat_item_ttl > 0
Plan:
SEARCH groups USING INDEX idx_groups_chat_ts (user_id=?)
Query: SELECT COUNT(1), COALESCE(SUM(user_mention), 0) FROM chat_items WHERE user_id = ? AND group_id = ? AND group_scope_tag IS NULL AND group_scope_group_member_id IS NULL AND group_scope_tag IS NULL AND group_scope_group_member_id IS NULL AND item_status = ?
Query: SELECT COUNT(1), COALESCE(SUM(user_mention), 0) FROM chat_items WHERE user_id = ? AND group_id = ? AND group_scope_tag IS NULL AND group_scope_group_member_id IS NULL AND item_status = ?
Plan:
SEARCH chat_items USING COVERING INDEX idx_chat_items_group_scope_stats_all (user_id=? AND group_id=? AND group_scope_tag=? AND group_scope_group_member_id=? AND item_status=?)