This commit is contained in:
spaced4ndy
2025-10-31 16:17:09 +04:00
parent 1c93967e57
commit 717bdec972
12 changed files with 201 additions and 37 deletions
+9
View File
@@ -2344,6 +2344,7 @@ Known:
**Record type**:
- groupRelayId: int64
- userChatRelayId: int64
- relayStatus: [RelayStatus](#relaystatus)
- relayLink: string?
@@ -3588,6 +3589,14 @@ OperatorNotFound:
UsageConditionsNotFound:
- type: "usageConditionsNotFound"
UserChatRelayNotFound:
- type: "userChatRelayNotFound"
- chatRelayId: int64
GroupRelayNotFound:
- type: "groupRelayNotFound"
- groupRelayId: int64
InvalidQuote:
- type: "invalidQuote"
@@ -2594,6 +2594,7 @@ export interface GroupProfile {
export interface GroupRelay {
groupRelayId: number // int64
userChatRelayId: number // int64
relayStatus: RelayStatus
relayLink?: string
}
@@ -3812,6 +3813,8 @@ export type StoreError =
| StoreError.ProhibitedDeleteUser
| StoreError.OperatorNotFound
| StoreError.UsageConditionsNotFound
| StoreError.UserChatRelayNotFound
| StoreError.GroupRelayNotFound
| StoreError.InvalidQuote
| StoreError.InvalidMention
| StoreError.InvalidDeliveryTask
@@ -3897,6 +3900,8 @@ export namespace StoreError {
| "prohibitedDeleteUser"
| "operatorNotFound"
| "usageConditionsNotFound"
| "userChatRelayNotFound"
| "groupRelayNotFound"
| "invalidQuote"
| "invalidMention"
| "invalidDeliveryTask"
@@ -4274,6 +4279,16 @@ export namespace StoreError {
type: "usageConditionsNotFound"
}
export interface UserChatRelayNotFound extends Interface {
type: "userChatRelayNotFound"
chatRelayId: number // int64
}
export interface GroupRelayNotFound extends Interface {
type: "groupRelayNotFound"
groupRelayId: number // int64
}
export interface InvalidQuote extends Interface {
type: "invalidQuote"
}
+47 -12
View File
@@ -2690,7 +2690,7 @@ processChatCommand vr nm = \case
-- TODO - prepare group link (without creating on server)
-- TODO - add link, owner key to group profile, sign
-- TODO - create group link on server, use signed profile as data
-- vvv
-- vvv FROM HERE vvv
(connId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) True True SCMContact (Just userData) (Just crClientData) IKPQOff subMode
ccLink' <- createdGroupLink <$> shortenCreatedLink ccLink
sLnk <- case toShortLinkContact ccLink' of
@@ -2700,7 +2700,7 @@ processChatCommand vr nm = \case
userData' = encodeShortLinkData $ GroupShortLinkData groupProfile'
-- same link with updated profile
_sLnk' <- shortenShortLink' . toShortGroupLink =<< withAgent (\a -> setConnShortLink a nm connId SCMContact userData' (Just crClientData))
-- ^^^
-- ^^^ TO HERE ^^^
gVar <- asks random
(gLink, gInfo') <- withFastStore $ \db -> do
gLink <- createGroupLink db gVar user gInfo connId ccLink' groupLinkId GRMember subMode
@@ -2709,7 +2709,7 @@ processChatCommand vr nm = \case
if autoChooseRelays
then do
relays <- chooseRelays user
groupRelays <- addRelays user gInfo' relays
groupRelays <- addRelays user gInfo' sLnk relays
pure $ CRGroupRelaysAdded user gInfo' gLink groupRelays
else
pure $ CRGroupLinkCreated user gInfo' gLink
@@ -2722,12 +2722,15 @@ processChatCommand vr nm = \case
when (null selectedRelays) $ throwChatError $ CEException "failed to select relays: no enabled relays configured"
pure selectedRelays
APIAddRelays groupId relayIds -> withUser $ \user -> withGroupLock "addRelays" groupId $ do
(gInfo, gLink) <- withFastStore $ \db -> do
(gInfo, gLink@GroupLink {connLinkContact}) <- withFastStore $ \db -> do
gInfo <- getGroupInfo db vr user groupId
gLink <- getGroupLink db user gInfo
pure (gInfo, gLink)
sLnk <- case toShortLinkContact connLinkContact of
Just sl -> pure sl
Nothing -> throwChatError $ CEException "failed to add relays: no short link in group link"
relays <- withFastStore $ \db -> mapM (getChatRelayById db user) (L.toList relayIds)
groupRelays <- addRelays user gInfo relays
groupRelays <- addRelays user gInfo sLnk relays
pure $ CRGroupRelaysAdded user gInfo gLink groupRelays
APIGroupLinkMemberRole groupId mRole' -> withUser $ \user -> withGroupLock "groupLinkMemberRole" groupId $ do
gInfo <- withFastStore $ \db -> getGroupInfo db vr user groupId
@@ -3571,13 +3574,45 @@ processChatCommand vr nm = \case
toView $ CEvtNewChatItems user [AChatItem SCTDirect SMDSnd (DirectChat ct) ci]
forM_ (timed_ >>= timedDeleteAt') $
startProximateTimedItemThread user (ChatRef CTDirect contactId Nothing, chatItemId' ci)
addRelays :: User -> GroupInfo -> [UserChatRelay] -> CM [GroupRelay]
addRelays _user _gInfo _relays = do
-- TODO [relays] owner: send contact requests to relays
-- TODO - create relay member connections, relay records (group_relays), relay status: RSNew
-- TODO - send requests to relays: INV message - XGrpRelayInv, relay status: RSInvited
-- TODO - agent joinConnectionAsync for contact links (currently prohibited)
pure []
addRelays :: User -> GroupInfo -> ShortLinkContact -> [UserChatRelay] -> CM [GroupRelay]
addRelays user gInfo@GroupInfo {membership} groupSLink relays =
forM relays $ \relay -> addRelay relay
where
addRelay :: UserChatRelay -> CM GroupRelay
addRelay relay@UserChatRelay {address} = do
-- TODO [relays] owner: can update relay profile from data retrieved via getConnShortLink
(cReq, _cData) <- withAgent $ \a -> getConnShortLink a nm (aUserId user) address
lift (withAgent' $ \a -> connRequestPQSupport a PQSupportOff cReq) >>= \case
Nothing -> throwChatError CEInvalidConnReq
Just (agentV, _) -> do
let chatV = agentToChatVersion agentV
gVar <- asks random
subMode <- chatReadVar subscriptionMode
-- TODO [relays] owner: replace with async join (joinConnectionAsync currently prohibited for contact links)
-- TODO - or make "add relays" api retriable, via prepared connection
connId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq PQSupportOff
(relayMember, conn, groupRelay) <- withStore $ \db -> do
groupRelay <- createGroupRelayRecord db gInfo relay
relayMember <- createRelayMemberRecord db vr gVar user gInfo relay groupRelay
conn <- createRelayConnection db vr user (groupMemberId' relayMember) connId ConnPrepared chatV subMode
pure (relayMember, conn, groupRelay)
let GroupMember {memberRole = userRole, memberId = userMemberId} = membership
allowSimplexLinks = groupFeatureUserAllowed SGFSimplexLinks gInfo
membershipProfile = redactedMemberProfile allowSimplexLinks $ fromLocalProfile $ memberProfile membership
GroupMember {memberRole = relayRole, memberId = relayMemberId} = relayMember
relayInv = GroupRelayInvitation {
fromMember = MemberIdRole userMemberId userRole,
fromMemberProfile = membershipProfile,
invitedMember = MemberIdRole relayMemberId relayRole,
groupLink = groupSLink
}
dm <- encodeConnInfo $ XGrpRelayInv relayInv
(sqSecured, _serviceId) <- withAgent $ \a -> joinConnection a nm (aUserId user) (aConnId conn) True cReq dm PQSupportOff subMode
let newConnStatus = if sqSecured then ConnSndReady else ConnJoined
groupRelay' <- withFastStore' $ \db -> do
void $ updateConnectionStatusFromTo db conn ConnPrepared newConnStatus
updateRelayStatusFromTo db groupRelay RSNew RSInvited
pure groupRelay'
drgRandomBytes :: Int -> CM ByteString
drgRandomBytes n = asks random >>= atomically . C.randomBytes n
privateGetUser :: UserId -> CM User
+3 -3
View File
@@ -1150,7 +1150,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
case chatMsgEvent of
XContact p xContactId_ welcomeMsgId_ requestMsg_ -> profileContactRequest invId chatVRange p xContactId_ welcomeMsgId_ requestMsg_ pqSupport
XInfo p -> profileContactRequest invId chatVRange p Nothing Nothing Nothing pqSupport
XGrpRelayInv groupLink -> relayContactRequest groupLink
XGrpRelayInv groupRelayInv -> relayContactRequest groupRelayInv
-- TODO show/log error, other events in contact request
_ -> pure ()
MERR _ err -> do
@@ -1319,8 +1319,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
| otherwise -> do
mem <- acceptGroupJoinSendRejectAsync user uclId gInfo invId chatVRange p xContactId_ rjctReason
toViewTE $ TERejectingGroupJoinRequestMember user gInfo mem rjctReason
relayContactRequest :: ShortLinkContact -> CM ()
relayContactRequest _groupLink = do
relayContactRequest :: GroupRelayInvitation -> CM ()
relayContactRequest _groupRelayInv = do
-- TODO [relays] relay: process contact request to server group
-- TODO - retrieve group link data, validate group profile, verify owner's signature
-- TODO - create group record, relay status: RSInvited
+3 -3
View File
@@ -328,7 +328,7 @@ data ChatMsgEvent (e :: MsgEncoding) where
XGrpLinkReject :: GroupLinkRejection -> ChatMsgEvent 'Json
XGrpLinkMem :: Profile -> ChatMsgEvent 'Json
XGrpLinkAcpt :: GroupAcceptance -> GroupMemberRole -> MemberId -> ChatMsgEvent 'Json
XGrpRelayInv :: ShortLinkContact -> ChatMsgEvent 'Json
XGrpRelayInv :: GroupRelayInvitation -> ChatMsgEvent 'Json
XGrpRelayAcpt :: ShortLinkContact -> ChatMsgEvent 'Json
XGrpMemNew :: MemberInfo -> Maybe MsgScope -> ChatMsgEvent 'Json
XGrpMemIntro :: MemberInfo -> Maybe MemberRestrictions -> ChatMsgEvent 'Json
@@ -1100,7 +1100,7 @@ appJsonToCM AppMessageJson {v, msgId, event, params} = do
XGrpLinkReject_ -> XGrpLinkReject <$> p "groupLinkRejection"
XGrpLinkMem_ -> XGrpLinkMem <$> p "profile"
XGrpLinkAcpt_ -> XGrpLinkAcpt <$> p "acceptance" <*> p "role" <*> p "memberId"
XGrpRelayInv_ -> XGrpRelayInv <$> p "groupLink"
XGrpRelayInv_ -> XGrpRelayInv <$> p "groupRelayInvitation"
XGrpRelayAcpt_ -> XGrpRelayAcpt <$> p "relayLink"
XGrpMemNew_ -> XGrpMemNew <$> p "memberInfo" <*> opt "scope"
XGrpMemIntro_ -> XGrpMemIntro <$> p "memberInfo" <*> opt "memberRestrictions"
@@ -1161,7 +1161,7 @@ chatToAppMessage chatMsg@ChatMessage {chatVRange, msgId, chatMsgEvent} = case en
XGrpLinkReject groupLinkRjct -> o ["groupLinkRejection" .= groupLinkRjct]
XGrpLinkMem profile -> o ["profile" .= profile]
XGrpLinkAcpt acceptance role memberId -> o ["acceptance" .= acceptance, "role" .= role, "memberId" .= memberId]
XGrpRelayInv groupLink -> o ["groupLink" .= groupLink]
XGrpRelayInv groupRelayInv -> o ["groupRelayInvitation" .= groupRelayInv]
XGrpRelayAcpt relayLink -> o ["relayLink" .= relayLink]
XGrpMemNew memInfo scope -> o $ ("scope" .=? scope) ["memberInfo" .= memInfo]
XGrpMemIntro memInfo memRestrictions -> o $ ("memberRestrictions" .=? memRestrictions) ["memberInfo" .= memInfo]
+2 -2
View File
@@ -149,13 +149,13 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
mu.created_at, mu.updated_at,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts,
mu.is_chat_relay, NULL, NULL, NULL,
mu.is_chat_relay, NULL, NULL, NULL, NULL,
-- from GroupMember
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
m.is_chat_relay, r.group_relay_id, r.relay_status, r.relay_link
m.is_chat_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link
FROM group_members m
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
LEFT JOIN group_relays r ON r.group_relay_id = m.group_relay_id
+99 -3
View File
@@ -74,6 +74,11 @@ module Simplex.Chat.Store.Groups
getContactGroupPreferences,
getGroupInvitation,
createNewContactMember,
createGroupRelayRecord,
getGroupRelayById,
createRelayMemberRecord,
createRelayConnection,
updateRelayStatusFromTo,
createNewContactMemberAsync,
createJoiningMember,
getMemberJoinRequest,
@@ -156,6 +161,7 @@ import Crypto.Random (ChaChaDRG)
import Data.Bifunctor (second)
import Data.Char (toLower)
import Data.Either (rights)
import Data.Functor (($>))
import Data.Int (Int64)
import Data.List (partition, sortOn)
import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing)
@@ -165,6 +171,7 @@ import qualified Data.Text as T
import Data.Time.Clock (UTCTime (..), getCurrentTime)
import Data.Text.Encoding (encodeUtf8)
import Simplex.Chat.Messages
import Simplex.Chat.Operators
import Simplex.Chat.Protocol hiding (Binary)
import Simplex.Chat.Store.Direct
import Simplex.Chat.Store.Shared
@@ -190,11 +197,11 @@ import Database.SQLite.Simple (Only (..), Query, (:.) (..))
import Database.SQLite.Simple.QQ (sql)
#endif
type MaybeGroupMemberRow = (Maybe Int64, Maybe Int64, Maybe MemberId, Maybe VersionChat, Maybe VersionChat, Maybe GroupMemberRole, Maybe GroupMemberCategory, Maybe GroupMemberStatus, Maybe BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, Maybe ContactName, Maybe ContactId, Maybe ProfileId) :. (Maybe ProfileId, Maybe ContactName, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe LocalAlias, Maybe Preferences) :. (Maybe UTCTime, Maybe UTCTime) :. (Maybe UTCTime, Maybe Int64, Maybe Int64, Maybe Int64, Maybe UTCTime) :. (Maybe BoolInt, Maybe Int64, Maybe RelayStatus, Maybe ShortLinkContact)
type MaybeGroupMemberRow = (Maybe Int64, Maybe Int64, Maybe MemberId, Maybe VersionChat, Maybe VersionChat, Maybe GroupMemberRole, Maybe GroupMemberCategory, Maybe GroupMemberStatus, Maybe BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, Maybe ContactName, Maybe ContactId, Maybe ProfileId) :. (Maybe ProfileId, Maybe ContactName, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe LocalAlias, Maybe Preferences) :. (Maybe UTCTime, Maybe UTCTime) :. (Maybe UTCTime, Maybe Int64, Maybe Int64, Maybe Int64, Maybe UTCTime) :. (Maybe BoolInt, Maybe Int64, Maybe Int64, Maybe RelayStatus, Maybe ShortLinkContact)
toMaybeGroupMember :: Int64 -> MaybeGroupMemberRow -> Maybe GroupMember
toMaybeGroupMember userContactId ((Just groupMemberId, Just groupId, Just memberId, Just minVer, Just maxVer, Just memberRole, Just memberCategory, Just memberStatus, Just showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, Just localDisplayName, memberContactId, Just memberContactProfileId) :. (Just profileId, Just displayName, Just fullName, shortDescr, image, contactLink, peerType, Just localAlias, contactPreferences) :. (Just createdAt, Just updatedAt) :. (supportChatTs, Just supportChatUnread, Just supportChatUnanswered, Just supportChatMentions, supportChatLastMsgFromMemberTs) :. (Just isChatRelay, groupRelayId, relayStatus, relayLink)) =
Just $ toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. (profileId, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, contactPreferences) :. (createdAt, updatedAt) :. (supportChatTs, supportChatUnread, supportChatUnanswered, supportChatMentions, supportChatLastMsgFromMemberTs) :. (isChatRelay, groupRelayId, relayStatus, relayLink))
toMaybeGroupMember userContactId ((Just groupMemberId, Just groupId, Just memberId, Just minVer, Just maxVer, Just memberRole, Just memberCategory, Just memberStatus, Just showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, Just localDisplayName, memberContactId, Just memberContactProfileId) :. (Just profileId, Just displayName, Just fullName, shortDescr, image, contactLink, peerType, Just localAlias, contactPreferences) :. (Just createdAt, Just updatedAt) :. (supportChatTs, Just supportChatUnread, Just supportChatUnanswered, Just supportChatMentions, supportChatLastMsgFromMemberTs) :. (Just isChatRelay, groupRelayId, chatRelayId, relayStatus, relayLink)) =
Just $ toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. (profileId, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, contactPreferences) :. (createdAt, updatedAt) :. (supportChatTs, supportChatUnread, supportChatUnanswered, supportChatMentions, supportChatLastMsgFromMemberTs) :. (isChatRelay, groupRelayId, chatRelayId, relayStatus, relayLink))
toMaybeGroupMember _ _ = Nothing
createGroupLink :: DB.Connection -> TVar ChaChaDRG -> User -> GroupInfo -> ConnId -> CreatedLinkContact -> GroupLinkId -> GroupMemberRole -> SubscriptionMode -> ExceptT StoreError IO GroupLink
@@ -1122,6 +1129,95 @@ createNewContactMember db gVar User {userId, userContactId} GroupInfo {groupId,
:. (minV, maxV)
)
createGroupRelayRecord :: DB.Connection -> GroupInfo -> UserChatRelay -> ExceptT StoreError IO GroupRelay
createGroupRelayRecord db GroupInfo {groupId} UserChatRelay {chatRelayId} = do
currentTs <- liftIO getCurrentTime
liftIO $
DB.execute
db
[sql|
INSERT INTO group_relays
(group_id, chat_relay_id, relay_status, created_at, updated_at)
VALUES (?,?,?,?,?)
|]
(groupId, chatRelayId, RSNew, currentTs, currentTs)
relayId <- liftIO $ insertedRowId db
getGroupRelayById db relayId
getGroupRelayById :: DB.Connection -> Int64 -> ExceptT StoreError IO GroupRelay
getGroupRelayById db relayId =
ExceptT . firstRow toGroupRelay (SEGroupRelayNotFound relayId) $
DB.query
db
[sql|
SELECT group_relay_id, chat_relay_id, chat_relay_id, relay_status, relay_link
FROM group_relays
WHERE group_relay_id = ?
|]
(Only relayId)
where
toGroupRelay :: (Int64, Int64, RelayStatus, Maybe ShortLinkContact) -> GroupRelay
toGroupRelay (groupRelayId, userChatRelayId, relayStatus, relayLink) =
GroupRelay {groupRelayId, userChatRelayId, relayStatus, relayLink}
-- TODO [relays] TBC role, category, relay profile
-- TODO - GCInviteeMember -> GCRelayMember?
-- TODO - GRMember -> GRRelay?
-- TODO - create 1 profile per relay, link to chat_relays?
createRelayMemberRecord :: DB.Connection -> VersionRangeChat -> TVar ChaChaDRG -> User -> GroupInfo -> UserChatRelay -> GroupRelay -> ExceptT StoreError IO GroupMember
createRelayMemberRecord db vr gVar user@User {userId, userContactId} GroupInfo {groupId, membership} UserChatRelay {name} GroupRelay {groupRelayId} = do
currentTs <- liftIO getCurrentTime
let relayProfile = profileFromName name
(localDisplayName, memProfileId) <- createNewMemberProfile_ db user relayProfile currentTs
groupMemberId <- createWithRandomId gVar $ \memId -> do
DB.execute
db
[sql|
INSERT INTO group_members
( group_id, member_id, member_role, member_category, member_status, invited_by, invited_by_group_member_id,
user_id, local_display_name, contact_profile_id, created_at, updated_at,
is_chat_relay, group_relay_id
)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|]
( (groupId, MemberId memId, GRMember, GCInviteeMember, GSMemInvited, fromInvitedBy userContactId IBUser, groupMemberId' membership)
:. (userId, localDisplayName, memProfileId, currentTs, currentTs)
:. (BI True, groupRelayId)
)
insertedRowId db
getGroupMemberById db vr user groupMemberId
createRelayConnection :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ConnId -> ConnStatus -> VersionChat -> SubscriptionMode -> ExceptT StoreError IO Connection
createRelayConnection db vr user@User {userId} groupMemberId agentConnId connStatus chatV subMode = do
currentTs <- liftIO getCurrentTime
liftIO $
DB.execute
db
[sql|
INSERT INTO connections (
user_id, agent_conn_id, conn_level, conn_status, conn_type,
group_member_id, conn_chat_version, to_subscribe, pq_support, pq_encryption,
created_at, updated_at
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)
|]
( (userId, agentConnId, 0 :: Int, connStatus, ConnMember)
:. (groupMemberId, chatV, BI (subMode == SMOnlyCreate), PQSupportOff, PQSupportOff)
:. (currentTs, currentTs)
)
connId <- liftIO $ insertedRowId db
getConnectionById db vr user connId
updateRelayStatusFromTo :: DB.Connection -> GroupRelay -> RelayStatus -> RelayStatus -> IO GroupRelay
updateRelayStatusFromTo db relay@GroupRelay {groupRelayId} fromStatus toStatus = do
maybeFirstRow fromOnly (DB.query db "SELECT relay_status FROM group_relays WHERE group_relay_id = ?" (Only groupRelayId)) >>= \case
Just status | status == fromStatus -> updateRelayStatus_ db groupRelayId toStatus $> relay {relayStatus = toStatus}
_ -> pure relay
updateRelayStatus_ :: DB.Connection -> Int64 -> RelayStatus -> IO ()
updateRelayStatus_ db relayId relayStatus = do
currentTs <- getCurrentTime
DB.execute db "UPDATE group_relays SET relay_status = ?, updated_at = ? WHERE group_relay_id = ?" (relayStatus, currentTs, relayId)
createNewContactMemberAsync :: DB.Connection -> TVar ChaChaDRG -> User -> GroupInfo -> Contact -> GroupMemberRole -> (CommandId, ConnId) -> VersionChat -> VersionRangeChat -> SubscriptionMode -> ExceptT StoreError IO ()
createNewContactMemberAsync db gVar user@User {userId, userContactId} GroupInfo {groupId, membership} Contact {contactId, localDisplayName, profile} memberRole (cmdId, agentConnId) chatV peerChatVRange subMode =
createWithRandomId gVar $ \memId -> do
+4 -4
View File
@@ -679,7 +679,7 @@ getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRe
p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
m.is_chat_relay, r.group_relay_id, r.relay_status, r.relay_link
m.is_chat_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link
FROM group_members m
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
LEFT JOIN group_relays r ON r.group_relay_id = m.group_relay_id
@@ -3005,7 +3005,7 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do
p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
m.is_chat_relay, r.group_relay_id, r.relay_status, r.relay_link,
m.is_chat_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link,
-- quoted ChatItem
ri.chat_item_id, i.quoted_shared_msg_id, i.quoted_sent_at, i.quoted_content, i.quoted_sent,
-- quoted GroupMember
@@ -3014,14 +3014,14 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do
rp.display_name, rp.full_name, rp.short_descr, rp.image, rp.contact_link, rp.chat_peer_type, rp.local_alias, rp.preferences,
rm.created_at, rm.updated_at,
rm.support_chat_ts, rm.support_chat_items_unread, rm.support_chat_items_member_attention, rm.support_chat_items_mentions, rm.support_chat_last_msg_from_member_ts,
rm.is_chat_relay, rr.group_relay_id, rr.relay_status, rr.relay_link,
rm.is_chat_relay, rr.group_relay_id, rr.chat_relay_id, rr.relay_status, rr.relay_link,
-- deleted by GroupMember
dbm.group_member_id, dbm.group_id, dbm.member_id, dbm.peer_chat_min_version, dbm.peer_chat_max_version, dbm.member_role, dbm.member_category,
dbm.member_status, dbm.show_messages, dbm.member_restriction, dbm.invited_by, dbm.invited_by_group_member_id, dbm.local_display_name, dbm.contact_id, dbm.contact_profile_id, dbp.contact_profile_id,
dbp.display_name, dbp.full_name, dbp.short_descr, dbp.image, dbp.contact_link, dbp.chat_peer_type, dbp.local_alias, dbp.preferences,
dbm.created_at, dbm.updated_at,
dbm.support_chat_ts, dbm.support_chat_items_unread, dbm.support_chat_items_member_attention, dbm.support_chat_items_mentions, dbm.support_chat_last_msg_from_member_ts,
dbm.is_chat_relay, dbr.group_relay_id, dbr.relay_status, dbr.relay_link
dbm.is_chat_relay, dbr.group_relay_id, dbr.chat_relay_id, dbr.relay_status, dbr.relay_link
FROM chat_items i
LEFT JOIN files f ON f.chat_item_id = i.chat_item_id
LEFT JOIN group_members m ON m.group_member_id = i.group_member_id
@@ -53,7 +53,9 @@ CREATE TABLE group_relays(
group_id INTEGER NOT NULL REFERENCES groups ON DELETE CASCADE,
chat_relay_id INTEGER NOT NULL REFERENCES chat_relays ON DELETE CASCADE,
relay_status TEXT NOT NULL,
relay_link BLOB
relay_link BLOB,
created_at TEXT NOT NULL DEFAULT(datetime('now')),
updated_at TEXT NOT NULL DEFAULT(datetime('now'))
);
CREATE INDEX idx_group_relays_group_id ON group_relays(group_id);
CREATE INDEX idx_group_relays_chat_relay_id ON group_relays(chat_relay_id);
@@ -747,7 +747,9 @@ CREATE TABLE group_relays(
group_id INTEGER NOT NULL REFERENCES groups ON DELETE CASCADE,
chat_relay_id INTEGER NOT NULL REFERENCES chat_relays ON DELETE CASCADE,
relay_status TEXT NOT NULL,
relay_link BLOB
relay_link BLOB,
created_at TEXT NOT NULL DEFAULT(datetime('now')),
updated_at TEXT NOT NULL DEFAULT(datetime('now'))
);
CREATE INDEX contact_profiles_index ON contact_profiles(
display_name,
+8 -7
View File
@@ -148,6 +148,7 @@ data StoreError
| SEOperatorNotFound {serverOperatorId :: Int64}
| SEUsageConditionsNotFound
| SEUserChatRelayNotFound {chatRelayId :: Int64}
| SEGroupRelayNotFound {groupRelayId :: Int64}
| SEInvalidQuote
| SEInvalidMention
| SEInvalidDeliveryTask {taskId :: Int64}
@@ -657,7 +658,7 @@ type BusinessChatInfoRow = (Maybe BusinessChatType, Maybe MemberId, Maybe Member
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Text, Maybe Text, Maybe ImageData, Maybe ShortLinkContact) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. PreparedGroupRow :. BusinessChatInfoRow :. (BoolInt, Maybe RelayStatus, Maybe UIThemeEntityOverrides, Int64, Maybe CustomData, Maybe Int64, Int, Maybe ConnReqContact) :. GroupMemberRow
type GroupMemberRow = (Int64, Int64, MemberId, VersionChat, VersionChat, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, ContactName, Maybe ContactId, ProfileId) :. ProfileRow :. (UTCTime, UTCTime) :. (Maybe UTCTime, Int64, Int64, Int64, Maybe UTCTime) :. (BoolInt, Maybe Int64, Maybe RelayStatus, Maybe ShortLinkContact)
type GroupMemberRow = (Int64, Int64, MemberId, VersionChat, VersionChat, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, ContactName, Maybe ContactId, ProfileId) :. ProfileRow :. (UTCTime, UTCTime) :. (Maybe UTCTime, Int64, Int64, Int64, Maybe UTCTime) :. (BoolInt, Maybe Int64, Maybe Int64, Maybe RelayStatus, Maybe ShortLinkContact)
type ProfileRow = (ProfileId, ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, Maybe Preferences)
@@ -679,7 +680,7 @@ toPreparedGroup = \case
_ -> Nothing
toGroupMember :: Int64 -> GroupMemberRow -> GroupMember
toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, BI showMessages, memberRestriction_) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. profileRow :. (createdAt, updatedAt) :. (supportChatTs_, supportChatUnread, supportChatMemberAttention, supportChatMentions, supportChatLastMsgFromMemberTs) :. (BI isCRelay, groupRelayId_, relayStatus_, relayLink)) =
toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, BI showMessages, memberRestriction_) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. profileRow :. (createdAt, updatedAt) :. (supportChatTs_, supportChatUnread, supportChatMemberAttention, supportChatMentions, supportChatLastMsgFromMemberTs) :. (BI isCRelay, groupRelayId_, chatRelayId_, relayStatus_, relayLink)) =
let memberProfile = rowToLocalProfile profileRow
memberSettings = GroupMemberSettings {showMessages}
blockedByAdmin = maybe False mrsBlocked memberRestriction_
@@ -698,8 +699,8 @@ toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer,
}
_ -> Nothing
isChatRelay = BoolDef isCRelay
relayData = case (groupRelayId_, relayStatus_) of
(Just groupRelayId, Just relayStatus) -> Just GroupRelay {groupRelayId, relayStatus, relayLink}
relayData = case (groupRelayId_, chatRelayId_, relayStatus_) of
(Just groupRelayId, Just userChatRelayId, Just relayStatus) -> Just GroupRelay {groupRelayId, userChatRelayId, relayStatus, relayLink}
_ -> Nothing
in GroupMember {..}
@@ -711,7 +712,7 @@ groupMemberQuery =
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
m.is_chat_relay, r.group_relay_id, r.relay_status, r.relay_link,
m.is_chat_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
@@ -737,7 +738,7 @@ toBusinessChatInfo _ = Nothing
groupInfoQuery :: Query
groupInfoQuery = groupInfoQueryFields <> " " <> groupInfoQueryFrom
-- membership "member" never references group_relays, therefore `NULL, NULL, NULL` which avoids extra join
-- membership "member" never references group_relays, therefore `NULL, NULL, NULL, NULL` which avoids extra join
groupInfoQueryFields :: Query
groupInfoQueryFields =
[sql|
@@ -756,7 +757,7 @@ groupInfoQueryFields =
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
mu.created_at, mu.updated_at,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts,
mu.is_chat_relay, NULL, NULL, NULL
mu.is_chat_relay, NULL, NULL, NULL, NULL
|]
groupInfoQueryFrom :: Query
+5 -1
View File
@@ -820,8 +820,9 @@ data GroupLinkRejection = GroupLinkRejection
data GroupRelayInvitation = GroupRelayInvitation
{ fromMember :: MemberIdRole,
fromMemberProfile :: Profile,
invitedMember :: MemberIdRole,
groupProfile :: GroupProfile
groupLink :: ShortLinkContact
}
deriving (Eq, Show)
@@ -968,6 +969,7 @@ data GroupMember = GroupMember
data GroupRelay = GroupRelay
{ groupRelayId :: Int64,
userChatRelayId :: Int64, -- ID of configured UserChatRelay
relayStatus :: RelayStatus,
relayLink :: Maybe ShortLinkContact
}
@@ -2076,6 +2078,8 @@ $(JQ.deriveJSON defaultJSON ''GroupLinkInvitation)
$(JQ.deriveJSON defaultJSON ''GroupLinkRejection)
$(JQ.deriveJSON defaultJSON ''GroupRelayInvitation)
$(JQ.deriveJSON defaultJSON ''IntroInvitation)
$(JQ.deriveJSON defaultJSON ''MemberRestrictions)