mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-25 14:14:39 +00:00
Merge branch 'master' into group-knocking
This commit is contained in:
+6
-76
@@ -30,6 +30,7 @@ import Data.Time.Clock (getCurrentTime)
|
||||
import Simplex.Chat.Controller
|
||||
import Simplex.Chat.Library.Commands
|
||||
import Simplex.Chat.Operators
|
||||
import Simplex.Chat.Operators.Presets
|
||||
import Simplex.Chat.Options
|
||||
import Simplex.Chat.Options.DB
|
||||
import Simplex.Chat.Protocol
|
||||
@@ -39,7 +40,7 @@ import Simplex.Chat.Types
|
||||
import Simplex.Chat.Util (shuffle)
|
||||
import Simplex.FileTransfer.Client.Presets (defaultXFTPServers)
|
||||
import Simplex.Messaging.Agent as Agent
|
||||
import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..), InitialAgentServers (..), ServerCfg (..), ServerRoles (..), allRoles, createAgentStore, defaultAgentConfig, presetServerCfg)
|
||||
import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..), InitialAgentServers (..), ServerCfg (..), allRoles, createAgentStore, defaultAgentConfig, presetServerCfg)
|
||||
import Simplex.Messaging.Agent.Protocol
|
||||
import Simplex.Messaging.Agent.Store.Common (DBStore (dbNew))
|
||||
import qualified Simplex.Messaging.Agent.Store.DB as DB
|
||||
@@ -51,34 +52,6 @@ import qualified Simplex.Messaging.TMap as TM
|
||||
import qualified UnliftIO.Exception as E
|
||||
import UnliftIO.STM
|
||||
|
||||
operatorSimpleXChat :: NewServerOperator
|
||||
operatorSimpleXChat =
|
||||
ServerOperator
|
||||
{ operatorId = DBNewEntity,
|
||||
operatorTag = Just OTSimplex,
|
||||
tradeName = "SimpleX Chat",
|
||||
legalName = Just "SimpleX Chat Ltd",
|
||||
serverDomains = ["simplex.im"],
|
||||
conditionsAcceptance = CARequired Nothing,
|
||||
enabled = True,
|
||||
smpRoles = allRoles,
|
||||
xftpRoles = allRoles
|
||||
}
|
||||
|
||||
operatorFlux :: NewServerOperator
|
||||
operatorFlux =
|
||||
ServerOperator
|
||||
{ operatorId = DBNewEntity,
|
||||
operatorTag = Just OTFlux,
|
||||
tradeName = "Flux",
|
||||
legalName = Just "InFlux Technologies Limited",
|
||||
serverDomains = ["simplexonflux.com"],
|
||||
conditionsAcceptance = CARequired Nothing,
|
||||
enabled = False,
|
||||
smpRoles = ServerRoles {storage = False, proxy = True},
|
||||
xftpRoles = ServerRoles {storage = False, proxy = True}
|
||||
}
|
||||
|
||||
defaultChatConfig :: ChatConfig
|
||||
defaultChatConfig =
|
||||
ChatConfig
|
||||
@@ -112,6 +85,10 @@ defaultChatConfig =
|
||||
ntf = _defaultNtfServers,
|
||||
netCfg = defaultNetworkConfig
|
||||
},
|
||||
-- please note: if these servers are changed, this option needs to be split to two,
|
||||
-- to have a different set of servers on the receiving end and on the sending end.
|
||||
-- To preserve backward compatibility receiving end should update before the sending.
|
||||
shortLinkPresetServers = allPresetServers,
|
||||
tbqSize = 1024,
|
||||
fileChunkSize = 15780, -- do not change
|
||||
xftpDescrPartSize = 14000,
|
||||
@@ -133,53 +110,6 @@ defaultChatConfig =
|
||||
chatHooks = defaultChatHooks
|
||||
}
|
||||
|
||||
simplexChatSMPServers :: [NewUserServer 'PSMP]
|
||||
simplexChatSMPServers =
|
||||
map
|
||||
(presetServer True)
|
||||
[ "smp://0YuTwO05YJWS8rkjn9eLJDjQhFKvIYd8d4xG8X1blIU=@smp8.simplex.im,beccx4yfxxbvyhqypaavemqurytl6hozr47wfc7uuecacjqdvwpw2xid.onion",
|
||||
"smp://SkIkI6EPd2D63F4xFKfHk7I1UGZVNn6k1QWZ5rcyr6w=@smp9.simplex.im,jssqzccmrcws6bhmn77vgmhfjmhwlyr3u7puw4erkyoosywgl67slqqd.onion",
|
||||
"smp://6iIcWT_dF2zN_w5xzZEY7HI2Prbh3ldP07YTyDexPjE=@smp10.simplex.im,rb2pbttocvnbrngnwziclp2f4ckjq65kebafws6g4hy22cdaiv5dwjqd.onion",
|
||||
"smp://1OwYGt-yqOfe2IyVHhxz3ohqo3aCCMjtB-8wn4X_aoY=@smp11.simplex.im,6ioorbm6i3yxmuoezrhjk6f6qgkc4syabh7m3so74xunb5nzr4pwgfqd.onion",
|
||||
"smp://UkMFNAXLXeAAe0beCa4w6X_zp18PwxSaSjY17BKUGXQ=@smp12.simplex.im,ie42b5weq7zdkghocs3mgxdjeuycheeqqmksntj57rmejagmg4eor5yd.onion",
|
||||
"smp://enEkec4hlR3UtKx2NMpOUK_K4ZuDxjWBO1d9Y4YXVaA=@smp14.simplex.im,aspkyu2sopsnizbyfabtsicikr2s4r3ti35jogbcekhm3fsoeyjvgrid.onion",
|
||||
"smp://h--vW7ZSkXPeOUpfxlFGgauQmXNFOzGoizak7Ult7cw=@smp15.simplex.im,oauu4bgijybyhczbnxtlggo6hiubahmeutaqineuyy23aojpih3dajad.onion",
|
||||
"smp://hejn2gVIqNU6xjtGM3OwQeuk8ZEbDXVJXAlnSBJBWUA=@smp16.simplex.im,p3ktngodzi6qrf7w64mmde3syuzrv57y55hxabqcq3l5p6oi7yzze6qd.onion",
|
||||
"smp://ZKe4uxF4Z_aLJJOEsC-Y6hSkXgQS5-oc442JQGkyP8M=@smp17.simplex.im,ogtwfxyi3h2h5weftjjpjmxclhb5ugufa5rcyrmg7j4xlch7qsr5nuqd.onion",
|
||||
"smp://PtsqghzQKU83kYTlQ1VKg996dW4Cw4x_bvpKmiv8uns=@smp18.simplex.im,lyqpnwbs2zqfr45jqkncwpywpbtq7jrhxnib5qddtr6npjyezuwd3nqd.onion",
|
||||
"smp://N_McQS3F9TGoh4ER0QstUf55kGnNSd-wXfNPZ7HukcM=@smp19.simplex.im,i53bbtoqhlc365k6kxzwdp5w3cdt433s7bwh3y32rcbml2vztiyyz5id.onion"
|
||||
]
|
||||
<> map
|
||||
(presetServer False)
|
||||
[ "smp://u2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU=@smp4.simplex.im,o5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion",
|
||||
"smp://hpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg=@smp5.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion",
|
||||
"smp://PQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo=@smp6.simplex.im,bylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion"
|
||||
]
|
||||
|
||||
fluxSMPServers :: [NewUserServer 'PSMP]
|
||||
fluxSMPServers =
|
||||
map
|
||||
(presetServer True)
|
||||
[ "smp://xQW_ufMkGE20UrTlBl8QqceG1tbuylXhr9VOLPyRJmw=@smp1.simplexonflux.com,qb4yoanyl4p7o33yrknv4rs6qo7ugeb2tu2zo66sbebezs4cpyosarid.onion",
|
||||
"smp://LDnWZVlAUInmjmdpQQoIo6FUinRXGe0q3zi5okXDE4s=@smp2.simplexonflux.com,yiqtuh3q4x7hgovkomafsod52wvfjucdljqbbipg5sdssnklgongxbqd.onion",
|
||||
"smp://1jne379u7IDJSxAvXbWb_JgoE7iabcslX0LBF22Rej0=@smp3.simplexonflux.com,a5lm4k7ufei66cdck6fy63r4lmkqy3dekmmb7jkfdm5ivi6kfaojshad.onion",
|
||||
"smp://xmAmqj75I9mWrUihLUlI0ZuNLXlIwFIlHRq5Pb6cHAU=@smp4.simplexonflux.com,qpcz2axyy66u26hfdd2e23uohcf3y6c36mn7dcuilcgnwjasnrvnxjqd.onion",
|
||||
"smp://rWvBYyTamuRCBYb_KAn-nsejg879ndhiTg5Sq3k0xWA=@smp5.simplexonflux.com,4ao347qwiuluyd45xunmii4skjigzuuox53hpdsgbwxqafd4yrticead.onion",
|
||||
"smp://PN7-uqLBToqlf1NxHEaiL35lV2vBpXq8Nj8BW11bU48=@smp6.simplexonflux.com,hury6ot3ymebbr2535mlp7gcxzrjpc6oujhtfxcfh2m4fal4xw5fq6qd.onion"
|
||||
]
|
||||
|
||||
fluxXFTPServers :: [NewUserServer 'PXFTP]
|
||||
fluxXFTPServers =
|
||||
map
|
||||
(presetServer True)
|
||||
[ "xftp://92Sctlc09vHl_nAqF2min88zKyjdYJ9mgxRCJns5K2U=@xftp1.simplexonflux.com,apl3pumq3emwqtrztykyyoomdx4dg6ysql5zek2bi3rgznz7ai3odkid.onion",
|
||||
"xftp://YBXy4f5zU1CEhnbbCzVWTNVNsaETcAGmYqGNxHntiE8=@xftp2.simplexonflux.com,c5jjecisncnngysah3cz2mppediutfelco4asx65mi75d44njvua3xid.onion",
|
||||
"xftp://ARQO74ZSvv2OrulRF3CdgwPz_AMy27r0phtLSq5b664=@xftp3.simplexonflux.com,dc4mohiubvbnsdfqqn7xhlhpqs5u4tjzp7xpz6v6corwvzvqjtaqqiqd.onion",
|
||||
"xftp://ub2jmAa9U0uQCy90O-fSUNaYCj6sdhl49Jh3VpNXP58=@xftp4.simplexonflux.com,4qq5pzier3i4yhpuhcrhfbl6j25udc4czoyascrj4yswhodhfwev3nyd.onion",
|
||||
"xftp://Rh19D5e4Eez37DEE9hAlXDB3gZa1BdFYJTPgJWPO9OI=@xftp5.simplexonflux.com,q7itltdn32hjmgcqwhow4tay5ijetng3ur32bolssw32fvc5jrwvozad.onion",
|
||||
"xftp://0AznwoyfX8Od9T_acp1QeeKtxUi676IBIiQjXVwbdyU=@xftp6.simplexonflux.com,upvzf23ou6nrmaf3qgnhd6cn3d74tvivlmz3p7wdfwq6fhthjrjiiqid.onion"
|
||||
]
|
||||
|
||||
logCfg :: LogConfig
|
||||
logCfg = LogConfig {lc_file = Nothing, lc_stderr = True}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import qualified Data.ByteString.Char8 as B
|
||||
import Data.List.NonEmpty (NonEmpty)
|
||||
import qualified Data.List.NonEmpty as L
|
||||
import qualified Data.Map.Strict as M
|
||||
import Data.Maybe (isJust)
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
import Simplex.Chat.Controller
|
||||
@@ -24,6 +25,7 @@ import Simplex.Chat.Messages.CIContent
|
||||
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.Encoding.String (strEncode)
|
||||
import System.Exit (exitFailure)
|
||||
|
||||
@@ -49,16 +51,18 @@ initializeBotAddress = initializeBotAddress' True
|
||||
initializeBotAddress' :: Bool -> ChatController -> IO ()
|
||||
initializeBotAddress' logAddress cc = do
|
||||
sendChatCmd cc ShowMyAddress >>= \case
|
||||
CRUserContactLink _ UserContactLink {connReqContact} -> showBotAddress connReqContact
|
||||
CRUserContactLink _ UserContactLink {connLinkContact} -> showBotAddress connLinkContact
|
||||
CRChatCmdError _ (ChatErrorStore SEUserContactLinkNotFound) -> do
|
||||
when logAddress $ putStrLn "No bot address, creating..."
|
||||
sendChatCmd cc CreateMyAddress >>= \case
|
||||
CRUserContactLinkCreated _ uri -> showBotAddress uri
|
||||
-- TODO [short links] create short link by default
|
||||
sendChatCmd cc (CreateMyAddress False) >>= \case
|
||||
CRUserContactLinkCreated _ ccLink -> showBotAddress ccLink
|
||||
_ -> putStrLn "can't create bot address" >> exitFailure
|
||||
_ -> putStrLn "unexpected response" >> exitFailure
|
||||
where
|
||||
showBotAddress uri = do
|
||||
when logAddress $ putStrLn $ "Bot's contact address is: " <> B.unpack (strEncode uri)
|
||||
showBotAddress (CCLink uri shortUri) = do
|
||||
when logAddress $ putStrLn $ "Bot's contact address is: " <> B.unpack (maybe (strEncode uri) strEncode shortUri)
|
||||
when (isJust shortUri) $ putStrLn $ "Full contact address for old clients: " <> B.unpack (strEncode uri)
|
||||
void $ sendChatCmd cc $ AddressAutoAccept $ Just AutoAccept {businessAddress = False, acceptIncognito = False, autoReply = Nothing}
|
||||
|
||||
sendMessage :: ChatController -> Contact -> Text -> IO ()
|
||||
|
||||
@@ -138,6 +138,7 @@ data ChatConfig = ChatConfig
|
||||
chatVRange :: VersionRangeChat,
|
||||
confirmMigrations :: MigrationConfirmation,
|
||||
presetServers :: PresetServers,
|
||||
shortLinkPresetServers :: NonEmpty SMPServer,
|
||||
tbqSize :: Natural,
|
||||
fileChunkSize :: Integer,
|
||||
xftpDescrPartSize :: Int,
|
||||
@@ -366,7 +367,7 @@ data ChatCommand
|
||||
-- | APIDeleteGroupConversations GroupId (NonEmpty GroupConversationId)
|
||||
-- | APIArchiveGroupConversations GroupId (NonEmpty GroupConversationId)
|
||||
| APIUpdateGroupProfile GroupId GroupProfile
|
||||
| APICreateGroupLink GroupId GroupMemberRole
|
||||
| APICreateGroupLink GroupId GroupMemberRole CreateShortLink
|
||||
| APIGroupLinkMemberRole GroupId GroupMemberRole
|
||||
| APIDeleteGroupLink GroupId
|
||||
| APIGetGroupLink GroupId
|
||||
@@ -439,21 +440,21 @@ data ChatCommand
|
||||
| EnableGroupMember GroupName ContactName
|
||||
| ChatHelp HelpSection
|
||||
| Welcome
|
||||
| APIAddContact UserId IncognitoEnabled
|
||||
| AddContact IncognitoEnabled
|
||||
| APIAddContact UserId CreateShortLink IncognitoEnabled
|
||||
| AddContact CreateShortLink IncognitoEnabled
|
||||
| APISetConnectionIncognito Int64 IncognitoEnabled
|
||||
| APIChangeConnectionUser Int64 UserId -- new user id to switch connection to
|
||||
| APIConnectPlan UserId AConnectionRequestUri
|
||||
| APIConnect UserId IncognitoEnabled (Maybe AConnectionRequestUri)
|
||||
| Connect IncognitoEnabled (Maybe AConnectionRequestUri)
|
||||
| APIConnectPlan UserId AConnectionLink
|
||||
| APIConnect UserId IncognitoEnabled (Maybe ACreatedConnLink)
|
||||
| Connect IncognitoEnabled (Maybe AConnectionLink)
|
||||
| APIConnectContactViaAddress UserId IncognitoEnabled ContactId
|
||||
| ConnectSimplex IncognitoEnabled -- UserId (not used in UI)
|
||||
| DeleteContact ContactName ChatDeleteMode
|
||||
| ClearContact ContactName
|
||||
| APIListContacts UserId
|
||||
| ListContacts
|
||||
| APICreateMyAddress UserId
|
||||
| CreateMyAddress
|
||||
| APICreateMyAddress UserId CreateShortLink
|
||||
| CreateMyAddress CreateShortLink
|
||||
| APIDeleteMyAddress UserId
|
||||
| DeleteMyAddress
|
||||
| APIShowMyAddress UserId
|
||||
@@ -495,7 +496,7 @@ data ChatCommand
|
||||
| ShowGroupProfile GroupName
|
||||
| UpdateGroupDescription GroupName (Maybe Text)
|
||||
| ShowGroupDescription GroupName
|
||||
| CreateGroupLink GroupName GroupMemberRole
|
||||
| CreateGroupLink GroupName GroupMemberRole CreateShortLink
|
||||
| GroupLinkMemberRole GroupName GroupMemberRole
|
||||
| DeleteGroupLink GroupName
|
||||
| ShowGroupLink GroupName
|
||||
@@ -680,10 +681,10 @@ data ChatResponse
|
||||
| CRUserProfileNoChange {user :: User}
|
||||
| CRUserPrivacy {user :: User, updatedUser :: User}
|
||||
| CRVersionInfo {versionInfo :: CoreVersionInfo, chatMigrations :: [UpMigration], agentMigrations :: [UpMigration]}
|
||||
| CRInvitation {user :: User, connReqInvitation :: ConnReqInvitation, connection :: PendingContactConnection}
|
||||
| CRInvitation {user :: User, connLinkInvitation :: CreatedLinkInvitation, connection :: PendingContactConnection}
|
||||
| CRConnectionIncognitoUpdated {user :: User, toConnection :: PendingContactConnection}
|
||||
| CRConnectionUserChanged {user :: User, fromConnection :: PendingContactConnection, toConnection :: PendingContactConnection, newUser :: User}
|
||||
| CRConnectionPlan {user :: User, connectionPlan :: ConnectionPlan}
|
||||
| CRConnectionPlan {user :: User, connLink :: ACreatedConnLink, connectionPlan :: ConnectionPlan}
|
||||
| CRSentConfirmation {user :: User, connection :: PendingContactConnection}
|
||||
| CRSentInvitation {user :: User, connection :: PendingContactConnection, customUserProfile :: Maybe Profile}
|
||||
| CRSentInvitationToContact {user :: User, contact :: Contact, customUserProfile :: Maybe Profile}
|
||||
@@ -693,7 +694,7 @@ data ChatResponse
|
||||
| CRContactDeleted {user :: User, contact :: Contact}
|
||||
| CRContactDeletedByContact {user :: User, contact :: Contact}
|
||||
| CRChatCleared {user :: User, chatInfo :: AChatInfo}
|
||||
| CRUserContactLinkCreated {user :: User, connReqContact :: ConnReqContact}
|
||||
| CRUserContactLinkCreated {user :: User, connLinkContact :: CreatedLinkContact}
|
||||
| CRUserContactLinkDeleted {user :: User}
|
||||
| CRReceivedContactRequest {user :: User, contactRequest :: UserContactRequest}
|
||||
| CRAcceptingContactRequest {user :: User, contact :: Contact}
|
||||
@@ -773,8 +774,8 @@ data ChatResponse
|
||||
| CRGroupUpdated {user :: User, fromGroup :: GroupInfo, toGroup :: GroupInfo, member_ :: Maybe GroupMember}
|
||||
| CRGroupProfile {user :: User, groupInfo :: GroupInfo}
|
||||
| CRGroupDescription {user :: User, groupInfo :: GroupInfo} -- only used in CLI
|
||||
| CRGroupLinkCreated {user :: User, groupInfo :: GroupInfo, connReqContact :: ConnReqContact, memberRole :: GroupMemberRole}
|
||||
| CRGroupLink {user :: User, groupInfo :: GroupInfo, connReqContact :: ConnReqContact, memberRole :: GroupMemberRole}
|
||||
| CRGroupLinkCreated {user :: User, groupInfo :: GroupInfo, connLinkContact :: CreatedLinkContact, memberRole :: GroupMemberRole}
|
||||
| CRGroupLink {user :: User, groupInfo :: GroupInfo, connLinkContact :: CreatedLinkContact, memberRole :: GroupMemberRole}
|
||||
| CRGroupLinkDeleted {user :: User, groupInfo :: GroupInfo}
|
||||
| CRAcceptingGroupJoinRequestMember {user :: User, groupInfo :: GroupInfo, member :: GroupMember}
|
||||
| CRNoMemberContactCreating {user :: User, groupInfo :: GroupInfo, member :: GroupMember} -- only used in CLI
|
||||
@@ -949,6 +950,7 @@ data ConnectionPlan
|
||||
= CPInvitationLink {invitationLinkPlan :: InvitationLinkPlan}
|
||||
| CPContactAddress {contactAddressPlan :: ContactAddressPlan}
|
||||
| CPGroupLink {groupLinkPlan :: GroupLinkPlan}
|
||||
| CPError {chatError :: ChatError}
|
||||
deriving (Show)
|
||||
|
||||
data InvitationLinkPlan
|
||||
@@ -992,6 +994,7 @@ connectionPlanProceed = \case
|
||||
GLPOwnLink _ -> True
|
||||
GLPConnectingConfirmReconnect -> True
|
||||
_ -> False
|
||||
CPError _ -> True
|
||||
|
||||
data ForwardConfirmation
|
||||
= FCFilesNotAccepted {fileIds :: [FileTransferId]}
|
||||
@@ -1255,8 +1258,8 @@ data ChatErrorType
|
||||
| CEChatNotStarted
|
||||
| CEChatNotStopped
|
||||
| CEChatStoreChanged
|
||||
| CEConnectionPlan {connectionPlan :: ConnectionPlan}
|
||||
| CEInvalidConnReq
|
||||
| CEUnsupportedConnReq
|
||||
| CEInvalidChatMessage {connection :: Connection, msgMeta :: Maybe MsgMetaJSON, messageData :: Text, message :: String}
|
||||
| CEContactNotFound {contactName :: ContactName, suspectedMember :: Maybe (GroupInfo, GroupMember)}
|
||||
| CEContactNotReady {contact :: Contact}
|
||||
@@ -1591,8 +1594,6 @@ $(JQ.deriveJSON (sumTypeJSON $ dropPrefix "CAP") ''ContactAddressPlan)
|
||||
|
||||
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "GLP") ''GroupLinkPlan)
|
||||
|
||||
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "CP") ''ConnectionPlan)
|
||||
|
||||
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "FC") ''ForwardConfirmation)
|
||||
|
||||
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "CE") ''ChatErrorType)
|
||||
@@ -1607,6 +1608,8 @@ $(JQ.deriveJSON (sumTypeJSON $ dropPrefix "DB") ''DatabaseError)
|
||||
|
||||
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "Chat") ''ChatError)
|
||||
|
||||
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "CP") ''ConnectionPlan)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''AppFilePathsConfig)
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''ContactSubStatus)
|
||||
|
||||
@@ -1666,16 +1666,18 @@ processChatCommand' vr = \case
|
||||
EnableGroupMember gName mName -> withMemberName gName mName $ \gId mId -> APIEnableGroupMember gId mId
|
||||
ChatHelp section -> pure $ CRChatHelp section
|
||||
Welcome -> withUser $ pure . CRWelcome
|
||||
APIAddContact userId incognito -> withUserId userId $ \user -> procCmd $ do
|
||||
APIAddContact userId short incognito -> withUserId userId $ \user -> procCmd $ do
|
||||
-- [incognito] generate profile for connection
|
||||
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
(connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing IKPQOn subMode
|
||||
let userData = shortLinkUserData short
|
||||
(connId, ccLink) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation userData Nothing IKPQOn subMode
|
||||
ccLink' <- shortenCreatedLink ccLink
|
||||
-- TODO PQ pass minVersion from the current range
|
||||
conn <- withFastStore' $ \db -> createDirectConnection db user connId cReq ConnNew incognitoProfile subMode initialChatVersion PQSupportOn
|
||||
pure $ CRInvitation user cReq conn
|
||||
AddContact incognito -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APIAddContact userId incognito
|
||||
conn <- withFastStore' $ \db -> createDirectConnection db user connId ccLink' ConnNew incognitoProfile subMode initialChatVersion PQSupportOn
|
||||
pure $ CRInvitation user ccLink' conn
|
||||
AddContact short incognito -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APIAddContact userId short incognito
|
||||
APISetConnectionIncognito connId incognito -> withUser $ \user@User {userId} -> do
|
||||
conn'_ <- withFastStore $ \db -> do
|
||||
conn@PendingContactConnection {pccConnStatus, customUserProfileId} <- getPendingContactConnection db userId connId
|
||||
@@ -1693,9 +1695,9 @@ processChatCommand' vr = \case
|
||||
Nothing -> throwChatError CEConnectionIncognitoChangeProhibited
|
||||
APIChangeConnectionUser connId newUserId -> withUser $ \user@User {userId} -> do
|
||||
conn <- withFastStore $ \db -> getPendingContactConnection db userId connId
|
||||
let PendingContactConnection {pccConnStatus, connReqInv} = conn
|
||||
case (pccConnStatus, connReqInv) of
|
||||
(ConnNew, Just cReqInv) -> do
|
||||
let PendingContactConnection {pccConnStatus, connLinkInv} = conn
|
||||
case (pccConnStatus, connLinkInv) of
|
||||
(ConnNew, Just (CCLink cReqInv _)) -> do
|
||||
newUser <- privateGetUser newUserId
|
||||
conn' <- ifM (canKeepLink cReqInv newUser) (updateConnRecord user conn newUser) (recreateConn user conn newUser)
|
||||
pure $ CRConnectionUserChanged user conn conn' newUser
|
||||
@@ -1716,19 +1718,21 @@ processChatCommand' vr = \case
|
||||
forM_ customUserProfileId $ \profileId ->
|
||||
deletePCCIncognitoProfile db user profileId
|
||||
pure conn'
|
||||
recreateConn user conn@PendingContactConnection {customUserProfileId} newUser = do
|
||||
recreateConn user conn@PendingContactConnection {customUserProfileId, connLinkInv} newUser = do
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
(agConnId, cReq) <- withAgent $ \a -> createConnection a (aUserId newUser) True SCMInvitation Nothing IKPQOn subMode
|
||||
let userData = shortLinkUserData $ isJust $ connShortLink =<< connLinkInv
|
||||
(agConnId, ccLink) <- withAgent $ \a -> createConnection a (aUserId newUser) True SCMInvitation userData Nothing IKPQOn subMode
|
||||
ccLink' <- shortenCreatedLink ccLink
|
||||
conn' <- withFastStore' $ \db -> do
|
||||
deleteConnectionRecord db user connId
|
||||
forM_ customUserProfileId $ \profileId ->
|
||||
deletePCCIncognitoProfile db user profileId
|
||||
createDirectConnection db newUser agConnId cReq ConnNew Nothing subMode initialChatVersion PQSupportOn
|
||||
createDirectConnection db newUser agConnId ccLink' ConnNew Nothing subMode initialChatVersion PQSupportOn
|
||||
deleteAgentConnectionAsync user (aConnId' conn)
|
||||
pure conn'
|
||||
APIConnectPlan userId cReqUri -> withUserId userId $ \user ->
|
||||
CRConnectionPlan user <$> connectPlan user cReqUri
|
||||
APIConnect userId incognito (Just (ACR SCMInvitation cReq@(CRInvitationUri crData e2e))) -> withUserId userId $ \user -> withInvitationLock "connect" (strEncode cReq) . procCmd $ do
|
||||
APIConnectPlan userId cLink -> withUserId userId $ \user ->
|
||||
uncurry (CRConnectionPlan user) <$> connectPlan user cLink
|
||||
APIConnect userId incognito (Just (ACCL SCMInvitation (CCLink cReq@(CRInvitationUri crData e2e) sLnk_))) -> withUserId userId $ \user -> withInvitationLock "connect" (strEncode cReq) . procCmd $ do
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
-- [incognito] generate profile to send
|
||||
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
|
||||
@@ -1751,7 +1755,8 @@ processChatCommand' vr = \case
|
||||
where
|
||||
joinNewConn chatV dm = do
|
||||
connId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq pqSup'
|
||||
pcc <- withFastStore' $ \db -> createDirectConnection db user connId cReq ConnPrepared (incognitoProfile $> profileToSend) subMode chatV pqSup'
|
||||
let ccLink = CCLink cReq $ serverShortLink <$> sLnk_
|
||||
pcc <- withFastStore' $ \db -> createDirectConnection db user connId ccLink ConnPrepared (incognitoProfile $> profileToSend) subMode chatV pqSup'
|
||||
joinPreparedConn connId pcc dm
|
||||
joinPreparedConn connId pcc@PendingContactConnection {pccConnId} dm = do
|
||||
void $ withAgent $ \a -> joinConnection a (aUserId user) connId True cReq dm pqSup' subMode
|
||||
@@ -1761,43 +1766,40 @@ processChatCommand' vr = \case
|
||||
( CRInvitationUri crData {crScheme = SSSimplex} e2e,
|
||||
CRInvitationUri crData {crScheme = simplexChat} e2e
|
||||
)
|
||||
APIConnect userId incognito (Just (ACR SCMContact cReq)) -> withUserId userId $ \user -> connectViaContact user incognito cReq
|
||||
APIConnect userId incognito (Just (ACCL SCMContact ccLink)) -> withUserId userId $ \user -> connectViaContact user incognito ccLink
|
||||
APIConnect _ _ Nothing -> throwChatError CEInvalidConnReq
|
||||
Connect incognito aCReqUri@(Just cReqUri) -> withUser $ \user@User {userId} -> do
|
||||
plan <- connectPlan user cReqUri `catchChatError` const (pure $ CPInvitationLink ILPOk)
|
||||
unless (connectionPlanProceed plan) $ throwChatError (CEConnectionPlan plan)
|
||||
case plan of
|
||||
CPContactAddress (CAPContactViaAddress Contact {contactId}) ->
|
||||
processChatCommand $ APIConnectContactViaAddress userId incognito contactId
|
||||
_ -> processChatCommand $ APIConnect userId incognito aCReqUri
|
||||
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); _ -> throwError e
|
||||
connectWithPlan user incognito ccLink plan
|
||||
Connect _ Nothing -> throwChatError CEInvalidConnReq
|
||||
APIConnectContactViaAddress userId incognito contactId -> withUserId userId $ \user -> do
|
||||
ct@Contact {activeConn, profile = LocalProfile {contactLink}} <- withFastStore $ \db -> getContact db vr user contactId
|
||||
when (isJust activeConn) $ throwChatError (CECommandError "contact already has connection")
|
||||
case contactLink of
|
||||
Just cReq -> connectContactViaAddress user incognito ct cReq
|
||||
ccLink <- case contactLink of
|
||||
Just (CLFull cReq) -> pure $ CCLink cReq Nothing
|
||||
Just (CLShort sLnk) -> do
|
||||
cReq <- getShortLinkConnReq user sLnk
|
||||
pure $ CCLink cReq $ Just sLnk
|
||||
Nothing -> throwChatError (CECommandError "no address in contact profile")
|
||||
ConnectSimplex incognito -> withUser $ \user@User {userId} -> do
|
||||
let cReqUri = ACR SCMContact adminContactReq
|
||||
plan <- connectPlan user cReqUri `catchChatError` const (pure $ CPInvitationLink ILPOk)
|
||||
unless (connectionPlanProceed plan) $ throwChatError (CEConnectionPlan plan)
|
||||
case plan of
|
||||
CPContactAddress (CAPContactViaAddress Contact {contactId}) ->
|
||||
processChatCommand $ APIConnectContactViaAddress userId incognito contactId
|
||||
_ -> processChatCommand $ APIConnect userId incognito (Just cReqUri)
|
||||
connectContactViaAddress user incognito ct ccLink
|
||||
ConnectSimplex incognito -> withUser $ \user -> do
|
||||
plan <- contactRequestPlan user adminContactReq `catchChatError` const (pure $ CPContactAddress CAPOk)
|
||||
connectWithPlan user incognito (ACCL SCMContact (CCLink adminContactReq Nothing)) plan
|
||||
DeleteContact cName cdm -> withContactName cName $ \ctId -> APIDeleteChat (ChatRef CTDirect ctId Nothing) cdm
|
||||
ClearContact cName -> withContactName cName $ \chatId -> APIClearChat $ ChatRef CTDirect chatId Nothing
|
||||
APIListContacts userId -> withUserId userId $ \user ->
|
||||
CRContactsList user <$> withFastStore' (\db -> getUserContacts db vr user)
|
||||
ListContacts -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APIListContacts userId
|
||||
APICreateMyAddress userId -> withUserId userId $ \user -> procCmd $ do
|
||||
APICreateMyAddress userId short -> withUserId userId $ \user -> procCmd $ do
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
(connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMContact Nothing IKPQOn subMode
|
||||
withFastStore $ \db -> createUserContactLink db user connId cReq subMode
|
||||
pure $ CRUserContactLinkCreated user cReq
|
||||
CreateMyAddress -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APICreateMyAddress userId
|
||||
let userData = shortLinkUserData short
|
||||
(connId, ccLink) <- withAgent $ \a -> createConnection a (aUserId user) True SCMContact userData Nothing IKPQOn subMode
|
||||
ccLink' <- shortenCreatedLink ccLink
|
||||
withFastStore $ \db -> createUserContactLink db user connId ccLink' subMode
|
||||
pure $ CRUserContactLinkCreated user ccLink'
|
||||
CreateMyAddress short -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APICreateMyAddress userId short
|
||||
APIDeleteMyAddress userId -> withUserId userId $ \user@User {profile = p} -> do
|
||||
conns <- withFastStore $ \db -> getUserAddressConnections db vr user
|
||||
withChatLock "deleteMyAddress" $ do
|
||||
@@ -1819,8 +1821,9 @@ processChatCommand' vr = \case
|
||||
let p' = (fromLocalProfile p :: Profile) {contactLink = Nothing}
|
||||
updateProfile_ user p' $ withFastStore' $ \db -> setUserProfileContactLink db user Nothing
|
||||
APISetProfileAddress userId True -> withUserId userId $ \user@User {profile = p} -> do
|
||||
ucl@UserContactLink {connReqContact} <- withFastStore (`getUserAddress` user)
|
||||
let p' = (fromLocalProfile p :: Profile) {contactLink = Just connReqContact}
|
||||
ucl@UserContactLink {connLinkContact = CCLink cReq _} <- withFastStore (`getUserAddress` user)
|
||||
-- TODO [short links] replace with short links
|
||||
let p' = (fromLocalProfile p :: Profile) {contactLink = Just $ CLFull cReq}
|
||||
updateProfile_ user p' $ withFastStore' $ \db -> setUserProfileContactLink db user $ Just ucl
|
||||
SetProfileAddress onOff -> withUser $ \User {userId} ->
|
||||
processChatCommand $ APISetProfileAddress userId onOff
|
||||
@@ -1998,7 +2001,7 @@ processChatCommand' vr = \case
|
||||
Nothing -> do
|
||||
gVar <- asks random
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
(agentConnId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing IKPQOff subMode
|
||||
(agentConnId, CCLink cReq _) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing Nothing IKPQOff subMode
|
||||
member <- withFastStore $ \db -> createNewContactMember db gVar user gInfo contact memRole agentConnId cReq subMode
|
||||
sendInvitation member cReq
|
||||
pure $ CRSentGroupInvitation user gInfo contact member
|
||||
@@ -2342,16 +2345,18 @@ processChatCommand' vr = \case
|
||||
updateGroupProfileByName gName $ \p -> p {description}
|
||||
ShowGroupDescription gName -> withUser $ \user ->
|
||||
CRGroupDescription user <$> withFastStore (\db -> getGroupInfoByName db vr user gName)
|
||||
APICreateGroupLink groupId mRole -> withUser $ \user -> withGroupLock "createGroupLink" groupId $ do
|
||||
APICreateGroupLink groupId mRole short -> withUser $ \user -> withGroupLock "createGroupLink" groupId $ do
|
||||
gInfo <- withFastStore $ \db -> getGroupInfo db vr user groupId
|
||||
assertUserGroupRole gInfo GRAdmin
|
||||
when (mRole > GRMember) $ throwChatError $ CEGroupMemberInitialRole gInfo mRole
|
||||
groupLinkId <- GroupLinkId <$> drgRandomBytes 16
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
let crClientData = encodeJSON $ CRDataGroup groupLinkId
|
||||
(connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMContact (Just crClientData) IKPQOff subMode
|
||||
withFastStore $ \db -> createGroupLink db user gInfo connId cReq groupLinkId mRole subMode
|
||||
pure $ CRGroupLinkCreated user gInfo cReq mRole
|
||||
userData = shortLinkUserData short
|
||||
(connId, ccLink) <- withAgent $ \a -> createConnection a (aUserId user) True SCMContact userData (Just crClientData) IKPQOff subMode
|
||||
ccLink' <- createdGroupLink <$> shortenCreatedLink ccLink
|
||||
withFastStore $ \db -> createGroupLink db user gInfo connId ccLink' groupLinkId mRole subMode
|
||||
pure $ CRGroupLinkCreated user gInfo ccLink' mRole
|
||||
APIGroupLinkMemberRole groupId mRole' -> withUser $ \user -> withGroupLock "groupLinkMemberRole" groupId $ do
|
||||
gInfo <- withFastStore $ \db -> getGroupInfo db vr user groupId
|
||||
(groupLinkId, groupLink, mRole) <- withFastStore $ \db -> getGroupLink db user gInfo
|
||||
@@ -2377,7 +2382,7 @@ processChatCommand' vr = \case
|
||||
when (isJust $ memberContactId m) $ throwChatError $ CECommandError "member contact already exists"
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
-- TODO PQ should negotitate contact connection with PQSupportOn?
|
||||
(connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing IKPQOff subMode
|
||||
(connId, CCLink cReq _) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing Nothing IKPQOff subMode
|
||||
-- [incognito] reuse membership incognito profile
|
||||
ct <- withFastStore' $ \db -> createMemberContact db user connId cReq g m mConn subMode
|
||||
-- TODO not sure it is correct to set connections status here?
|
||||
@@ -2398,9 +2403,9 @@ processChatCommand' vr = \case
|
||||
toView $ CRNewChatItems user [AChatItem SCTDirect SMDSnd (DirectChat ct') ci]
|
||||
pure $ CRNewMemberContactSentInv user ct' g m
|
||||
_ -> throwChatError CEGroupMemberNotActive
|
||||
CreateGroupLink gName mRole -> withUser $ \user -> do
|
||||
CreateGroupLink gName mRole short -> withUser $ \user -> do
|
||||
groupId <- withFastStore $ \db -> getGroupIdByName db user gName
|
||||
processChatCommand $ APICreateGroupLink groupId mRole
|
||||
processChatCommand $ APICreateGroupLink groupId mRole short
|
||||
GroupLinkMemberRole gName mRole -> withUser $ \user -> do
|
||||
groupId <- withFastStore $ \db -> getGroupIdByName db user gName
|
||||
processChatCommand $ APIGroupLinkMemberRole groupId mRole
|
||||
@@ -2746,8 +2751,8 @@ processChatCommand' vr = \case
|
||||
CTGroup -> withFastStore $ \db -> getGroupChatItemIdByText' db user cId msg
|
||||
CTLocal -> withFastStore $ \db -> getLocalChatItemIdByText' db user cId msg
|
||||
_ -> throwChatError $ CECommandError "not supported"
|
||||
connectViaContact :: User -> IncognitoEnabled -> ConnectionRequestUri 'CMContact -> CM ChatResponse
|
||||
connectViaContact user@User {userId} incognito cReq@(CRContactUri ConnReqUriData {crClientData}) = withInvitationLock "connectViaContact" (strEncode cReq) $ do
|
||||
connectViaContact :: User -> IncognitoEnabled -> CreatedLinkContact -> CM ChatResponse
|
||||
connectViaContact user@User {userId} incognito (CCLink cReq@(CRContactUri ConnReqUriData {crClientData}) sLnk) = withInvitationLock "connectViaContact" (strEncode cReq) $ do
|
||||
let groupLinkId = crClientData >>= decodeJSON >>= \(CRDataGroup gli) -> Just gli
|
||||
cReqHash = ConnReqUriHash . C.sha256Hash $ strEncode cReq
|
||||
case groupLinkId of
|
||||
@@ -2777,11 +2782,12 @@ processChatCommand' vr = \case
|
||||
-- [incognito] generate profile to send
|
||||
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
conn@PendingContactConnection {pccConnId} <- withFastStore' $ \db -> createConnReqConnection db userId connId cReqHash xContactId incognitoProfile groupLinkId subMode chatV pqSup
|
||||
let sLnk' = serverShortLink <$> sLnk
|
||||
conn@PendingContactConnection {pccConnId} <- withFastStore' $ \db -> createConnReqConnection db userId connId cReqHash sLnk' xContactId incognitoProfile groupLinkId subMode chatV pqSup
|
||||
joinContact user pccConnId connId cReq incognitoProfile xContactId inGroup pqSup chatV
|
||||
pure $ CRSentInvitation user conn incognitoProfile
|
||||
connectContactViaAddress :: User -> IncognitoEnabled -> Contact -> ConnectionRequestUri 'CMContact -> CM ChatResponse
|
||||
connectContactViaAddress user incognito ct cReq =
|
||||
connectContactViaAddress :: User -> IncognitoEnabled -> Contact -> CreatedLinkContact -> CM ChatResponse
|
||||
connectContactViaAddress user incognito ct (CCLink cReq shortLink) =
|
||||
withInvitationLock "connectContactViaAddress" (strEncode cReq) $ do
|
||||
newXContactId <- XContactId <$> drgRandomBytes 16
|
||||
let pqSup = PQSupportOn
|
||||
@@ -2790,10 +2796,10 @@ processChatCommand' vr = \case
|
||||
-- [incognito] generate profile to send
|
||||
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
|
||||
subMode <- chatReadVar subscriptionMode
|
||||
(pccConnId, ct') <- withFastStore $ \db -> createAddressContactConnection db vr user ct connId cReqHash newXContactId incognitoProfile subMode chatV pqSup
|
||||
(pccConnId, ct') <- withFastStore $ \db -> createAddressContactConnection db vr user ct connId cReqHash shortLink newXContactId incognitoProfile subMode chatV pqSup
|
||||
joinContact user pccConnId connId cReq incognitoProfile newXContactId False pqSup chatV
|
||||
pure $ CRSentInvitationToContact user ct' incognitoProfile
|
||||
prepareContact :: User -> ConnectionRequestUri 'CMContact -> PQSupport -> CM (ConnId, VersionChat)
|
||||
prepareContact :: User -> ConnReqContact -> PQSupport -> CM (ConnId, VersionChat)
|
||||
prepareContact user cReq pqSup = do
|
||||
-- 0) toggle disabled - PQSupportOff
|
||||
-- 1) toggle enabled, address supports PQ (connRequestPQSupport returns Just True) - PQSupportOn, enable support with compression
|
||||
@@ -2804,7 +2810,7 @@ processChatCommand' vr = \case
|
||||
let chatV = agentToChatVersion agentV
|
||||
connId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq pqSup
|
||||
pure (connId, chatV)
|
||||
joinContact :: User -> Int64 -> ConnId -> ConnectionRequestUri 'CMContact -> Maybe Profile -> XContactId -> Bool -> PQSupport -> VersionChat -> CM ()
|
||||
joinContact :: User -> Int64 -> ConnId -> ConnReqContact -> Maybe Profile -> XContactId -> Bool -> PQSupport -> VersionChat -> CM ()
|
||||
joinContact user pccConnId connId cReq incognitoProfile xContactId inGroup pqSup chatV = do
|
||||
let profileToSend = userProfileToSend user incognitoProfile Nothing inGroup
|
||||
dm <- encodeConnInfoPQ pqSup chatV (XContact profileToSend $ Just xContactId)
|
||||
@@ -3110,32 +3116,77 @@ processChatCommand' vr = \case
|
||||
pure (gId, chatSettings)
|
||||
_ -> throwChatError $ CECommandError "not supported"
|
||||
processChatCommand $ APISetChatSettings (ChatRef cType chatId Nothing) $ updateSettings chatSettings
|
||||
connectPlan :: User -> AConnectionRequestUri -> CM ConnectionPlan
|
||||
connectPlan user (ACR SCMInvitation (CRInvitationUri crData e2e)) = do
|
||||
withFastStore' (\db -> getConnectionEntityByConnReq db vr user cReqSchemas) >>= \case
|
||||
Nothing -> pure $ CPInvitationLink ILPOk
|
||||
Just (RcvDirectMsgConnection Connection {connStatus = ConnPrepared} Nothing) ->
|
||||
pure $ CPInvitationLink ILPOk
|
||||
Just (RcvDirectMsgConnection conn ct_) -> do
|
||||
let Connection {connStatus, contactConnInitiated} = conn
|
||||
if
|
||||
| connStatus == ConnNew && contactConnInitiated ->
|
||||
pure $ CPInvitationLink ILPOwnLink
|
||||
| not (connReady conn) ->
|
||||
pure $ CPInvitationLink (ILPConnecting ct_)
|
||||
| otherwise -> case ct_ of
|
||||
Just ct -> pure $ CPInvitationLink (ILPKnown ct)
|
||||
Nothing -> throwChatError $ CEInternalError "ready RcvDirectMsgConnection connection should have associated contact"
|
||||
Just _ -> throwChatError $ CECommandError "found connection entity is not RcvDirectMsgConnection"
|
||||
connectPlan :: User -> AConnectionLink -> CM (ACreatedConnLink, ConnectionPlan)
|
||||
connectPlan user (ACL SCMInvitation cLink) = case cLink of
|
||||
CLFull cReq -> invitationReqAndPlan cReq Nothing
|
||||
CLShort l -> do
|
||||
let l' = serverShortLink l
|
||||
withFastStore' (\db -> getConnectionEntityViaShortLink db vr user l') >>= \case
|
||||
Just (cReq, ent) ->
|
||||
(ACCL SCMInvitation (CCLink cReq (Just l')),) <$> (invitationEntityPlan ent `catchChatError` (pure . CPError))
|
||||
Nothing -> getShortLinkConnReq user l' >>= (`invitationReqAndPlan` Just l')
|
||||
where
|
||||
cReqSchemas :: (ConnReqInvitation, ConnReqInvitation)
|
||||
cReqSchemas =
|
||||
invitationReqAndPlan cReq sLnk_ = do
|
||||
plan <- inviationRequestPlan user cReq `catchChatError` (pure . CPError)
|
||||
pure (ACCL SCMInvitation (CCLink cReq sLnk_), plan)
|
||||
connectPlan user (ACL SCMContact cLink) = case cLink of
|
||||
CLFull cReq -> contactReqAndPlan cReq Nothing
|
||||
CLShort l@(CSLContact _ ct _ _) -> do
|
||||
let l' = serverShortLink l
|
||||
case ct of
|
||||
CCTContact ->
|
||||
withFastStore' (\db -> getUserContactLinkViaShortLink db user l') >>= \case
|
||||
Just (UserContactLink (CCLink cReq _) _) -> pure (ACCL SCMContact $ CCLink cReq (Just l'), CPContactAddress CAPOwnLink)
|
||||
Nothing -> getShortLinkConnReq user l' >>= (`contactReqAndPlan` Just l')
|
||||
CCTGroup ->
|
||||
withFastStore' (\db -> getGroupInfoViaUserShortLink db vr user l') >>= \case
|
||||
Just (cReq, g) -> pure (ACCL SCMContact $ CCLink cReq (Just l'), CPGroupLink (GLPOwnLink g))
|
||||
Nothing -> getShortLinkConnReq user l' >>= (`contactReqAndPlan` Just l')
|
||||
CCTChannel -> throwChatError $ CECommandError "channel links are not supported in this version"
|
||||
where
|
||||
contactReqAndPlan cReq sLnk_ = do
|
||||
plan <- contactRequestPlan user cReq `catchChatError` (pure . CPError)
|
||||
pure (ACCL SCMContact $ CCLink cReq sLnk_, plan)
|
||||
connectWithPlan :: User -> IncognitoEnabled -> ACreatedConnLink -> ConnectionPlan -> CM ChatResponse
|
||||
connectWithPlan user@User {userId} incognito ccLink plan
|
||||
| connectionPlanProceed plan = do
|
||||
case plan of CPError e -> toView $ CRChatError (Just user) e; _ -> pure ()
|
||||
case plan of
|
||||
CPContactAddress (CAPContactViaAddress Contact {contactId}) ->
|
||||
processChatCommand $ APIConnectContactViaAddress userId incognito contactId
|
||||
_ -> processChatCommand $ APIConnect userId incognito (Just ccLink)
|
||||
| otherwise = pure $ CRConnectionPlan user ccLink plan
|
||||
inviationRequestPlan :: User -> ConnReqInvitation -> CM ConnectionPlan
|
||||
inviationRequestPlan user cReq = do
|
||||
withFastStore' (\db -> getConnectionEntityByConnReq db vr user $ cReqSchemas cReq) >>= \case
|
||||
Nothing -> pure $ CPInvitationLink ILPOk
|
||||
Just ent -> invitationEntityPlan ent
|
||||
where
|
||||
cReqSchemas :: ConnReqInvitation -> (ConnReqInvitation, ConnReqInvitation)
|
||||
cReqSchemas (CRInvitationUri crData e2e) =
|
||||
( CRInvitationUri crData {crScheme = SSSimplex} e2e,
|
||||
CRInvitationUri crData {crScheme = simplexChat} e2e
|
||||
)
|
||||
connectPlan user (ACR SCMContact (CRContactUri crData)) = do
|
||||
invitationEntityPlan :: ConnectionEntity -> CM ConnectionPlan
|
||||
invitationEntityPlan = \case
|
||||
RcvDirectMsgConnection Connection {connStatus = ConnPrepared} Nothing ->
|
||||
pure $ CPInvitationLink ILPOk
|
||||
RcvDirectMsgConnection conn ct_ -> do
|
||||
let Connection {connStatus, contactConnInitiated} = conn
|
||||
if
|
||||
| connStatus == ConnNew && contactConnInitiated ->
|
||||
pure $ CPInvitationLink ILPOwnLink
|
||||
| not (connReady conn) ->
|
||||
pure $ CPInvitationLink (ILPConnecting ct_)
|
||||
| otherwise -> case ct_ of
|
||||
Just ct -> pure $ CPInvitationLink (ILPKnown ct)
|
||||
Nothing -> throwChatError $ CEInternalError "ready RcvDirectMsgConnection connection should have associated contact"
|
||||
_ -> throwChatError $ CECommandError "found connection entity is not RcvDirectMsgConnection"
|
||||
contactRequestPlan :: User -> ConnReqContact -> CM ConnectionPlan
|
||||
contactRequestPlan user (CRContactUri crData) = do
|
||||
let ConnReqUriData {crClientData} = crData
|
||||
groupLinkId = crClientData >>= decodeJSON >>= \(CRDataGroup gli) -> Just gli
|
||||
cReqHashes = bimap hash hash cReqSchemas
|
||||
case groupLinkId of
|
||||
-- contact address
|
||||
Nothing ->
|
||||
@@ -3181,9 +3232,31 @@ processChatCommand' vr = \case
|
||||
( CRContactUri crData {crScheme = SSSimplex},
|
||||
CRContactUri crData {crScheme = simplexChat}
|
||||
)
|
||||
cReqHashes :: (ConnReqUriHash, ConnReqUriHash)
|
||||
cReqHashes = bimap hash hash cReqSchemas
|
||||
hash :: ConnReqContact -> ConnReqUriHash
|
||||
hash = ConnReqUriHash . C.sha256Hash . strEncode
|
||||
getShortLinkConnReq :: User -> ConnShortLink m -> CM (ConnectionRequestUri m)
|
||||
getShortLinkConnReq User {userId} l = do
|
||||
l' <- restoreShortLink' l
|
||||
(cReq, cData) <- withAgent (\a -> getConnShortLink a userId l')
|
||||
case cData of
|
||||
ContactLinkData {direct} | not direct -> throwChatError CEUnsupportedConnReq
|
||||
_ -> pure ()
|
||||
pure cReq
|
||||
-- This function is needed, as UI uses simplex:/ schema in message view, so that the links can be handled without browser,
|
||||
-- and short links are stored with server hostname schema, so they wouldn't match without it.
|
||||
serverShortLink :: ConnShortLink m -> ConnShortLink m
|
||||
serverShortLink = \case
|
||||
CSLInvitation _ srv lnkId linkKey -> CSLInvitation SLSServer srv lnkId linkKey
|
||||
CSLContact _ ct srv linkKey -> CSLContact SLSServer ct srv linkKey
|
||||
restoreShortLink' l = (`restoreShortLink` l) <$> asks (shortLinkPresetServers . config)
|
||||
shortLinkUserData short = if short then Just "" else Nothing
|
||||
shortenCreatedLink :: CreatedConnLink m -> CM (CreatedConnLink m)
|
||||
shortenCreatedLink (CCLink cReq sLnk) = CCLink cReq <$> mapM (\l -> (`shortenShortLink` l) <$> asks (shortLinkPresetServers . config)) sLnk
|
||||
createdGroupLink :: CreatedLinkContact -> CreatedLinkContact
|
||||
createdGroupLink (CCLink cReq shortLink) = CCLink cReq (toGroupLink <$> shortLink)
|
||||
where
|
||||
toGroupLink :: ShortLinkContact -> ShortLinkContact
|
||||
toGroupLink (CSLContact sch _ srv k) = CSLContact sch CCTGroup srv k
|
||||
updateCIGroupInvitationStatus :: User -> GroupInfo -> CIGroupInvitationStatus -> CM ()
|
||||
updateCIGroupInvitationStatus user GroupInfo {groupId} newStatus = do
|
||||
AChatItem _ _ cInfo ChatItem {content, meta = CIMeta {itemId}} <- withFastStore $ \db -> getChatItemByGroupId db vr user groupId
|
||||
@@ -3649,7 +3722,7 @@ subscribeUserConnections vr onlyNeeded agentBatchSubscribe user = do
|
||||
viaUserContactLink,
|
||||
groupLinkId,
|
||||
customUserProfileId,
|
||||
connReqInv = Nothing,
|
||||
connLinkInv = Nothing,
|
||||
localAlias,
|
||||
createdAt,
|
||||
updatedAt = createdAt
|
||||
@@ -4120,11 +4193,11 @@ chatCommandP =
|
||||
"/set welcome " *> char_ '#' *> (UpdateGroupDescription <$> displayNameP <* A.space <*> (Just <$> msgTextP)),
|
||||
"/delete welcome " *> char_ '#' *> (UpdateGroupDescription <$> displayNameP <*> pure Nothing),
|
||||
"/show welcome " *> char_ '#' *> (ShowGroupDescription <$> displayNameP),
|
||||
"/_create link #" *> (APICreateGroupLink <$> A.decimal <*> (memberRole <|> pure GRMember)),
|
||||
"/_create link #" *> (APICreateGroupLink <$> A.decimal <*> (memberRole <|> pure GRMember) <*> shortOnOffP),
|
||||
"/_set link role #" *> (APIGroupLinkMemberRole <$> A.decimal <*> memberRole),
|
||||
"/_delete link #" *> (APIDeleteGroupLink <$> A.decimal),
|
||||
"/_get link #" *> (APIGetGroupLink <$> A.decimal),
|
||||
"/create link #" *> (CreateGroupLink <$> displayNameP <*> (memberRole <|> pure GRMember)),
|
||||
"/create link #" *> (CreateGroupLink <$> displayNameP <*> (memberRole <|> pure GRMember) <*> shortP),
|
||||
"/set link role #" *> (GroupLinkMemberRole <$> displayNameP <*> memberRole),
|
||||
"/delete link #" *> (DeleteGroupLink <$> displayNameP),
|
||||
"/show link #" *> (ShowGroupLink <$> displayNameP),
|
||||
@@ -4135,12 +4208,12 @@ chatCommandP =
|
||||
"/_contacts " *> (APIListContacts <$> A.decimal),
|
||||
"/contacts" $> ListContacts,
|
||||
"/_connect plan " *> (APIConnectPlan <$> A.decimal <* A.space <*> strP),
|
||||
"/_connect " *> (APIConnect <$> A.decimal <*> incognitoOnOffP <* A.space <*> ((Just <$> strP) <|> A.takeByteString $> Nothing)),
|
||||
"/_connect " *> (APIAddContact <$> A.decimal <*> incognitoOnOffP),
|
||||
"/_connect " *> (APIAddContact <$> A.decimal <*> shortOnOffP <*> incognitoOnOffP),
|
||||
"/_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 <$> shortP <*> incognitoP),
|
||||
("/connect" <|> "/c") *> (Connect <$> incognitoP <* A.space <*> ((Just <$> strP) <|> A.takeTill isSpace $> Nothing)),
|
||||
("/connect" <|> "/c") *> (AddContact <$> incognitoP),
|
||||
ForwardMessage <$> chatNameP <* " <- @" <*> displayNameP <* A.space <*> msgTextP,
|
||||
ForwardGroupMessage <$> chatNameP <* " <- #" <*> displayNameP <* A.space <* A.char '@' <*> (Just <$> displayNameP) <* A.space <*> msgTextP,
|
||||
ForwardGroupMessage <$> chatNameP <* " <- #" <*> displayNameP <*> pure Nothing <* A.space <*> msgTextP,
|
||||
@@ -4174,8 +4247,8 @@ chatCommandP =
|
||||
("/fstatus " <|> "/fs ") *> (FileStatus <$> A.decimal),
|
||||
"/_connect contact " *> (APIConnectContactViaAddress <$> A.decimal <*> incognitoOnOffP <* A.space <*> A.decimal),
|
||||
"/simplex" *> (ConnectSimplex <$> incognitoP),
|
||||
"/_address " *> (APICreateMyAddress <$> A.decimal),
|
||||
("/address" <|> "/ad") $> CreateMyAddress,
|
||||
"/_address " *> (APICreateMyAddress <$> A.decimal <*> shortOnOffP),
|
||||
("/address" <|> "/ad") *> (CreateMyAddress <$> shortP),
|
||||
"/_delete_address " *> (APIDeleteMyAddress <$> A.decimal),
|
||||
("/delete_address" <|> "/da") $> DeleteMyAddress,
|
||||
"/_show_address " *> (APIShowMyAddress <$> A.decimal),
|
||||
@@ -4246,7 +4319,12 @@ chatCommandP =
|
||||
]
|
||||
where
|
||||
choice = A.choice . map (\p -> p <* A.takeWhile (== ' ') <* A.endOfInput)
|
||||
connLinkP = do
|
||||
((Just <$> strP) <|> A.takeTill (== ' ') $> Nothing)
|
||||
>>= mapM (\(ACR m cReq) -> ACCL m . CCLink cReq <$> optional (A.space *> strP))
|
||||
shortP = (A.space *> ("short" <|> "s")) $> True <|> pure False
|
||||
incognitoP = (A.space *> ("incognito" <|> "i")) $> True <|> pure False
|
||||
shortOnOffP = (A.space *> "short=" *> onOffP) <|> pure False
|
||||
incognitoOnOffP = (A.space *> "incognito=" *> onOffP) <|> pure False
|
||||
imagePrefix = (<>) <$> "data:" <*> ("image/png;base64," <|> "image/jpg;base64,")
|
||||
imageP = safeDecodeUtf8 <$> ((<>) <$> imagePrefix <*> (B64.encode <$> base64P))
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1205,8 +1205,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||
CORGroup gInfo -> toView $ CRBusinessRequestAlreadyAccepted user gInfo
|
||||
CORRequest cReq -> do
|
||||
ucl <- withStore $ \db -> getUserContactLinkById db userId userContactLinkId
|
||||
let (UserContactLink {connReqContact, autoAccept}, gLinkInfo_) = ucl
|
||||
isSimplexTeam = sameConnReqContact connReqContact adminContactReq
|
||||
let (UserContactLink {connLinkContact = CCLink connReq _, autoAccept}, gLinkInfo_) = ucl
|
||||
isSimplexTeam = sameConnReqContact connReq adminContactReq
|
||||
v = maxVersion chatVRange
|
||||
case autoAccept of
|
||||
Just AutoAccept {acceptIncognito, businessAddress}
|
||||
|
||||
@@ -29,11 +29,10 @@ import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
import Data.Text.Encoding (encodeUtf8)
|
||||
import Simplex.Chat.Types
|
||||
import Simplex.Messaging.Agent.Protocol (AConnectionRequestUri (..), ConnReqUriData (..), ConnectionRequestUri (..), SMPQueue (..))
|
||||
import Simplex.Messaging.Agent.Protocol (AConnectionLink (..), ConnReqUriData (..), ConnShortLink (..), ConnectionLink (..), ConnectionRequestUri (..), ContactConnType (..), SMPQueue (..), simplexConnReqUri, simplexShortLink)
|
||||
import Simplex.Messaging.Encoding.String
|
||||
import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, enumJSON, fstToLower, sumTypeJSON)
|
||||
import Simplex.Messaging.Protocol (ProtocolServer (..))
|
||||
import Simplex.Messaging.ServiceScheme (ServiceScheme (..))
|
||||
import Simplex.Messaging.Util (decodeJSON, safeDecodeUtf8)
|
||||
import System.Console.ANSI.Types
|
||||
import qualified Text.Email.Validate as Email
|
||||
@@ -49,7 +48,7 @@ data Format
|
||||
| Secret
|
||||
| Colored {color :: FormatColor}
|
||||
| Uri
|
||||
| SimplexLink {linkType :: SimplexLinkType, simplexUri :: Text, smpHosts :: NonEmpty Text}
|
||||
| SimplexLink {linkType :: SimplexLinkType, simplexUri :: AConnectionLink, smpHosts :: NonEmpty Text}
|
||||
| Mention {memberName :: Text}
|
||||
| Email
|
||||
| Phone
|
||||
@@ -62,7 +61,7 @@ mentionedNames = mapMaybe (\(FormattedText f _) -> mentionedName =<< f)
|
||||
Mention name -> Just name
|
||||
_ -> Nothing
|
||||
|
||||
data SimplexLinkType = XLContact | XLInvitation | XLGroup
|
||||
data SimplexLinkType = XLContact | XLInvitation | XLGroup | XLChannel
|
||||
deriving (Eq, Show)
|
||||
|
||||
colored :: Color -> Format
|
||||
@@ -248,24 +247,34 @@ markdownP = mconcat <$> A.many' fragmentP
|
||||
')' -> False
|
||||
c -> isPunctuation c
|
||||
uriMarkdown s = case strDecode $ encodeUtf8 s of
|
||||
Right cReq -> markdown (simplexUriFormat cReq) s
|
||||
Right cLink -> markdown (simplexUriFormat cLink) s
|
||||
_ -> markdown Uri s
|
||||
isUri s = T.length s >= 10 && any (`T.isPrefixOf` s) ["http://", "https://", "simplex:/"]
|
||||
isEmail s = T.any (== '@') s && Email.isValid (encodeUtf8 s)
|
||||
noFormat = pure . unmarked
|
||||
simplexUriFormat :: AConnectionRequestUri -> Format
|
||||
simplexUriFormat :: AConnectionLink -> Format
|
||||
simplexUriFormat = \case
|
||||
ACR _ (CRContactUri crData) ->
|
||||
let uri = safeDecodeUtf8 . strEncode $ CRContactUri crData {crScheme = SSSimplex}
|
||||
in SimplexLink (linkType' crData) uri $ uriHosts crData
|
||||
ACR _ (CRInvitationUri crData e2e) ->
|
||||
let uri = safeDecodeUtf8 . strEncode $ CRInvitationUri crData {crScheme = SSSimplex} e2e
|
||||
in SimplexLink XLInvitation uri $ uriHosts crData
|
||||
where
|
||||
uriHosts ConnReqUriData {crSmpQueues} = L.map (safeDecodeUtf8 . strEncode) $ sconcat $ L.map (host . qServer) crSmpQueues
|
||||
linkType' ConnReqUriData {crClientData} = case crClientData >>= decodeJSON of
|
||||
Just (CRDataGroup _) -> XLGroup
|
||||
Nothing -> XLContact
|
||||
ACL m (CLFull cReq) -> case cReq of
|
||||
CRContactUri crData -> SimplexLink (linkType' crData) cLink $ uriHosts crData
|
||||
CRInvitationUri crData _ -> SimplexLink XLInvitation cLink $ uriHosts crData
|
||||
where
|
||||
cLink = ACL m $ CLFull $ simplexConnReqUri cReq
|
||||
uriHosts ConnReqUriData {crSmpQueues} = L.map strEncodeText $ sconcat $ L.map (host . qServer) crSmpQueues
|
||||
linkType' ConnReqUriData {crClientData} = case crClientData >>= decodeJSON of
|
||||
Just (CRDataGroup _) -> XLGroup
|
||||
Nothing -> XLContact
|
||||
ACL m (CLShort sLnk) -> case sLnk of
|
||||
CSLContact _ ct srv _ -> SimplexLink (linkType' ct) cLink $ uriHosts srv
|
||||
CSLInvitation _ srv _ _ -> SimplexLink XLInvitation cLink $ uriHosts srv
|
||||
where
|
||||
cLink = ACL m $ CLShort $ simplexShortLink sLnk
|
||||
uriHosts srv = L.map strEncodeText $ host srv
|
||||
linkType' = \case
|
||||
CCTGroup -> XLGroup
|
||||
CCTChannel -> XLChannel
|
||||
CCTContact -> XLContact
|
||||
strEncodeText :: StrEncoding a => a -> Text
|
||||
strEncodeText = safeDecodeUtf8 . strEncode
|
||||
|
||||
markdownText :: FormattedText -> Text
|
||||
markdownText (FormattedText f_ t) = case f_ of
|
||||
|
||||
@@ -275,6 +275,10 @@ data UserServer' s (p :: ProtocolType) = UserServer
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
presetServerAddress :: UserServer' s p -> ProtocolServer p
|
||||
presetServerAddress UserServer {server = ProtoServerWithAuth srv _} = srv
|
||||
{-# INLINE presetServerAddress #-}
|
||||
|
||||
data PresetOperator = PresetOperator
|
||||
{ operator :: Maybe NewServerOperator,
|
||||
smp :: [NewUserServer 'PSMP],
|
||||
@@ -297,6 +301,9 @@ operatorServersToUse p PresetOperator {useSMP, useXFTP} = case p of
|
||||
SPSMP -> useSMP
|
||||
SPXFTP -> useXFTP
|
||||
|
||||
presetServer' :: Bool -> ProtocolServer p -> NewUserServer p
|
||||
presetServer' enabled = presetServer enabled . (`ProtoServerWithAuth` Nothing)
|
||||
|
||||
presetServer :: Bool -> ProtoServerWithAuth p -> NewUserServer p
|
||||
presetServer = newUserServer_ True
|
||||
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE OverloadedLists #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
module Simplex.Chat.Operators.Presets where
|
||||
|
||||
import Data.List.NonEmpty (NonEmpty)
|
||||
import qualified Data.List.NonEmpty as L
|
||||
import Simplex.Chat.Operators
|
||||
import Simplex.Messaging.Agent.Env.SQLite (ServerRoles (..), allRoles)
|
||||
import Simplex.Messaging.Protocol (ProtocolType (..), SMPServer)
|
||||
|
||||
operatorSimpleXChat :: NewServerOperator
|
||||
operatorSimpleXChat =
|
||||
ServerOperator
|
||||
{ operatorId = DBNewEntity,
|
||||
operatorTag = Just OTSimplex,
|
||||
tradeName = "SimpleX Chat",
|
||||
legalName = Just "SimpleX Chat Ltd",
|
||||
serverDomains = ["simplex.im"],
|
||||
conditionsAcceptance = CARequired Nothing,
|
||||
enabled = True,
|
||||
smpRoles = allRoles,
|
||||
xftpRoles = allRoles
|
||||
}
|
||||
|
||||
operatorFlux :: NewServerOperator
|
||||
operatorFlux =
|
||||
ServerOperator
|
||||
{ operatorId = DBNewEntity,
|
||||
operatorTag = Just OTFlux,
|
||||
tradeName = "Flux",
|
||||
legalName = Just "InFlux Technologies Limited",
|
||||
serverDomains = ["simplexonflux.com"],
|
||||
conditionsAcceptance = CARequired Nothing,
|
||||
enabled = False,
|
||||
smpRoles = ServerRoles {storage = False, proxy = True},
|
||||
xftpRoles = ServerRoles {storage = False, proxy = True}
|
||||
}
|
||||
|
||||
-- Please note: if any servers are removed from the lists below, they MUST be added here.
|
||||
-- Otherwise previously created short links won't work.
|
||||
--
|
||||
-- !!! Also, if any servers need to be added, shortLinkPresetServers will need to be be split to two,
|
||||
-- so that option used for restoring links is updated earlier, for backward/forward compatibility.
|
||||
allPresetServers :: NonEmpty SMPServer
|
||||
allPresetServers = enabledSimplexChatSMPServers <> disabledSimplexChatSMPServers <> fluxSMPServers_
|
||||
-- TODO [short links] remove, added for testing
|
||||
<> ["smp://8Af90NX2TTkKEJAF1RCg69P_Odg2Z-6_J6DOKUqK3rQ=@smp7.simplex.im,dbxqutskmmbkbrs7ofi7pmopeyhgi5cxbjbh4ummgmep4r6bz4cbrcid.onion"]
|
||||
|
||||
simplexChatSMPServers :: [NewUserServer 'PSMP]
|
||||
simplexChatSMPServers =
|
||||
map (presetServer' True) (L.toList enabledSimplexChatSMPServers)
|
||||
<> map (presetServer' False) (L.toList disabledSimplexChatSMPServers)
|
||||
|
||||
-- Please note: if any servers are removed from this list, they MUST be added to allPresetServers.
|
||||
-- Otherwise previously created short links won't work.
|
||||
--
|
||||
-- !!! Also, if any servers need to be added, shortLinkPresetServers will need to be be split to two,
|
||||
-- so that option used for restoring links is updated earlier, for backward/forward compatibility.
|
||||
enabledSimplexChatSMPServers :: NonEmpty SMPServer
|
||||
enabledSimplexChatSMPServers =
|
||||
[ "smp://0YuTwO05YJWS8rkjn9eLJDjQhFKvIYd8d4xG8X1blIU=@smp8.simplex.im,beccx4yfxxbvyhqypaavemqurytl6hozr47wfc7uuecacjqdvwpw2xid.onion",
|
||||
"smp://SkIkI6EPd2D63F4xFKfHk7I1UGZVNn6k1QWZ5rcyr6w=@smp9.simplex.im,jssqzccmrcws6bhmn77vgmhfjmhwlyr3u7puw4erkyoosywgl67slqqd.onion",
|
||||
"smp://6iIcWT_dF2zN_w5xzZEY7HI2Prbh3ldP07YTyDexPjE=@smp10.simplex.im,rb2pbttocvnbrngnwziclp2f4ckjq65kebafws6g4hy22cdaiv5dwjqd.onion",
|
||||
"smp://1OwYGt-yqOfe2IyVHhxz3ohqo3aCCMjtB-8wn4X_aoY=@smp11.simplex.im,6ioorbm6i3yxmuoezrhjk6f6qgkc4syabh7m3so74xunb5nzr4pwgfqd.onion",
|
||||
"smp://UkMFNAXLXeAAe0beCa4w6X_zp18PwxSaSjY17BKUGXQ=@smp12.simplex.im,ie42b5weq7zdkghocs3mgxdjeuycheeqqmksntj57rmejagmg4eor5yd.onion",
|
||||
"smp://enEkec4hlR3UtKx2NMpOUK_K4ZuDxjWBO1d9Y4YXVaA=@smp14.simplex.im,aspkyu2sopsnizbyfabtsicikr2s4r3ti35jogbcekhm3fsoeyjvgrid.onion",
|
||||
"smp://h--vW7ZSkXPeOUpfxlFGgauQmXNFOzGoizak7Ult7cw=@smp15.simplex.im,oauu4bgijybyhczbnxtlggo6hiubahmeutaqineuyy23aojpih3dajad.onion",
|
||||
"smp://hejn2gVIqNU6xjtGM3OwQeuk8ZEbDXVJXAlnSBJBWUA=@smp16.simplex.im,p3ktngodzi6qrf7w64mmde3syuzrv57y55hxabqcq3l5p6oi7yzze6qd.onion",
|
||||
"smp://ZKe4uxF4Z_aLJJOEsC-Y6hSkXgQS5-oc442JQGkyP8M=@smp17.simplex.im,ogtwfxyi3h2h5weftjjpjmxclhb5ugufa5rcyrmg7j4xlch7qsr5nuqd.onion",
|
||||
"smp://PtsqghzQKU83kYTlQ1VKg996dW4Cw4x_bvpKmiv8uns=@smp18.simplex.im,lyqpnwbs2zqfr45jqkncwpywpbtq7jrhxnib5qddtr6npjyezuwd3nqd.onion",
|
||||
"smp://N_McQS3F9TGoh4ER0QstUf55kGnNSd-wXfNPZ7HukcM=@smp19.simplex.im,i53bbtoqhlc365k6kxzwdp5w3cdt433s7bwh3y32rcbml2vztiyyz5id.onion"
|
||||
]
|
||||
|
||||
-- Please note: if any servers are removed from this list, they MUST be added to allPresetServers.
|
||||
-- Otherwise previously created short links won't work.
|
||||
--
|
||||
-- !!! Also, if any servers need to be added, shortLinkPresetServers will need to be be split to two,
|
||||
-- so that option used for restoring links is updated earlier, for backward/forward compatibility.
|
||||
disabledSimplexChatSMPServers :: NonEmpty SMPServer
|
||||
disabledSimplexChatSMPServers =
|
||||
[ "smp://u2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU=@smp4.simplex.im,o5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion",
|
||||
"smp://hpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg=@smp5.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion",
|
||||
"smp://PQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo=@smp6.simplex.im,bylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion"
|
||||
]
|
||||
|
||||
fluxSMPServers :: [NewUserServer 'PSMP]
|
||||
fluxSMPServers = map (presetServer' True) $ L.toList fluxSMPServers_
|
||||
|
||||
-- Please note: if any servers are removed from this list, they MUST be added to allPresetServers.
|
||||
-- Otherwise previously created short links won't work.
|
||||
--
|
||||
-- !!! Also, if any servers need to be added, shortLinkPresetServers will need to be be split to two,
|
||||
-- so that option used for restoring links is updated earlier, for backward/forward compatibility.
|
||||
fluxSMPServers_ :: NonEmpty SMPServer
|
||||
fluxSMPServers_ =
|
||||
[ "smp://xQW_ufMkGE20UrTlBl8QqceG1tbuylXhr9VOLPyRJmw=@smp1.simplexonflux.com,qb4yoanyl4p7o33yrknv4rs6qo7ugeb2tu2zo66sbebezs4cpyosarid.onion",
|
||||
"smp://LDnWZVlAUInmjmdpQQoIo6FUinRXGe0q3zi5okXDE4s=@smp2.simplexonflux.com,yiqtuh3q4x7hgovkomafsod52wvfjucdljqbbipg5sdssnklgongxbqd.onion",
|
||||
"smp://1jne379u7IDJSxAvXbWb_JgoE7iabcslX0LBF22Rej0=@smp3.simplexonflux.com,a5lm4k7ufei66cdck6fy63r4lmkqy3dekmmb7jkfdm5ivi6kfaojshad.onion",
|
||||
"smp://xmAmqj75I9mWrUihLUlI0ZuNLXlIwFIlHRq5Pb6cHAU=@smp4.simplexonflux.com,qpcz2axyy66u26hfdd2e23uohcf3y6c36mn7dcuilcgnwjasnrvnxjqd.onion",
|
||||
"smp://rWvBYyTamuRCBYb_KAn-nsejg879ndhiTg5Sq3k0xWA=@smp5.simplexonflux.com,4ao347qwiuluyd45xunmii4skjigzuuox53hpdsgbwxqafd4yrticead.onion",
|
||||
"smp://PN7-uqLBToqlf1NxHEaiL35lV2vBpXq8Nj8BW11bU48=@smp6.simplexonflux.com,hury6ot3ymebbr2535mlp7gcxzrjpc6oujhtfxcfh2m4fal4xw5fq6qd.onion"
|
||||
]
|
||||
|
||||
fluxXFTPServers :: [NewUserServer 'PXFTP]
|
||||
fluxXFTPServers =
|
||||
map
|
||||
(presetServer True)
|
||||
[ "xftp://92Sctlc09vHl_nAqF2min88zKyjdYJ9mgxRCJns5K2U=@xftp1.simplexonflux.com,apl3pumq3emwqtrztykyyoomdx4dg6ysql5zek2bi3rgznz7ai3odkid.onion",
|
||||
"xftp://YBXy4f5zU1CEhnbbCzVWTNVNsaETcAGmYqGNxHntiE8=@xftp2.simplexonflux.com,c5jjecisncnngysah3cz2mppediutfelco4asx65mi75d44njvua3xid.onion",
|
||||
"xftp://ARQO74ZSvv2OrulRF3CdgwPz_AMy27r0phtLSq5b664=@xftp3.simplexonflux.com,dc4mohiubvbnsdfqqn7xhlhpqs5u4tjzp7xpz6v6corwvzvqjtaqqiqd.onion",
|
||||
"xftp://ub2jmAa9U0uQCy90O-fSUNaYCj6sdhl49Jh3VpNXP58=@xftp4.simplexonflux.com,4qq5pzier3i4yhpuhcrhfbl6j25udc4czoyascrj4yswhodhfwev3nyd.onion",
|
||||
"xftp://Rh19D5e4Eez37DEE9hAlXDB3gZa1BdFYJTPgJWPO9OI=@xftp5.simplexonflux.com,q7itltdn32hjmgcqwhow4tay5ijetng3ur32bolssw32fvc5jrwvozad.onion",
|
||||
"xftp://0AznwoyfX8Od9T_acp1QeeKtxUi676IBIiQjXVwbdyU=@xftp6.simplexonflux.com,upvzf23ou6nrmaf3qgnhd6cn3d74tvivlmz3p7wdfwq6fhthjrjiiqid.onion"
|
||||
]
|
||||
@@ -7,11 +7,14 @@ module Simplex.Chat.Options.Postgres where
|
||||
import qualified Data.ByteString.Char8 as B
|
||||
import Foreign.C.String
|
||||
import Options.Applicative
|
||||
import Numeric.Natural (Natural)
|
||||
import Simplex.Messaging.Agent.Store.Interface (DBOpts (..))
|
||||
|
||||
data ChatDbOpts = ChatDbOpts
|
||||
{ dbConnstr :: String,
|
||||
dbSchemaPrefix :: String
|
||||
dbSchemaPrefix :: String,
|
||||
dbPoolSize :: Natural,
|
||||
dbCreateSchema :: Bool
|
||||
}
|
||||
|
||||
chatDbOptsP :: FilePath -> String -> Parser ChatDbOpts
|
||||
@@ -33,16 +36,32 @@ chatDbOptsP _appDir defaultDbName = do
|
||||
<> value "simplex_v1"
|
||||
<> showDefault
|
||||
)
|
||||
pure ChatDbOpts {dbConnstr, dbSchemaPrefix}
|
||||
dbPoolSize <-
|
||||
option
|
||||
auto
|
||||
( long "pool-size"
|
||||
<> metavar "DB_POOL_SIZE"
|
||||
<> help "Database connection pool size"
|
||||
<> value 1
|
||||
<> showDefault
|
||||
)
|
||||
dbCreateSchema <-
|
||||
switch
|
||||
( long "create-schema"
|
||||
<> help "Create database schema when it does not exist"
|
||||
)
|
||||
pure ChatDbOpts {dbConnstr, dbSchemaPrefix, dbPoolSize, dbCreateSchema}
|
||||
|
||||
dbString :: ChatDbOpts -> String
|
||||
dbString ChatDbOpts {dbConnstr} = dbConnstr
|
||||
|
||||
toDBOpts :: ChatDbOpts -> String -> Bool -> DBOpts
|
||||
toDBOpts ChatDbOpts {dbConnstr, dbSchemaPrefix} dbSuffix _keepKey =
|
||||
toDBOpts ChatDbOpts {dbConnstr, dbSchemaPrefix, dbPoolSize, dbCreateSchema} dbSuffix _keepKey =
|
||||
DBOpts
|
||||
{ connstr = B.pack dbConnstr,
|
||||
schema = if null dbSchemaPrefix then "simplex_v1" <> dbSuffix else dbSchemaPrefix <> dbSuffix
|
||||
schema = B.pack $ if null dbSchemaPrefix then "simplex_v1" <> dbSuffix else dbSchemaPrefix <> dbSuffix,
|
||||
poolSize = dbPoolSize,
|
||||
createSchema = dbCreateSchema
|
||||
}
|
||||
|
||||
chatSuffix :: String
|
||||
@@ -58,11 +77,13 @@ mobileDbOpts schemaPrefix connstr = do
|
||||
pure $
|
||||
ChatDbOpts
|
||||
{ dbConnstr,
|
||||
dbSchemaPrefix
|
||||
dbSchemaPrefix,
|
||||
dbPoolSize = 1,
|
||||
dbCreateSchema = True
|
||||
}
|
||||
|
||||
removeDbKey :: ChatDbOpts -> ChatDbOpts
|
||||
removeDbKey = id
|
||||
|
||||
errorDbStr :: DBOpts -> String
|
||||
errorDbStr DBOpts {schema} = schema
|
||||
errorDbStr DBOpts {schema} = B.unpack schema
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
@@ -12,6 +13,7 @@ module Simplex.Chat.Store.Connections
|
||||
( getChatLockEntity,
|
||||
getConnectionEntity,
|
||||
getConnectionEntityByConnReq,
|
||||
getConnectionEntityViaShortLink,
|
||||
getContactConnEntityByConnReqHash,
|
||||
getConnectionsToSubscribe,
|
||||
unsetConnectionToSubscribe,
|
||||
@@ -33,7 +35,7 @@ import Simplex.Chat.Store.Groups
|
||||
import Simplex.Chat.Store.Profiles
|
||||
import Simplex.Chat.Store.Shared
|
||||
import Simplex.Chat.Types
|
||||
import Simplex.Messaging.Agent.Protocol (ConnId)
|
||||
import Simplex.Messaging.Agent.Protocol (ConnId, ConnShortLink, ConnectionMode (..))
|
||||
import Simplex.Messaging.Agent.Store.AgentStore (firstRow, firstRow', maybeFirstRow)
|
||||
import Simplex.Messaging.Agent.Store.DB (BoolInt (..))
|
||||
import qualified Simplex.Messaging.Agent.Store.DB as DB
|
||||
@@ -206,6 +208,26 @@ getConnectionEntityByConnReq db vr user@User {userId} (cReqSchema1, cReqSchema2)
|
||||
DB.query db "SELECT agent_conn_id FROM connections WHERE user_id = ? AND conn_req_inv IN (?,?) LIMIT 1" (userId, cReqSchema1, cReqSchema2)
|
||||
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getConnectionEntity db vr user) connId_
|
||||
|
||||
getConnectionEntityViaShortLink :: DB.Connection -> VersionRangeChat -> User -> ConnShortLink 'CMInvitation -> IO (Maybe (ConnReqInvitation, ConnectionEntity))
|
||||
getConnectionEntityViaShortLink db vr user@User {userId} shortLink = fmap eitherToMaybe $ runExceptT $ do
|
||||
(cReq, connId) <- ExceptT getConnReqConnId
|
||||
(cReq,) <$> getConnectionEntity db vr user connId
|
||||
where
|
||||
getConnReqConnId =
|
||||
firstRow' toConnReqConnId (SEInternalError "connection not found") $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT conn_req_inv, agent_conn_id
|
||||
FROM connections
|
||||
WHERE user_id = ? AND short_link_inv = ? LIMIT 1
|
||||
|]
|
||||
(userId, shortLink)
|
||||
-- cReq is Maybe - it is removed when connection is established
|
||||
toConnReqConnId = \case
|
||||
(Just cReq, connId) -> Right (cReq, connId)
|
||||
_ -> Left $ SEInternalError "no connection request"
|
||||
|
||||
-- search connection for connection plan:
|
||||
-- multiple connections can have same via_contact_uri_hash if request was repeated;
|
||||
-- this function searches for latest connection with contact so that "known contact" plan would be chosen;
|
||||
|
||||
@@ -100,7 +100,7 @@ import Simplex.Chat.Store.Shared
|
||||
import Simplex.Chat.Types
|
||||
import Simplex.Chat.Types.Preferences
|
||||
import Simplex.Chat.Types.UITheme
|
||||
import Simplex.Messaging.Agent.Protocol (ConnId, InvitationId, UserId)
|
||||
import Simplex.Messaging.Agent.Protocol (ConnId, CreatedConnLink (..), InvitationId, UserId)
|
||||
import Simplex.Messaging.Agent.Store.AgentStore (firstRow, maybeFirstRow)
|
||||
import Simplex.Messaging.Agent.Store.DB (Binary (..), BoolInt (..))
|
||||
import qualified Simplex.Messaging.Agent.Store.DB as DB
|
||||
@@ -122,7 +122,7 @@ getPendingContactConnection db userId connId = do
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT connection_id, agent_conn_id, conn_status, via_contact_uri_hash, via_user_contact_link, group_link_id, custom_user_profile_id, conn_req_inv, local_alias, created_at, updated_at
|
||||
SELECT connection_id, agent_conn_id, conn_status, via_contact_uri_hash, via_user_contact_link, group_link_id, custom_user_profile_id, conn_req_inv, short_link_inv, local_alias, created_at, updated_at
|
||||
FROM connections
|
||||
WHERE user_id = ?
|
||||
AND connection_id = ?
|
||||
@@ -148,14 +148,14 @@ deletePendingContactConnection db userId connId =
|
||||
|]
|
||||
(userId, connId, ConnContact)
|
||||
|
||||
createAddressContactConnection :: DB.Connection -> VersionRangeChat -> User -> Contact -> ConnId -> ConnReqUriHash -> XContactId -> Maybe Profile -> SubscriptionMode -> VersionChat -> PQSupport -> ExceptT StoreError IO (Int64, Contact)
|
||||
createAddressContactConnection db vr user@User {userId} Contact {contactId} acId cReqHash xContactId incognitoProfile subMode chatV pqSup = do
|
||||
PendingContactConnection {pccConnId} <- liftIO $ createConnReqConnection db userId acId cReqHash xContactId incognitoProfile Nothing subMode chatV pqSup
|
||||
createAddressContactConnection :: DB.Connection -> VersionRangeChat -> User -> Contact -> ConnId -> ConnReqUriHash -> Maybe ShortLinkContact -> XContactId -> Maybe Profile -> SubscriptionMode -> VersionChat -> PQSupport -> ExceptT StoreError IO (Int64, Contact)
|
||||
createAddressContactConnection db vr user@User {userId} Contact {contactId} acId cReqHash sLnk xContactId incognitoProfile subMode chatV pqSup = do
|
||||
PendingContactConnection {pccConnId} <- liftIO $ createConnReqConnection db userId acId cReqHash sLnk xContactId incognitoProfile Nothing subMode chatV pqSup
|
||||
liftIO $ DB.execute db "UPDATE connections SET contact_id = ? WHERE connection_id = ?" (contactId, pccConnId)
|
||||
(pccConnId,) <$> getContact db vr user contactId
|
||||
|
||||
createConnReqConnection :: DB.Connection -> UserId -> ConnId -> ConnReqUriHash -> XContactId -> Maybe Profile -> Maybe GroupLinkId -> SubscriptionMode -> VersionChat -> PQSupport -> IO PendingContactConnection
|
||||
createConnReqConnection db userId acId cReqHash xContactId incognitoProfile groupLinkId subMode chatV pqSup = do
|
||||
createConnReqConnection :: DB.Connection -> UserId -> ConnId -> ConnReqUriHash -> Maybe ShortLinkContact -> XContactId -> Maybe Profile -> Maybe GroupLinkId -> SubscriptionMode -> VersionChat -> PQSupport -> IO PendingContactConnection
|
||||
createConnReqConnection db userId acId cReqHash sLnk xContactId incognitoProfile groupLinkId subMode chatV pqSup = do
|
||||
createdAt <- getCurrentTime
|
||||
customUserProfileId <- mapM (createIncognitoProfile_ db userId createdAt) incognitoProfile
|
||||
let pccConnStatus = ConnJoined
|
||||
@@ -164,16 +164,16 @@ createConnReqConnection db userId acId cReqHash xContactId incognitoProfile grou
|
||||
[sql|
|
||||
INSERT INTO connections (
|
||||
user_id, agent_conn_id, conn_status, conn_type, contact_conn_initiated,
|
||||
via_contact_uri_hash, xcontact_id, custom_user_profile_id, via_group_link, group_link_id,
|
||||
via_contact_uri_hash, via_short_link_contact, xcontact_id, custom_user_profile_id, via_group_link, group_link_id,
|
||||
created_at, updated_at, to_subscribe, conn_chat_version, pq_support, pq_encryption
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
( (userId, acId, pccConnStatus, ConnContact, BI True, cReqHash, xContactId)
|
||||
( (userId, acId, pccConnStatus, ConnContact, BI True, cReqHash, sLnk, xContactId)
|
||||
:. (customUserProfileId, BI (isJust groupLinkId), groupLinkId)
|
||||
:. (createdAt, createdAt, BI (subMode == SMOnlyCreate), chatV, pqSup, pqSup)
|
||||
)
|
||||
pccConnId <- insertedRowId db
|
||||
pure PendingContactConnection {pccConnId, pccAgentConnId = AgentConnId acId, pccConnStatus, viaContactUri = True, viaUserContactLink = Nothing, groupLinkId, customUserProfileId, connReqInv = Nothing, localAlias = "", createdAt, updatedAt = createdAt}
|
||||
pure PendingContactConnection {pccConnId, pccAgentConnId = AgentConnId acId, pccConnStatus, viaContactUri = True, viaUserContactLink = Nothing, groupLinkId, customUserProfileId, connLinkInv = Nothing, localAlias = "", createdAt, updatedAt = createdAt}
|
||||
|
||||
getConnReqContactXContactId :: DB.Connection -> VersionRangeChat -> User -> ConnReqUriHash -> IO (Maybe Contact, Maybe XContactId)
|
||||
getConnReqContactXContactId db vr user@User {userId} cReqHash = do
|
||||
@@ -214,8 +214,8 @@ getContactByConnReqHash db vr user@User {userId} cReqHash = do
|
||||
(userId, cReqHash, CSActive)
|
||||
mapM (addDirectChatTags db) ct_
|
||||
|
||||
createDirectConnection :: DB.Connection -> User -> ConnId -> ConnReqInvitation -> ConnStatus -> Maybe Profile -> SubscriptionMode -> VersionChat -> PQSupport -> IO PendingContactConnection
|
||||
createDirectConnection db User {userId} acId cReq pccConnStatus incognitoProfile subMode chatV pqSup = do
|
||||
createDirectConnection :: DB.Connection -> User -> ConnId -> CreatedLinkInvitation -> ConnStatus -> Maybe Profile -> SubscriptionMode -> VersionChat -> PQSupport -> IO PendingContactConnection
|
||||
createDirectConnection db User {userId} acId ccLink@(CCLink cReq shortLinkInv) pccConnStatus incognitoProfile subMode chatV pqSup = do
|
||||
createdAt <- getCurrentTime
|
||||
customUserProfileId <- mapM (createIncognitoProfile_ db userId createdAt) incognitoProfile
|
||||
let contactConnInitiated = pccConnStatus == ConnNew
|
||||
@@ -223,15 +223,15 @@ createDirectConnection db User {userId} acId cReq pccConnStatus incognitoProfile
|
||||
db
|
||||
[sql|
|
||||
INSERT INTO connections
|
||||
(user_id, agent_conn_id, conn_req_inv, conn_status, conn_type, contact_conn_initiated, custom_user_profile_id,
|
||||
(user_id, agent_conn_id, conn_req_inv, short_link_inv, conn_status, conn_type, contact_conn_initiated, custom_user_profile_id,
|
||||
created_at, updated_at, to_subscribe, conn_chat_version, pq_support, pq_encryption)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
( (userId, acId, cReq, pccConnStatus, ConnContact, BI contactConnInitiated, customUserProfileId)
|
||||
( (userId, acId, cReq, shortLinkInv, pccConnStatus, ConnContact, BI contactConnInitiated, customUserProfileId)
|
||||
:. (createdAt, createdAt, BI (subMode == SMOnlyCreate), chatV, pqSup, pqSup)
|
||||
)
|
||||
pccConnId <- insertedRowId db
|
||||
pure PendingContactConnection {pccConnId, pccAgentConnId = AgentConnId acId, pccConnStatus, viaContactUri = False, viaUserContactLink = Nothing, groupLinkId = Nothing, customUserProfileId, connReqInv = Just cReq, localAlias = "", createdAt, updatedAt = createdAt}
|
||||
pure PendingContactConnection {pccConnId, pccAgentConnId = AgentConnId acId, pccConnStatus, viaContactUri = False, viaUserContactLink = Nothing, groupLinkId = Nothing, customUserProfileId, connLinkInv = Just ccLink, localAlias = "", createdAt, updatedAt = createdAt}
|
||||
|
||||
createIncognitoProfile :: DB.Connection -> User -> Profile -> IO Int64
|
||||
createIncognitoProfile db User {userId} p = do
|
||||
@@ -904,7 +904,7 @@ getPendingContactConnections db User {userId} = do
|
||||
<$> DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT connection_id, agent_conn_id, conn_status, via_contact_uri_hash, via_user_contact_link, group_link_id, custom_user_profile_id, conn_req_inv, local_alias, created_at, updated_at
|
||||
SELECT connection_id, agent_conn_id, conn_status, via_contact_uri_hash, via_user_contact_link, group_link_id, custom_user_profile_id, conn_req_inv, short_link_inv, local_alias, created_at, updated_at
|
||||
FROM connections
|
||||
WHERE user_id = ?
|
||||
AND conn_type = ?
|
||||
@@ -989,7 +989,7 @@ updateConnectionStatus_ :: DB.Connection -> Int64 -> ConnStatus -> IO ()
|
||||
updateConnectionStatus_ db connId connStatus = do
|
||||
currentTs <- getCurrentTime
|
||||
if connStatus == ConnReady
|
||||
then DB.execute db "UPDATE connections SET conn_status = ?, updated_at = ?, conn_req_inv = NULL WHERE connection_id = ?" (connStatus, currentTs, connId)
|
||||
then DB.execute db "UPDATE connections SET conn_status = ?, updated_at = ?, conn_req_inv = NULL, short_link_inv = NULL WHERE connection_id = ?" (connStatus, currentTs, connId)
|
||||
else DB.execute db "UPDATE connections SET conn_status = ?, updated_at = ? WHERE connection_id = ?" (connStatus, currentTs, connId)
|
||||
|
||||
updateContactSettings :: DB.Connection -> User -> Int64 -> ChatSettings -> IO ()
|
||||
|
||||
@@ -39,6 +39,7 @@ module Simplex.Chat.Store.Groups
|
||||
getGroup,
|
||||
getGroupInfo,
|
||||
getGroupInfoByUserContactLinkConnReq,
|
||||
getGroupInfoViaUserShortLink,
|
||||
getGroupInfoByGroupLinkHash,
|
||||
updateGroupProfile,
|
||||
updateGroupPreferences,
|
||||
@@ -158,14 +159,14 @@ import Simplex.Chat.Types
|
||||
import Simplex.Chat.Types.Preferences
|
||||
import Simplex.Chat.Types.Shared
|
||||
import Simplex.Chat.Types.UITheme
|
||||
import Simplex.Messaging.Agent.Protocol (ConnId, UserId)
|
||||
import Simplex.Messaging.Agent.Protocol (ConnId, CreatedConnLink (..), UserId)
|
||||
import Simplex.Messaging.Agent.Store.AgentStore (firstRow, fromOnlyBI, maybeFirstRow)
|
||||
import Simplex.Messaging.Agent.Store.DB (Binary (..), BoolInt (..))
|
||||
import qualified Simplex.Messaging.Agent.Store.DB as DB
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import Simplex.Messaging.Crypto.Ratchet (pattern PQEncOff, pattern PQSupportOff)
|
||||
import Simplex.Messaging.Protocol (SubscriptionMode (..))
|
||||
import Simplex.Messaging.Util (eitherToMaybe, ($>>=), (<$$>))
|
||||
import Simplex.Messaging.Util (eitherToMaybe, firstRow', ($>>=), (<$$>))
|
||||
import Simplex.Messaging.Version
|
||||
import UnliftIO.STM
|
||||
#if defined(dbPostgres)
|
||||
@@ -176,21 +177,21 @@ 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 ImageData, Maybe ConnReqContact, Maybe LocalAlias, Maybe Preferences) :. (Maybe UTCTime, Maybe UTCTime) :. (Maybe UTCTime, Maybe Int64, Maybe Int64, Maybe Int64)
|
||||
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 ImageData, Maybe ConnLinkContact, Maybe LocalAlias, Maybe Preferences) :. (Maybe UTCTime, Maybe UTCTime) :. (Maybe UTCTime, Maybe Int64, Maybe Int64, Maybe Int64)
|
||||
|
||||
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, image, contactLink, Just localAlias, contactPreferences) :. (Just createdAt, Just updatedAt) :. (supportChatTs, Just supportChatUnread, Just supportChatUnanswered, Just supportChatMentions)) =
|
||||
Just $ toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, showMessages, memberBlocked) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId, profileId, displayName, fullName, image, contactLink, localAlias, contactPreferences) :. (createdAt, updatedAt) :. (supportChatTs, supportChatUnread, supportChatUnanswered, supportChatMentions))
|
||||
toMaybeGroupMember _ _ = Nothing
|
||||
|
||||
createGroupLink :: DB.Connection -> User -> GroupInfo -> ConnId -> ConnReqContact -> GroupLinkId -> GroupMemberRole -> SubscriptionMode -> ExceptT StoreError IO ()
|
||||
createGroupLink db User {userId} groupInfo@GroupInfo {groupId, localDisplayName} agentConnId cReq groupLinkId memberRole subMode =
|
||||
createGroupLink :: DB.Connection -> User -> GroupInfo -> ConnId -> CreatedLinkContact -> GroupLinkId -> GroupMemberRole -> SubscriptionMode -> ExceptT StoreError IO ()
|
||||
createGroupLink db User {userId} groupInfo@GroupInfo {groupId, localDisplayName} agentConnId (CCLink cReq shortLink) groupLinkId memberRole subMode =
|
||||
checkConstraint (SEDuplicateGroupLink groupInfo) . liftIO $ do
|
||||
currentTs <- getCurrentTime
|
||||
DB.execute
|
||||
db
|
||||
"INSERT INTO user_contact_links (user_id, group_id, group_link_id, local_display_name, conn_req_contact, group_link_member_role, auto_accept, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)"
|
||||
(userId, groupId, groupLinkId, "group_link_" <> localDisplayName, cReq, memberRole, BI True, currentTs, currentTs)
|
||||
"INSERT INTO user_contact_links (user_id, group_id, group_link_id, local_display_name, conn_req_contact, short_link_contact, group_link_member_role, auto_accept, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?)"
|
||||
(userId, groupId, groupLinkId, "group_link_" <> localDisplayName, cReq, shortLink, memberRole, BI True, currentTs, currentTs)
|
||||
userContactLinkId <- insertedRowId db
|
||||
void $ createConnection_ db userId ConnUserContact (Just userContactLinkId) agentConnId ConnNew initialChatVersion chatInitialVRange Nothing Nothing Nothing 0 currentTs subMode PQSupportOff
|
||||
|
||||
@@ -251,12 +252,12 @@ deleteGroupLink db User {userId} GroupInfo {groupId} = do
|
||||
(userId, groupId)
|
||||
DB.execute db "DELETE FROM user_contact_links WHERE user_id = ? AND group_id = ?" (userId, groupId)
|
||||
|
||||
getGroupLink :: DB.Connection -> User -> GroupInfo -> ExceptT StoreError IO (Int64, ConnReqContact, GroupMemberRole)
|
||||
getGroupLink :: DB.Connection -> User -> GroupInfo -> ExceptT StoreError IO (Int64, CreatedLinkContact, GroupMemberRole)
|
||||
getGroupLink db User {userId} gInfo@GroupInfo {groupId} =
|
||||
ExceptT . firstRow groupLink (SEGroupLinkNotFound gInfo) $
|
||||
DB.query db "SELECT user_contact_link_id, conn_req_contact, group_link_member_role FROM user_contact_links WHERE user_id = ? AND group_id = ? LIMIT 1" (userId, groupId)
|
||||
DB.query db "SELECT user_contact_link_id, conn_req_contact, short_link_contact, group_link_member_role FROM user_contact_links WHERE user_id = ? AND group_id = ? LIMIT 1" (userId, groupId)
|
||||
where
|
||||
groupLink (linkId, cReq, mRole_) = (linkId, cReq, fromMaybe GRMember mRole_)
|
||||
groupLink (linkId, cReq, shortLink, mRole_) = (linkId, CCLink cReq shortLink, fromMaybe GRMember mRole_)
|
||||
|
||||
getGroupLinkId :: DB.Connection -> User -> GroupInfo -> IO (Maybe GroupLinkId)
|
||||
getGroupLinkId db User {userId} GroupInfo {groupId} =
|
||||
@@ -1701,8 +1702,9 @@ getGroupInfo db vr User {userId, userContactId} groupId = ExceptT $ do
|
||||
|
||||
getGroupInfoByUserContactLinkConnReq :: DB.Connection -> VersionRangeChat -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe GroupInfo)
|
||||
getGroupInfoByUserContactLinkConnReq db vr user@User {userId} (cReqSchema1, cReqSchema2) = do
|
||||
-- fmap join is to support group_id = NULL if non-group contact request is sent to this function (e.g., if client data is appended).
|
||||
groupId_ <-
|
||||
maybeFirstRow fromOnly $
|
||||
fmap join . maybeFirstRow fromOnly $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
@@ -1713,6 +1715,26 @@ getGroupInfoByUserContactLinkConnReq db vr user@User {userId} (cReqSchema1, cReq
|
||||
(userId, cReqSchema1, cReqSchema2)
|
||||
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getGroupInfo db vr user) groupId_
|
||||
|
||||
getGroupInfoViaUserShortLink :: DB.Connection -> VersionRangeChat -> User -> ShortLinkContact -> IO (Maybe (ConnReqContact, GroupInfo))
|
||||
getGroupInfoViaUserShortLink db vr user@User {userId} shortLink = fmap eitherToMaybe $ runExceptT $ do
|
||||
(cReq, groupId) <- ExceptT getConnReqGroup
|
||||
(cReq,) <$> getGroupInfo db vr user groupId
|
||||
where
|
||||
getConnReqGroup =
|
||||
firstRow' toConnReqGroupId (SEInternalError "group link not found") $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT conn_req_contact, group_id
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND short_link_contact = ?
|
||||
|]
|
||||
(userId, shortLink)
|
||||
toConnReqGroupId = \case
|
||||
-- cReq is "not null", group_id is nullable
|
||||
(cReq, Just groupId) -> Right (cReq, groupId)
|
||||
_ -> Left $ SEInternalError "no conn req or group ID"
|
||||
|
||||
getGroupInfoByGroupLinkHash :: DB.Connection -> VersionRangeChat -> User -> (ConnReqUriHash, ConnReqUriHash) -> IO (Maybe GroupInfo)
|
||||
getGroupInfoByGroupLinkHash db vr user@User {userId, userContactId} (groupLinkHash1, groupLinkHash2) = do
|
||||
groupId_ <-
|
||||
|
||||
@@ -166,7 +166,7 @@ import Simplex.Chat.Store.NoteFolders
|
||||
import Simplex.Chat.Store.Shared
|
||||
import Simplex.Chat.Types
|
||||
import Simplex.Chat.Types.Shared
|
||||
import Simplex.Messaging.Agent.Protocol (AgentMsgId, ConnId, MsgMeta (..), UserId)
|
||||
import Simplex.Messaging.Agent.Protocol (AgentMsgId, ConnId, ConnShortLink, ConnectionMode (..), MsgMeta (..), UserId)
|
||||
import Simplex.Messaging.Agent.Store.AgentStore (firstRow, firstRow', maybeFirstRow)
|
||||
import Simplex.Messaging.Agent.Store.DB (BoolInt (..))
|
||||
import qualified Simplex.Messaging.Agent.Store.DB as DB
|
||||
@@ -1015,7 +1015,7 @@ getContactConnectionChatPreviews_ db User {userId} pagination clq = case clq of
|
||||
[sql|
|
||||
SELECT
|
||||
connection_id, agent_conn_id, conn_status, via_contact_uri_hash, via_user_contact_link, group_link_id,
|
||||
custom_user_profile_id, conn_req_inv, local_alias, created_at, updated_at
|
||||
custom_user_profile_id, conn_req_inv, short_link_inv, local_alias, created_at, updated_at
|
||||
FROM connections
|
||||
WHERE user_id = ?
|
||||
AND conn_type = ?
|
||||
@@ -1031,7 +1031,7 @@ getContactConnectionChatPreviews_ db User {userId} pagination clq = case clq of
|
||||
PTLast count -> DB.query db (query <> " ORDER BY updated_at DESC LIMIT ?") (params search :. Only count)
|
||||
PTAfter ts count -> DB.query db (query <> " AND updated_at > ? ORDER BY updated_at ASC LIMIT ?") (params search :. (ts, count))
|
||||
PTBefore ts count -> DB.query db (query <> " AND updated_at < ? ORDER BY updated_at DESC LIMIT ?") (params search :. (ts, count))
|
||||
toPreview :: (Int64, ConnId, ConnStatus, Maybe ByteString, Maybe Int64, Maybe GroupLinkId, Maybe Int64, Maybe ConnReqInvitation, LocalAlias, UTCTime, UTCTime) -> AChatPreviewData
|
||||
toPreview :: (Int64, ConnId, ConnStatus, Maybe ByteString, Maybe Int64, Maybe GroupLinkId, Maybe Int64, Maybe ConnReqInvitation, Maybe (ConnShortLink 'CMInvitation), LocalAlias, UTCTime, UTCTime) -> AChatPreviewData
|
||||
toPreview connRow =
|
||||
let conn@PendingContactConnection {updatedAt} = toPendingContactConnection connRow
|
||||
aChat = AChat SCTContactConnection $ Chat (ContactConnection conn) [] emptyChatStats
|
||||
|
||||
@@ -5,11 +5,13 @@ module Simplex.Chat.Store.Postgres.Migrations (migrations) where
|
||||
import Data.List (sortOn)
|
||||
import Data.Text (Text)
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20241220_initial
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20250402_short_links
|
||||
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
|
||||
|
||||
schemaMigrations :: [(String, Text, Maybe Text)]
|
||||
schemaMigrations =
|
||||
[ ("20241220_initial", m20241220_initial, Nothing)
|
||||
[ ("20241220_initial", m20241220_initial, Nothing),
|
||||
("20250402_short_links", m20250402_short_links, Just down_m20250402_short_links)
|
||||
]
|
||||
|
||||
-- | The list of migrations in ascending order by date
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.Postgres.Migrations.M20250402_short_links where
|
||||
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
import Text.RawString.QQ (r)
|
||||
|
||||
m20250402_short_links :: Text
|
||||
m20250402_short_links =
|
||||
T.pack
|
||||
[r|
|
||||
ALTER TABLE user_contact_links ADD COLUMN short_link_contact BYTEA;
|
||||
ALTER TABLE connections ADD COLUMN short_link_inv BYTEA;
|
||||
|]
|
||||
|
||||
down_m20250402_short_links :: Text
|
||||
down_m20250402_short_links =
|
||||
T.pack
|
||||
[r|
|
||||
ALTER TABLE user_contact_links DROP COLUMN short_link_contact;
|
||||
ALTER TABLE connections DROP COLUMN short_link_inv;
|
||||
|]
|
||||
@@ -50,6 +50,7 @@ module Simplex.Chat.Store.Profiles
|
||||
getUserContactLinkById,
|
||||
getGroupLinkInfo,
|
||||
getUserContactLinkByConnReq,
|
||||
getUserContactLinkViaShortLink,
|
||||
getContactWithoutConnViaAddress,
|
||||
updateUserAddressAutoAccept,
|
||||
getProtocolServers,
|
||||
@@ -100,7 +101,7 @@ import Simplex.Chat.Types.Preferences
|
||||
import Simplex.Chat.Types.Shared
|
||||
import Simplex.Chat.Types.UITheme
|
||||
import Simplex.Messaging.Agent.Env.SQLite (ServerRoles (..))
|
||||
import Simplex.Messaging.Agent.Protocol (ACorrId, ConnId, UserId)
|
||||
import Simplex.Messaging.Agent.Protocol (ACorrId, ConnId, ConnectionLink (..), CreatedConnLink (..), UserId)
|
||||
import Simplex.Messaging.Agent.Store.AgentStore (firstRow, maybeFirstRow)
|
||||
import Simplex.Messaging.Agent.Store.DB (BoolInt (..))
|
||||
import qualified Simplex.Messaging.Agent.Store.DB as DB
|
||||
@@ -326,11 +327,13 @@ setUserProfileContactLink db user@User {userId, profile = p@LocalProfile {profil
|
||||
SET contact_link = ?, updated_at = ?
|
||||
WHERE user_id = ? AND contact_profile_id = ?
|
||||
|]
|
||||
(connReqContact_, ts, userId, profileId)
|
||||
pure (user :: User) {profile = p {contactLink = connReqContact_}}
|
||||
(contactLink, ts, userId, profileId)
|
||||
pure (user :: User) {profile = p {contactLink}}
|
||||
where
|
||||
connReqContact_ = case ucl_ of
|
||||
Just UserContactLink {connReqContact} -> Just connReqContact
|
||||
-- TODO [short links] this should be replaced with short links once they are supported by all clients.
|
||||
-- Or, maybe, we want to allow both, when both are optional.
|
||||
contactLink = case ucl_ of
|
||||
Just UserContactLink {connLinkContact = CCLink cReq _} -> Just $ CLFull cReq
|
||||
_ -> Nothing
|
||||
|
||||
-- only used in tests
|
||||
@@ -346,17 +349,17 @@ getUserContactProfiles db User {userId} =
|
||||
|]
|
||||
(Only userId)
|
||||
where
|
||||
toContactProfile :: (ContactName, Text, Maybe ImageData, Maybe ConnReqContact, Maybe Preferences) -> Profile
|
||||
toContactProfile :: (ContactName, Text, Maybe ImageData, Maybe ConnLinkContact, Maybe Preferences) -> Profile
|
||||
toContactProfile (displayName, fullName, image, contactLink, preferences) = Profile {displayName, fullName, image, contactLink, preferences}
|
||||
|
||||
createUserContactLink :: DB.Connection -> User -> ConnId -> ConnReqContact -> SubscriptionMode -> ExceptT StoreError IO ()
|
||||
createUserContactLink db User {userId} agentConnId cReq subMode =
|
||||
createUserContactLink :: DB.Connection -> User -> ConnId -> CreatedLinkContact -> SubscriptionMode -> ExceptT StoreError IO ()
|
||||
createUserContactLink db User {userId} agentConnId (CCLink cReq shortLink) subMode =
|
||||
checkConstraint SEDuplicateContactLink . liftIO $ do
|
||||
currentTs <- getCurrentTime
|
||||
DB.execute
|
||||
db
|
||||
"INSERT INTO user_contact_links (user_id, conn_req_contact, created_at, updated_at) VALUES (?,?,?,?)"
|
||||
(userId, cReq, currentTs, currentTs)
|
||||
"INSERT INTO user_contact_links (user_id, conn_req_contact, short_link_contact, created_at, updated_at) VALUES (?,?,?,?,?)"
|
||||
(userId, cReq, shortLink, currentTs, currentTs)
|
||||
userContactLinkId <- insertedRowId db
|
||||
void $ createConnection_ db userId ConnUserContact (Just userContactLinkId) agentConnId ConnNew initialChatVersion chatInitialVRange Nothing Nothing Nothing 0 currentTs subMode CR.PQSupportOff
|
||||
|
||||
@@ -450,7 +453,7 @@ data UserMsgReceiptSettings = UserMsgReceiptSettings
|
||||
deriving (Show)
|
||||
|
||||
data UserContactLink = UserContactLink
|
||||
{ connReqContact :: ConnReqContact,
|
||||
{ connLinkContact :: CreatedLinkContact,
|
||||
autoAccept :: Maybe AutoAccept
|
||||
}
|
||||
deriving (Show)
|
||||
@@ -472,22 +475,15 @@ $(J.deriveJSON defaultJSON ''AutoAccept)
|
||||
|
||||
$(J.deriveJSON defaultJSON ''UserContactLink)
|
||||
|
||||
toUserContactLink :: (ConnReqContact, BoolInt, BoolInt, BoolInt, Maybe MsgContent) -> UserContactLink
|
||||
toUserContactLink (connReq, BI autoAccept, BI businessAddress, BI acceptIncognito, autoReply) =
|
||||
UserContactLink connReq $
|
||||
toUserContactLink :: (ConnReqContact, Maybe ShortLinkContact, BoolInt, BoolInt, BoolInt, Maybe MsgContent) -> UserContactLink
|
||||
toUserContactLink (connReq, shortLink, BI autoAccept, BI businessAddress, BI acceptIncognito, autoReply) =
|
||||
UserContactLink (CCLink connReq shortLink) $
|
||||
if autoAccept then Just AutoAccept {businessAddress, acceptIncognito, autoReply} else Nothing
|
||||
|
||||
getUserAddress :: DB.Connection -> User -> ExceptT StoreError IO UserContactLink
|
||||
getUserAddress db User {userId} =
|
||||
ExceptT . firstRow toUserContactLink SEUserContactLinkNotFound $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT conn_req_contact, auto_accept, business_address, auto_accept_incognito, auto_reply_msg_content
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND local_display_name = '' AND group_id IS NULL
|
||||
|]
|
||||
(Only userId)
|
||||
DB.query db (userContactLinkQuery <> " WHERE user_id = ? AND local_display_name = '' AND group_id IS NULL") (Only userId)
|
||||
|
||||
getUserContactLinkById :: DB.Connection -> UserId -> Int64 -> ExceptT StoreError IO (UserContactLink, Maybe GroupLinkInfo)
|
||||
getUserContactLinkById db userId userContactLinkId =
|
||||
@@ -495,7 +491,7 @@ getUserContactLinkById db userId userContactLinkId =
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT conn_req_contact, auto_accept, business_address, auto_accept_incognito, auto_reply_msg_content, group_id, group_link_member_role
|
||||
SELECT conn_req_contact, short_link_contact, auto_accept, business_address, auto_accept_incognito, auto_reply_msg_content, group_id, group_link_member_role
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND user_contact_link_id = ?
|
||||
|]
|
||||
@@ -521,14 +517,19 @@ getGroupLinkInfo db userId groupId =
|
||||
getUserContactLinkByConnReq :: DB.Connection -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe UserContactLink)
|
||||
getUserContactLinkByConnReq db User {userId} (cReqSchema1, cReqSchema2) =
|
||||
maybeFirstRow toUserContactLink $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT conn_req_contact, auto_accept, business_address, auto_accept_incognito, auto_reply_msg_content
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND conn_req_contact IN (?,?)
|
||||
|]
|
||||
(userId, cReqSchema1, cReqSchema2)
|
||||
DB.query db (userContactLinkQuery <> " WHERE user_id = ? AND conn_req_contact IN (?,?)") (userId, cReqSchema1, cReqSchema2)
|
||||
|
||||
getUserContactLinkViaShortLink :: DB.Connection -> User -> ShortLinkContact -> IO (Maybe UserContactLink)
|
||||
getUserContactLinkViaShortLink db User {userId} shortLink =
|
||||
maybeFirstRow toUserContactLink $
|
||||
DB.query db (userContactLinkQuery <> " WHERE user_id = ? AND short_link_contact = ?") (userId, shortLink)
|
||||
|
||||
userContactLinkQuery :: Query
|
||||
userContactLinkQuery =
|
||||
[sql|
|
||||
SELECT conn_req_contact, short_link_contact, auto_accept, business_address, auto_accept_incognito, auto_reply_msg_content
|
||||
FROM user_contact_links
|
||||
|]
|
||||
|
||||
getContactWithoutConnViaAddress :: DB.Connection -> VersionRangeChat -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe Contact)
|
||||
getContactWithoutConnViaAddress db vr user@User {userId} (cReqSchema1, cReqSchema2) = do
|
||||
|
||||
@@ -128,7 +128,8 @@ import Simplex.Chat.Store.SQLite.Migrations.M20250122_chat_items_include_in_hist
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20250126_mentions
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20250129_delete_unused_contacts
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20250130_indexes
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20250310_group_scope
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20250402_short_links
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20250403_group_scope
|
||||
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
|
||||
|
||||
schemaMigrations :: [(String, Query, Maybe Query)]
|
||||
@@ -257,7 +258,8 @@ schemaMigrations =
|
||||
("20250126_mentions", m20250126_mentions, Just down_m20250126_mentions),
|
||||
("20250129_delete_unused_contacts", m20250129_delete_unused_contacts, Just down_m20250129_delete_unused_contacts),
|
||||
("20250130_indexes", m20250130_indexes, Just down_m20250130_indexes),
|
||||
("20250310_group_scope", m20250310_group_scope, Just down_m20250310_group_scope)
|
||||
("20250402_short_links", m20250402_short_links, Just down_m20250402_short_links),
|
||||
("20250403_group_scope", m20250403_group_scope, Just down_m20250403_group_scope)
|
||||
]
|
||||
|
||||
-- | The list of migrations in ascending order by date
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20250402_short_links where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20250402_short_links :: Query
|
||||
m20250402_short_links =
|
||||
[sql|
|
||||
ALTER TABLE user_contact_links ADD COLUMN short_link_contact BLOB;
|
||||
ALTER TABLE connections ADD COLUMN short_link_inv BLOB;
|
||||
ALTER TABLE connections ADD COLUMN via_short_link_contact BLOB;
|
||||
|
||||
|]
|
||||
|
||||
down_m20250402_short_links :: Query
|
||||
down_m20250402_short_links =
|
||||
[sql|
|
||||
ALTER TABLE user_contact_links DROP COLUMN short_link_contact;
|
||||
ALTER TABLE connections DROP COLUMN short_link_inv;
|
||||
ALTER TABLE connections DROP COLUMN via_short_link_contact;
|
||||
|]
|
||||
+5
-5
@@ -1,13 +1,13 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20250310_group_scope where
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20250403_group_scope where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
-- TODO [knocking] review indexes (drop idx_chat_items_groups_item_ts?)
|
||||
m20250310_group_scope :: Query
|
||||
m20250310_group_scope =
|
||||
m20250403_group_scope :: Query
|
||||
m20250403_group_scope =
|
||||
[sql|
|
||||
ALTER TABLE group_profiles ADD COLUMN member_admission TEXT;
|
||||
|
||||
@@ -30,8 +30,8 @@ CREATE INDEX idx_chat_items_group_scope_item_ts ON chat_items(
|
||||
);
|
||||
|]
|
||||
|
||||
down_m20250310_group_scope :: Query
|
||||
down_m20250310_group_scope =
|
||||
down_m20250403_group_scope :: Query
|
||||
down_m20250403_group_scope =
|
||||
[sql|
|
||||
DROP INDEX idx_chat_items_group_scope_item_ts;
|
||||
|
||||
@@ -443,6 +443,22 @@ Query:
|
||||
Plan:
|
||||
SEARCH connections USING PRIMARY KEY (conn_id=?)
|
||||
|
||||
Query:
|
||||
SELECT link_id, snd_private_key
|
||||
FROM inv_short_links
|
||||
WHERE host = ? AND port = ? AND snd_id = ?
|
||||
|
||||
Plan:
|
||||
SEARCH inv_short_links USING INDEX idx_inv_short_links_link_id (host=? AND port=?)
|
||||
|
||||
Query:
|
||||
SELECT link_key, snd_private_key, snd_id
|
||||
FROM inv_short_links
|
||||
WHERE host = ? AND port = ? AND link_id = ?
|
||||
|
||||
Plan:
|
||||
SEARCH inv_short_links USING INDEX idx_inv_short_links_link_id (host=? AND port=? AND link_id=?)
|
||||
|
||||
Query:
|
||||
SELECT s.internal_id, m.msg_type, s.internal_hash, s.rcpt_internal_id, s.rcpt_status
|
||||
FROM snd_messages s
|
||||
@@ -466,6 +482,19 @@ Query:
|
||||
|
||||
Plan:
|
||||
|
||||
Query:
|
||||
INSERT INTO inv_short_links
|
||||
(host, port, server_key_hash, link_id, link_key, snd_private_key, snd_id)
|
||||
VALUES (?,?,?,?,?,?,?)
|
||||
ON CONFLICT (host, port, link_id)
|
||||
DO UPDATE SET
|
||||
server_key_hash = EXCLUDED.server_key_hash,
|
||||
link_key = EXCLUDED.link_key,
|
||||
snd_private_key = EXCLUDED.snd_private_key,
|
||||
snd_id = EXCLUDED.snd_id
|
||||
|
||||
Plan:
|
||||
|
||||
Query:
|
||||
INSERT INTO messages
|
||||
(conn_id, internal_id, internal_ts, internal_rcv_id, internal_snd_id, msg_type, msg_flags, msg_body, pq_encryption)
|
||||
@@ -524,7 +553,10 @@ SEARCH messages USING COVERING INDEX idx_messages_conn_id_internal_rcv_id (conn_
|
||||
|
||||
Query:
|
||||
INSERT INTO rcv_queues
|
||||
(host, port, rcv_id, conn_id, rcv_private_key, rcv_dh_secret, e2e_priv_key, e2e_dh_secret, snd_id, snd_secure, status, rcv_queue_id, rcv_primary, replace_rcv_queue_id, smp_client_version, server_key_hash) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);
|
||||
( host, port, rcv_id, conn_id, rcv_private_key, rcv_dh_secret, e2e_priv_key, e2e_dh_secret,
|
||||
snd_id, queue_mode, status, rcv_queue_id, rcv_primary, replace_rcv_queue_id, smp_client_version, server_key_hash,
|
||||
link_id, link_key, link_priv_sig_key, link_enc_fixed_data
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);
|
||||
|
||||
Plan:
|
||||
|
||||
@@ -546,14 +578,14 @@ SEARCH messages USING COVERING INDEX idx_messages_conn_id_internal_snd_id (conn_
|
||||
|
||||
Query:
|
||||
INSERT INTO snd_queues
|
||||
(host, port, snd_id, snd_secure, conn_id, snd_public_key, snd_private_key, e2e_pub_key, e2e_dh_secret,
|
||||
(host, port, snd_id, queue_mode, conn_id, snd_public_key, snd_private_key, e2e_pub_key, e2e_dh_secret,
|
||||
status, snd_queue_id, snd_primary, replace_snd_queue_id, smp_client_version, server_key_hash)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
ON CONFLICT (host, port, snd_id) DO UPDATE SET
|
||||
host=EXCLUDED.host,
|
||||
port=EXCLUDED.port,
|
||||
snd_id=EXCLUDED.snd_id,
|
||||
snd_secure=EXCLUDED.snd_secure,
|
||||
queue_mode=EXCLUDED.queue_mode,
|
||||
conn_id=EXCLUDED.conn_id,
|
||||
snd_public_key=EXCLUDED.snd_public_key,
|
||||
snd_private_key=EXCLUDED.snd_private_key,
|
||||
@@ -631,6 +663,14 @@ Query:
|
||||
Plan:
|
||||
SEARCH connections USING PRIMARY KEY (conn_id=?)
|
||||
|
||||
Query:
|
||||
UPDATE inv_short_links
|
||||
SET snd_id = ?
|
||||
WHERE host = ? AND port = ? AND link_id = ?
|
||||
|
||||
Plan:
|
||||
SEARCH inv_short_links USING INDEX idx_inv_short_links_link_id (host=? AND port=? AND link_id=?)
|
||||
|
||||
Query:
|
||||
UPDATE ratchets
|
||||
SET x3dh_priv_key_1 = ?, x3dh_priv_key_2 = ?, pq_priv_kem = ?
|
||||
@@ -691,7 +731,7 @@ SEARCH snd_queues USING PRIMARY KEY (host=? AND port=? AND snd_id=?)
|
||||
|
||||
Query:
|
||||
SELECT
|
||||
c.user_id, COALESCE(q.server_key_hash, s.key_hash), q.conn_id, q.host, q.port, q.snd_id, q.snd_secure,
|
||||
c.user_id, COALESCE(q.server_key_hash, s.key_hash), q.conn_id, q.host, q.port, q.snd_id, q.queue_mode,
|
||||
q.snd_public_key, q.snd_private_key, q.e2e_pub_key, q.e2e_dh_secret, q.status,
|
||||
q.snd_queue_id, q.snd_primary, q.replace_snd_queue_id, q.switch_status, q.smp_client_version
|
||||
FROM snd_queues q
|
||||
@@ -705,9 +745,10 @@ SEARCH s USING PRIMARY KEY (host=? AND port=?)
|
||||
|
||||
Query:
|
||||
SELECT c.user_id, COALESCE(q.server_key_hash, s.key_hash), q.conn_id, q.host, q.port, q.rcv_id, q.rcv_private_key, q.rcv_dh_secret,
|
||||
q.e2e_priv_key, q.e2e_dh_secret, q.snd_id, q.snd_secure, q.status,
|
||||
q.e2e_priv_key, q.e2e_dh_secret, q.snd_id, q.queue_mode, q.status,
|
||||
q.rcv_queue_id, q.rcv_primary, q.replace_rcv_queue_id, q.switch_status, q.smp_client_version, q.delete_errors,
|
||||
q.ntf_public_key, q.ntf_private_key, q.ntf_id, q.rcv_ntf_dh_secret
|
||||
q.ntf_public_key, q.ntf_private_key, q.ntf_id, q.rcv_ntf_dh_secret,
|
||||
q.link_id, q.link_key, q.link_priv_sig_key, q.link_enc_fixed_data
|
||||
FROM rcv_queues q
|
||||
JOIN servers s ON q.host = s.host AND q.port = s.port
|
||||
JOIN connections c ON q.conn_id = c.conn_id
|
||||
@@ -719,9 +760,10 @@ SEARCH s USING PRIMARY KEY (host=? AND port=?)
|
||||
|
||||
Query:
|
||||
SELECT c.user_id, COALESCE(q.server_key_hash, s.key_hash), q.conn_id, q.host, q.port, q.rcv_id, q.rcv_private_key, q.rcv_dh_secret,
|
||||
q.e2e_priv_key, q.e2e_dh_secret, q.snd_id, q.snd_secure, q.status,
|
||||
q.e2e_priv_key, q.e2e_dh_secret, q.snd_id, q.queue_mode, q.status,
|
||||
q.rcv_queue_id, q.rcv_primary, q.replace_rcv_queue_id, q.switch_status, q.smp_client_version, q.delete_errors,
|
||||
q.ntf_public_key, q.ntf_private_key, q.ntf_id, q.rcv_ntf_dh_secret
|
||||
q.ntf_public_key, q.ntf_private_key, q.ntf_id, q.rcv_ntf_dh_secret,
|
||||
q.link_id, q.link_key, q.link_priv_sig_key, q.link_enc_fixed_data
|
||||
FROM rcv_queues q
|
||||
JOIN servers s ON q.host = s.host AND q.port = s.port
|
||||
JOIN connections c ON q.conn_id = c.conn_id
|
||||
@@ -733,9 +775,10 @@ SEARCH c USING PRIMARY KEY (conn_id=?)
|
||||
|
||||
Query:
|
||||
SELECT c.user_id, COALESCE(q.server_key_hash, s.key_hash), q.conn_id, q.host, q.port, q.rcv_id, q.rcv_private_key, q.rcv_dh_secret,
|
||||
q.e2e_priv_key, q.e2e_dh_secret, q.snd_id, q.snd_secure, q.status,
|
||||
q.e2e_priv_key, q.e2e_dh_secret, q.snd_id, q.queue_mode, q.status,
|
||||
q.rcv_queue_id, q.rcv_primary, q.replace_rcv_queue_id, q.switch_status, q.smp_client_version, q.delete_errors,
|
||||
q.ntf_public_key, q.ntf_private_key, q.ntf_id, q.rcv_ntf_dh_secret
|
||||
q.ntf_public_key, q.ntf_private_key, q.ntf_id, q.rcv_ntf_dh_secret,
|
||||
q.link_id, q.link_key, q.link_priv_sig_key, q.link_enc_fixed_data
|
||||
FROM rcv_queues q
|
||||
JOIN servers s ON q.host = s.host AND q.port = s.port
|
||||
JOIN connections c ON q.conn_id = c.conn_id
|
||||
@@ -747,9 +790,10 @@ SEARCH c USING PRIMARY KEY (conn_id=?)
|
||||
|
||||
Query:
|
||||
SELECT c.user_id, COALESCE(q.server_key_hash, s.key_hash), q.conn_id, q.host, q.port, q.rcv_id, q.rcv_private_key, q.rcv_dh_secret,
|
||||
q.e2e_priv_key, q.e2e_dh_secret, q.snd_id, q.snd_secure, q.status,
|
||||
q.e2e_priv_key, q.e2e_dh_secret, q.snd_id, q.queue_mode, q.status,
|
||||
q.rcv_queue_id, q.rcv_primary, q.replace_rcv_queue_id, q.switch_status, q.smp_client_version, q.delete_errors,
|
||||
q.ntf_public_key, q.ntf_private_key, q.ntf_id, q.rcv_ntf_dh_secret
|
||||
q.ntf_public_key, q.ntf_private_key, q.ntf_id, q.rcv_ntf_dh_secret,
|
||||
q.link_id, q.link_key, q.link_priv_sig_key, q.link_enc_fixed_data
|
||||
FROM rcv_queues q
|
||||
JOIN servers s ON q.host = s.host AND q.port = s.port
|
||||
JOIN connections c ON q.conn_id = c.conn_id
|
||||
@@ -761,9 +805,10 @@ SEARCH s USING PRIMARY KEY (host=? AND port=?)
|
||||
|
||||
Query:
|
||||
SELECT c.user_id, COALESCE(q.server_key_hash, s.key_hash), q.conn_id, q.host, q.port, q.rcv_id, q.rcv_private_key, q.rcv_dh_secret,
|
||||
q.e2e_priv_key, q.e2e_dh_secret, q.snd_id, q.snd_secure, q.status,
|
||||
q.e2e_priv_key, q.e2e_dh_secret, q.snd_id, q.queue_mode, q.status,
|
||||
q.rcv_queue_id, q.rcv_primary, q.replace_rcv_queue_id, q.switch_status, q.smp_client_version, q.delete_errors,
|
||||
q.ntf_public_key, q.ntf_private_key, q.ntf_id, q.rcv_ntf_dh_secret
|
||||
q.ntf_public_key, q.ntf_private_key, q.ntf_id, q.rcv_ntf_dh_secret,
|
||||
q.link_id, q.link_key, q.link_priv_sig_key, q.link_enc_fixed_data
|
||||
FROM rcv_queues q
|
||||
JOIN servers s ON q.host = s.host AND q.port = s.port
|
||||
JOIN connections c ON q.conn_id = c.conn_id
|
||||
@@ -799,6 +844,10 @@ Query: DELETE FROM deleted_snd_chunk_replicas WHERE deleted_snd_chunk_replica_id
|
||||
Plan:
|
||||
SEARCH deleted_snd_chunk_replicas USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query: DELETE FROM inv_short_links WHERE host = ? AND port = ? AND link_id = ?
|
||||
Plan:
|
||||
SEARCH inv_short_links USING INDEX idx_inv_short_links_link_id (host=? AND port=? AND link_id=?)
|
||||
|
||||
Query: DELETE FROM messages WHERE conn_id = ? AND internal_id = ?;
|
||||
Plan:
|
||||
SEARCH messages USING PRIMARY KEY (conn_id=? AND internal_id=?)
|
||||
|
||||
@@ -484,6 +484,14 @@ Plan:
|
||||
SEARCH chat_items USING INDEX idx_chat_items_group_scope_item_ts (user_id=?)
|
||||
USE TEMP B-TREE FOR ORDER BY
|
||||
|
||||
Query:
|
||||
SELECT conn_req_contact, group_id
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND short_link_contact = ?
|
||||
|
||||
Plan:
|
||||
SEARCH user_contact_links USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=?)
|
||||
|
||||
Query:
|
||||
SELECT conn_req_contact, group_id
|
||||
FROM user_contact_links
|
||||
@@ -492,6 +500,14 @@ Query:
|
||||
Plan:
|
||||
SEARCH user_contact_links USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
SELECT conn_req_inv, agent_conn_id
|
||||
FROM connections
|
||||
WHERE user_id = ? AND short_link_inv = ? LIMIT 1
|
||||
|
||||
Plan:
|
||||
SEARCH connections USING INDEX idx_connections_updated_at (user_id=?)
|
||||
|
||||
Query:
|
||||
SELECT connection_id, agent_conn_id, conn_level, via_contact, via_user_contact_link, via_group_link, group_link_id, custom_user_profile_id,
|
||||
conn_status, conn_type, contact_conn_initiated, local_alias, contact_id, group_member_id, snd_file_id, rcv_file_id, user_contact_link_id,
|
||||
@@ -1394,7 +1410,7 @@ SCAN cc
|
||||
Query:
|
||||
SELECT
|
||||
connection_id, agent_conn_id, conn_status, via_contact_uri_hash, via_user_contact_link, group_link_id,
|
||||
custom_user_profile_id, conn_req_inv, local_alias, created_at, updated_at
|
||||
custom_user_profile_id, conn_req_inv, short_link_inv, local_alias, created_at, updated_at
|
||||
FROM connections
|
||||
WHERE user_id = ?
|
||||
AND conn_type = ?
|
||||
@@ -1411,7 +1427,7 @@ SEARCH connections USING INDEX idx_connections_updated_at (user_id=? AND updated
|
||||
Query:
|
||||
SELECT
|
||||
connection_id, agent_conn_id, conn_status, via_contact_uri_hash, via_user_contact_link, group_link_id,
|
||||
custom_user_profile_id, conn_req_inv, local_alias, created_at, updated_at
|
||||
custom_user_profile_id, conn_req_inv, short_link_inv, local_alias, created_at, updated_at
|
||||
FROM connections
|
||||
WHERE user_id = ?
|
||||
AND conn_type = ?
|
||||
@@ -1428,7 +1444,7 @@ SEARCH connections USING INDEX idx_connections_updated_at (user_id=? AND updated
|
||||
Query:
|
||||
SELECT
|
||||
connection_id, agent_conn_id, conn_status, via_contact_uri_hash, via_user_contact_link, group_link_id,
|
||||
custom_user_profile_id, conn_req_inv, local_alias, created_at, updated_at
|
||||
custom_user_profile_id, conn_req_inv, short_link_inv, local_alias, created_at, updated_at
|
||||
FROM connections
|
||||
WHERE user_id = ?
|
||||
AND conn_type = ?
|
||||
@@ -2982,23 +2998,7 @@ Plan:
|
||||
SEARCH commands USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
SELECT conn_req_contact, auto_accept, business_address, auto_accept_incognito, auto_reply_msg_content
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND conn_req_contact IN (?,?)
|
||||
|
||||
Plan:
|
||||
SEARCH user_contact_links USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=?)
|
||||
|
||||
Query:
|
||||
SELECT conn_req_contact, auto_accept, business_address, auto_accept_incognito, auto_reply_msg_content
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND local_display_name = '' AND group_id IS NULL
|
||||
|
||||
Plan:
|
||||
SEARCH user_contact_links USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=? AND local_display_name=?)
|
||||
|
||||
Query:
|
||||
SELECT conn_req_contact, auto_accept, business_address, auto_accept_incognito, auto_reply_msg_content, group_id, group_link_member_role
|
||||
SELECT conn_req_contact, short_link_contact, auto_accept, business_address, auto_accept_incognito, auto_reply_msg_content, group_id, group_link_member_role
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND user_contact_link_id = ?
|
||||
|
||||
@@ -3017,7 +3017,7 @@ Plan:
|
||||
SEARCH connections USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query:
|
||||
SELECT connection_id, agent_conn_id, conn_status, via_contact_uri_hash, via_user_contact_link, group_link_id, custom_user_profile_id, conn_req_inv, local_alias, created_at, updated_at
|
||||
SELECT connection_id, agent_conn_id, conn_status, via_contact_uri_hash, via_user_contact_link, group_link_id, custom_user_profile_id, conn_req_inv, short_link_inv, local_alias, created_at, updated_at
|
||||
FROM connections
|
||||
WHERE user_id = ?
|
||||
AND conn_type = ?
|
||||
@@ -3027,7 +3027,7 @@ Plan:
|
||||
SEARCH connections USING INDEX idx_connections_updated_at (user_id=?)
|
||||
|
||||
Query:
|
||||
SELECT connection_id, agent_conn_id, conn_status, via_contact_uri_hash, via_user_contact_link, group_link_id, custom_user_profile_id, conn_req_inv, local_alias, created_at, updated_at
|
||||
SELECT connection_id, agent_conn_id, conn_status, via_contact_uri_hash, via_user_contact_link, group_link_id, custom_user_profile_id, conn_req_inv, short_link_inv, local_alias, created_at, updated_at
|
||||
FROM connections
|
||||
WHERE user_id = ?
|
||||
AND connection_id = ?
|
||||
@@ -3999,9 +3999,9 @@ Plan:
|
||||
|
||||
Query:
|
||||
INSERT INTO connections
|
||||
(user_id, agent_conn_id, conn_req_inv, conn_status, conn_type, contact_conn_initiated, custom_user_profile_id,
|
||||
(user_id, agent_conn_id, conn_req_inv, short_link_inv, conn_status, conn_type, contact_conn_initiated, custom_user_profile_id,
|
||||
created_at, updated_at, to_subscribe, conn_chat_version, pq_support, pq_encryption)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|
||||
Plan:
|
||||
|
||||
@@ -4017,9 +4017,9 @@ Plan:
|
||||
Query:
|
||||
INSERT INTO connections (
|
||||
user_id, agent_conn_id, conn_status, conn_type, contact_conn_initiated,
|
||||
via_contact_uri_hash, xcontact_id, custom_user_profile_id, via_group_link, group_link_id,
|
||||
via_contact_uri_hash, via_short_link_contact, xcontact_id, custom_user_profile_id, via_group_link, group_link_id,
|
||||
created_at, updated_at, to_subscribe, conn_chat_version, pq_support, pq_encryption
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|
||||
Plan:
|
||||
|
||||
@@ -4713,6 +4713,27 @@ SEARCH c USING INTEGER PRIMARY KEY (rowid=?) LEFT-JOIN
|
||||
CORRELATED SCALAR SUBQUERY 1
|
||||
SEARCH cc USING COVERING INDEX idx_connections_group_member (user_id=? AND group_member_id=?)
|
||||
|
||||
Query:
|
||||
SELECT conn_req_contact, short_link_contact, auto_accept, business_address, auto_accept_incognito, auto_reply_msg_content
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND conn_req_contact IN (?,?)
|
||||
Plan:
|
||||
SEARCH user_contact_links USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=?)
|
||||
|
||||
Query:
|
||||
SELECT conn_req_contact, short_link_contact, auto_accept, business_address, auto_accept_incognito, auto_reply_msg_content
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND local_display_name = '' AND group_id IS NULL
|
||||
Plan:
|
||||
SEARCH user_contact_links USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=? AND local_display_name=?)
|
||||
|
||||
Query:
|
||||
SELECT conn_req_contact, short_link_contact, auto_accept, business_address, auto_accept_incognito, auto_reply_msg_content
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ? AND short_link_contact = ?
|
||||
Plan:
|
||||
SEARCH user_contact_links USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=?)
|
||||
|
||||
Query:
|
||||
SELECT f.file_id, f.ci_file_status, f.file_path
|
||||
FROM chat_items i
|
||||
@@ -5482,10 +5503,10 @@ SEARCH connections USING INTEGER PRIMARY KEY (rowid=?)
|
||||
Query: INSERT INTO temp_conn_ids (conn_id) VALUES (?)
|
||||
Plan:
|
||||
|
||||
Query: INSERT INTO user_contact_links (user_id, conn_req_contact, created_at, updated_at) VALUES (?,?,?,?)
|
||||
Query: INSERT INTO user_contact_links (user_id, conn_req_contact, short_link_contact, created_at, updated_at) VALUES (?,?,?,?,?)
|
||||
Plan:
|
||||
|
||||
Query: INSERT INTO user_contact_links (user_id, group_id, group_link_id, local_display_name, conn_req_contact, group_link_member_role, auto_accept, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)
|
||||
Query: INSERT INTO user_contact_links (user_id, group_id, group_link_id, local_display_name, conn_req_contact, short_link_contact, group_link_member_role, auto_accept, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?)
|
||||
Plan:
|
||||
|
||||
Query: INSERT INTO users (agent_user_id, local_display_name, active_user, active_order, contact_id, show_ntfs, send_rcpts_contacts, send_rcpts_small_groups, created_at, updated_at) VALUES (?,?,?,?,0,?,?,?,?,?)
|
||||
@@ -5714,7 +5735,7 @@ Query: SELECT user_contact_link_id FROM contact_requests WHERE contact_request_i
|
||||
Plan:
|
||||
SEARCH contact_requests USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query: SELECT user_contact_link_id, conn_req_contact, group_link_member_role FROM user_contact_links WHERE user_id = ? AND group_id = ? LIMIT 1
|
||||
Query: SELECT user_contact_link_id, conn_req_contact, short_link_contact, group_link_member_role FROM user_contact_links WHERE user_id = ? AND group_id = ? LIMIT 1
|
||||
Plan:
|
||||
SEARCH user_contact_links USING INDEX idx_user_contact_links_group_id (group_id=?)
|
||||
|
||||
@@ -5762,7 +5783,7 @@ Query: UPDATE connections SET conn_status = ?, updated_at = ? WHERE connection_i
|
||||
Plan:
|
||||
SEARCH connections USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
Query: UPDATE connections SET conn_status = ?, updated_at = ?, conn_req_inv = NULL WHERE connection_id = ?
|
||||
Query: UPDATE connections SET conn_status = ?, updated_at = ?, conn_req_inv = NULL, short_link_inv = NULL WHERE connection_id = ?
|
||||
Plan:
|
||||
SEARCH connections USING INTEGER PRIMARY KEY (rowid=?)
|
||||
|
||||
|
||||
@@ -302,6 +302,8 @@ CREATE TABLE connections(
|
||||
pq_snd_enabled INTEGER,
|
||||
pq_rcv_enabled INTEGER,
|
||||
quota_err_counter INTEGER NOT NULL DEFAULT 0,
|
||||
short_link_inv BLOB,
|
||||
via_short_link_contact BLOB,
|
||||
FOREIGN KEY(snd_file_id, connection_id)
|
||||
REFERENCES snd_files(file_id, connection_id)
|
||||
ON DELETE CASCADE
|
||||
@@ -321,6 +323,7 @@ CREATE TABLE user_contact_links(
|
||||
group_link_id BLOB,
|
||||
group_link_member_role TEXT NULL,
|
||||
business_address INTEGER DEFAULT 0,
|
||||
short_link_contact BLOB,
|
||||
UNIQUE(user_id, local_display_name)
|
||||
);
|
||||
CREATE TABLE contact_requests(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
{-# LANGUAGE DeriveAnyClass #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
@@ -35,7 +36,7 @@ import Simplex.Chat.Types
|
||||
import Simplex.Chat.Types.Preferences
|
||||
import Simplex.Chat.Types.Shared
|
||||
import Simplex.Chat.Types.UITheme
|
||||
import Simplex.Messaging.Agent.Protocol (ConnId, UserId)
|
||||
import Simplex.Messaging.Agent.Protocol (ConnId, ConnShortLink, ConnectionMode (..), CreatedConnLink (..), UserId)
|
||||
import Simplex.Messaging.Agent.Store.AgentStore (firstRow, maybeFirstRow)
|
||||
import Simplex.Messaging.Agent.Store.DB (BoolInt (..))
|
||||
import qualified Simplex.Messaging.Agent.Store.DB as DB
|
||||
@@ -416,7 +417,7 @@ deleteUnusedIncognitoProfileById_ db User {userId} profileId =
|
||||
|]
|
||||
(userId, profileId, userId, profileId, userId, profileId)
|
||||
|
||||
type ContactRow' = (ProfileId, ContactName, Maybe Int64, ContactName, Text, Maybe ImageData, Maybe ConnReqContact, LocalAlias, BoolInt, ContactStatus) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe Preferences, Preferences, UTCTime, UTCTime, Maybe UTCTime) :. (Maybe GroupMemberId, BoolInt, Maybe UIThemeEntityOverrides, BoolInt, Maybe CustomData, Maybe Int64)
|
||||
type ContactRow' = (ProfileId, ContactName, Maybe Int64, ContactName, Text, Maybe ImageData, Maybe ConnLinkContact, LocalAlias, BoolInt, ContactStatus) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe Preferences, Preferences, UTCTime, UTCTime, Maybe UTCTime) :. (Maybe GroupMemberId, BoolInt, Maybe UIThemeEntityOverrides, BoolInt, Maybe CustomData, Maybe Int64)
|
||||
|
||||
type ContactRow = Only ContactId :. ContactRow'
|
||||
|
||||
@@ -441,10 +442,10 @@ getProfileById db userId profileId =
|
||||
|]
|
||||
(userId, profileId)
|
||||
where
|
||||
toProfile :: (ContactName, Text, Maybe ImageData, Maybe ConnReqContact, LocalAlias, Maybe Preferences) -> LocalProfile
|
||||
toProfile :: (ContactName, Text, Maybe ImageData, Maybe ConnLinkContact, LocalAlias, Maybe Preferences) -> LocalProfile
|
||||
toProfile (displayName, fullName, image, contactLink, localAlias, preferences) = LocalProfile {profileId, displayName, fullName, image, contactLink, preferences, localAlias}
|
||||
|
||||
type ContactRequestRow = (Int64, ContactName, AgentInvId, Maybe ContactId, Int64, AgentConnId, Int64, ContactName, Text, Maybe ImageData, Maybe ConnReqContact) :. (Maybe XContactId, PQSupport, Maybe Preferences, UTCTime, UTCTime, VersionChat, VersionChat)
|
||||
type ContactRequestRow = (Int64, ContactName, AgentInvId, Maybe ContactId, Int64, AgentConnId, Int64, ContactName, Text, Maybe ImageData, Maybe ConnLinkContact) :. (Maybe XContactId, PQSupport, Maybe Preferences, UTCTime, UTCTime, VersionChat, VersionChat)
|
||||
|
||||
toContactRequest :: ContactRequestRow -> UserContactRequest
|
||||
toContactRequest ((contactRequestId, localDisplayName, agentInvitationId, contactId_, userContactLinkId, agentContactConnId, profileId, displayName, fullName, image, contactLink) :. (xContactId, pqSupport, preferences, createdAt, updatedAt, minVer, maxVer)) = do
|
||||
@@ -462,7 +463,7 @@ userQuery =
|
||||
JOIN contact_profiles ucp ON ucp.contact_profile_id = uct.contact_profile_id
|
||||
|]
|
||||
|
||||
toUser :: (UserId, UserId, ContactId, ProfileId, BoolInt, Int64, ContactName, Text, Maybe ImageData, Maybe ConnReqContact, Maybe Preferences) :. (BoolInt, BoolInt, BoolInt, Maybe B64UrlByteString, Maybe B64UrlByteString, Maybe UTCTime, Maybe UIThemeEntityOverrides) -> User
|
||||
toUser :: (UserId, UserId, ContactId, ProfileId, BoolInt, Int64, ContactName, Text, Maybe ImageData, Maybe ConnLinkContact, Maybe Preferences) :. (BoolInt, BoolInt, BoolInt, Maybe B64UrlByteString, Maybe B64UrlByteString, Maybe UTCTime, Maybe UIThemeEntityOverrides) -> User
|
||||
toUser ((userId, auId, userContactId, profileId, BI activeUser, activeOrder, displayName, fullName, image, contactLink, userPreferences) :. (BI showNtfs, BI sendRcptsContacts, BI sendRcptsSmallGroups, viewPwdHash_, viewPwdSalt_, userMemberProfileUpdatedAt, uiThemes)) =
|
||||
User {userId, agentUserId = AgentUserId auId, userContactId, localDisplayName = displayName, profile, activeUser, activeOrder, fullPreferences, showNtfs, sendRcptsContacts, sendRcptsSmallGroups, viewPwdHash, userMemberProfileUpdatedAt, uiThemes}
|
||||
where
|
||||
@@ -470,9 +471,10 @@ toUser ((userId, auId, userContactId, profileId, BI activeUser, activeOrder, dis
|
||||
fullPreferences = mergePreferences Nothing userPreferences
|
||||
viewPwdHash = UserPwdHash <$> viewPwdHash_ <*> viewPwdSalt_
|
||||
|
||||
toPendingContactConnection :: (Int64, ConnId, ConnStatus, Maybe ByteString, Maybe Int64, Maybe GroupLinkId, Maybe Int64, Maybe ConnReqInvitation, LocalAlias, UTCTime, UTCTime) -> PendingContactConnection
|
||||
toPendingContactConnection (pccConnId, acId, pccConnStatus, connReqHash, viaUserContactLink, groupLinkId, customUserProfileId, connReqInv, localAlias, createdAt, updatedAt) =
|
||||
PendingContactConnection {pccConnId, pccAgentConnId = AgentConnId acId, pccConnStatus, viaContactUri = isJust connReqHash, viaUserContactLink, groupLinkId, customUserProfileId, connReqInv, localAlias, createdAt, updatedAt}
|
||||
toPendingContactConnection :: (Int64, ConnId, ConnStatus, Maybe ByteString, Maybe Int64, Maybe GroupLinkId, Maybe Int64, Maybe ConnReqInvitation, Maybe (ConnShortLink 'CMInvitation), LocalAlias, UTCTime, UTCTime) -> PendingContactConnection
|
||||
toPendingContactConnection (pccConnId, acId, pccConnStatus, connReqHash, viaUserContactLink, groupLinkId, customUserProfileId, connReqInv, shortLinkInv, localAlias, createdAt, updatedAt) =
|
||||
let connLinkInv = (`CCLink` shortLinkInv) <$> connReqInv
|
||||
in PendingContactConnection {pccConnId, pccAgentConnId = AgentConnId acId, pccConnStatus, viaContactUri = isJust connReqHash, viaUserContactLink, groupLinkId, customUserProfileId, connLinkInv, localAlias, createdAt, updatedAt}
|
||||
|
||||
getConnReqInv :: DB.Connection -> Int64 -> ExceptT StoreError IO ConnReqInvitation
|
||||
getConnReqInv db connId =
|
||||
@@ -579,7 +581,7 @@ type BusinessChatInfoRow = (Maybe BusinessChatType, Maybe MemberId, Maybe Member
|
||||
|
||||
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Text, Maybe Text, Maybe ImageData, Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. BusinessChatInfoRow :. (Maybe UIThemeEntityOverrides, Maybe CustomData, Maybe Int64) :. GroupMemberRow
|
||||
|
||||
type GroupMemberRow = (Int64, Int64, MemberId, VersionChat, VersionChat, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, ContactName, Maybe ContactId, ProfileId, ProfileId, ContactName, Text, Maybe ImageData, Maybe ConnReqContact, LocalAlias, Maybe Preferences) :. (UTCTime, UTCTime) :. (Maybe UTCTime, Int64, Int64, Int64)
|
||||
type GroupMemberRow = (Int64, Int64, MemberId, VersionChat, VersionChat, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, ContactName, Maybe ContactId, ProfileId, ProfileId, ContactName, Text, Maybe ImageData, Maybe ConnLinkContact, LocalAlias, Maybe Preferences) :. (UTCTime, UTCTime) :. (Maybe UTCTime, Int64, Int64, Int64)
|
||||
|
||||
toGroupInfo :: VersionRangeChat -> Int64 -> [ChatTagId] -> GroupInfoRow -> GroupInfo
|
||||
toGroupInfo vr userContactId chatTags ((groupId, localDisplayName, displayName, fullName, localAlias, description, image, enableNtfs_, sendRcpts, BI favorite, groupPreferences, memberAdmission) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. businessRow :. (uiThemes, customData, chatItemTTL) :. userMemberRow) =
|
||||
|
||||
@@ -9,12 +9,13 @@ module Simplex.Chat.Terminal where
|
||||
|
||||
import Control.Monad
|
||||
import qualified Data.List.NonEmpty as L
|
||||
import Simplex.Chat (defaultChatConfig, operatorSimpleXChat)
|
||||
import Simplex.Chat (defaultChatConfig)
|
||||
import Simplex.Chat.Controller
|
||||
import Simplex.Chat.Core
|
||||
import Simplex.Chat.Help (chatWelcome)
|
||||
import Simplex.Chat.Library.Commands (_defaultNtfServers)
|
||||
import Simplex.Chat.Operators
|
||||
import Simplex.Chat.Operators.Presets (operatorSimpleXChat)
|
||||
import Simplex.Chat.Options
|
||||
import Simplex.Chat.Terminal.Input
|
||||
import Simplex.Chat.Terminal.Output
|
||||
|
||||
@@ -51,7 +51,7 @@ import Simplex.Chat.Types.UITheme
|
||||
import Simplex.Chat.Types.Util
|
||||
import Simplex.FileTransfer.Description (FileDigest)
|
||||
import Simplex.FileTransfer.Types (RcvFileId, SndFileId)
|
||||
import Simplex.Messaging.Agent.Protocol (ACorrId, AEventTag (..), AEvtTag (..), ConnId, ConnectionMode (..), ConnectionRequestUri, InvitationId, SAEntity (..), UserId)
|
||||
import Simplex.Messaging.Agent.Protocol (ACorrId, AEventTag (..), AEvtTag (..), ConnId, ConnShortLink, ConnectionLink, ConnectionMode (..), ConnectionRequestUri, CreatedConnLink, InvitationId, SAEntity (..), UserId)
|
||||
import Simplex.Messaging.Agent.Store.DB (Binary (..), blobFieldDecoder, fromTextField_)
|
||||
import Simplex.Messaging.Crypto.File (CryptoFileArgs (..))
|
||||
import Simplex.Messaging.Crypto.Ratchet (PQEncryption (..), PQSupport, pattern PQEncOff)
|
||||
@@ -220,6 +220,8 @@ contactConnId c = aConnId <$> contactConn c
|
||||
|
||||
type IncognitoEnabled = Bool
|
||||
|
||||
type CreateShortLink = Bool
|
||||
|
||||
contactConnIncognito :: Contact -> IncognitoEnabled
|
||||
contactConnIncognito = maybe False connIncognito . contactConn
|
||||
|
||||
@@ -559,7 +561,7 @@ data Profile = Profile
|
||||
{ displayName :: ContactName,
|
||||
fullName :: Text,
|
||||
image :: Maybe ImageData,
|
||||
contactLink :: Maybe ConnReqContact,
|
||||
contactLink :: Maybe ConnLinkContact,
|
||||
preferences :: Maybe Preferences
|
||||
-- fields that should not be read into this data type to prevent sending them as part of profile to contacts:
|
||||
-- - contact_profile_id
|
||||
@@ -592,7 +594,7 @@ data LocalProfile = LocalProfile
|
||||
displayName :: ContactName,
|
||||
fullName :: Text,
|
||||
image :: Maybe ImageData,
|
||||
contactLink :: Maybe ConnReqContact,
|
||||
contactLink :: Maybe ConnLinkContact,
|
||||
preferences :: Maybe Preferences,
|
||||
localAlias :: LocalAlias
|
||||
}
|
||||
@@ -1449,6 +1451,14 @@ type ConnReqInvitation = ConnectionRequestUri 'CMInvitation
|
||||
|
||||
type ConnReqContact = ConnectionRequestUri 'CMContact
|
||||
|
||||
type CreatedLinkInvitation = CreatedConnLink 'CMInvitation
|
||||
|
||||
type CreatedLinkContact = CreatedConnLink 'CMContact
|
||||
|
||||
type ConnLinkContact = ConnectionLink 'CMContact
|
||||
|
||||
type ShortLinkContact = ConnShortLink 'CMContact
|
||||
|
||||
data Connection = Connection
|
||||
{ connId :: Int64,
|
||||
agentConnId :: AgentConnId,
|
||||
@@ -1526,7 +1536,7 @@ data PendingContactConnection = PendingContactConnection
|
||||
viaUserContactLink :: Maybe Int64,
|
||||
groupLinkId :: Maybe GroupLinkId,
|
||||
customUserProfileId :: Maybe Int64,
|
||||
connReqInv :: Maybe ConnReqInvitation,
|
||||
connLinkInv :: Maybe CreatedLinkInvitation,
|
||||
localAlias :: Text,
|
||||
createdAt :: UTCTime,
|
||||
updatedAt :: UTCTime
|
||||
|
||||
+46
-28
@@ -90,7 +90,7 @@ serializeChatResponse :: (Maybe RemoteHostId, Maybe User) -> CurrentTime -> Time
|
||||
serializeChatResponse user_ ts tz remoteHost_ = unlines . map unStyle . responseToView user_ defaultChatConfig False ts tz remoteHost_
|
||||
|
||||
responseToView :: (Maybe RemoteHostId, Maybe User) -> ChatConfig -> Bool -> CurrentTime -> TimeZone -> Maybe RemoteHostId -> ChatResponse -> [StyledString]
|
||||
responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showReceipts, testView} liveItems ts tz outputRH = \case
|
||||
responseToView hu@(currentRH, user_) cfg@ChatConfig {logLevel, showReactions, showReceipts, testView} liveItems ts tz outputRH = \case
|
||||
CRActiveUser User {profile, uiThemes} -> viewUserProfile (fromLocalProfile profile) <> viewUITheme uiThemes
|
||||
CRUsersList users -> viewUsersList users
|
||||
CRChatStarted -> ["chat started"]
|
||||
@@ -108,7 +108,7 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
|
||||
CRUserServersValidation {} -> []
|
||||
CRUsageConditions current _ accepted_ -> viewUsageConditions current accepted_
|
||||
CRChatItemTTL u ttl -> ttyUser u $ viewChatItemTTL ttl
|
||||
CRNetworkConfig cfg -> viewNetworkConfig cfg
|
||||
CRNetworkConfig netCfg -> viewNetworkConfig netCfg
|
||||
CRContactInfo u ct cStats customUserProfile -> ttyUser u $ viewContactInfo ct cStats customUserProfile
|
||||
CRGroupInfo u g s -> ttyUser u $ viewGroupInfo g s
|
||||
CRGroupMemberInfo u g m cStats -> ttyUser u $ viewGroupMemberInfo g m cStats
|
||||
@@ -181,7 +181,7 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
|
||||
HSDatabase -> databaseHelpInfo
|
||||
CRWelcome user -> chatWelcome user
|
||||
CRContactsList u cs -> ttyUser u $ viewContactsList cs
|
||||
CRUserContactLink u UserContactLink {connReqContact, autoAccept} -> ttyUser u $ connReqContact_ "Your chat address:" connReqContact <> autoAcceptStatus_ autoAccept
|
||||
CRUserContactLink u UserContactLink {connLinkContact, autoAccept} -> ttyUser u $ connReqContact_ "Your chat address:" connLinkContact <> autoAcceptStatus_ autoAccept
|
||||
CRUserContactLinkUpdated u UserContactLink {autoAccept} -> ttyUser u $ autoAcceptStatus_ autoAccept
|
||||
CRContactRequestRejected u UserContactRequest {localDisplayName = c} -> ttyUser u [ttyContact c <> ": contact request rejected"]
|
||||
CRGroupCreated u g -> ttyUser u $ viewGroupCreated g testView
|
||||
@@ -202,10 +202,10 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
|
||||
CRUserProfileNoChange u -> ttyUser u ["user profile did not change"]
|
||||
CRUserPrivacy u u' -> ttyUserPrefix u $ viewUserPrivacy u u'
|
||||
CRVersionInfo info _ _ -> viewVersionInfo logLevel info
|
||||
CRInvitation u cReq _ -> ttyUser u $ viewConnReqInvitation cReq
|
||||
CRInvitation u ccLink _ -> ttyUser u $ viewConnReqInvitation ccLink
|
||||
CRConnectionIncognitoUpdated u c -> ttyUser u $ viewConnectionIncognitoUpdated c
|
||||
CRConnectionUserChanged u c c' nu -> ttyUser u $ viewConnectionUserChanged u c nu c'
|
||||
CRConnectionPlan u connectionPlan -> ttyUser u $ viewConnectionPlan connectionPlan
|
||||
CRConnectionPlan u _ connectionPlan -> ttyUser u $ viewConnectionPlan cfg connectionPlan
|
||||
CRSentConfirmation u _ -> ttyUser u ["confirmation sent!"]
|
||||
CRSentInvitation u _ customUserProfile -> ttyUser u $ viewSentInvitation customUserProfile testView
|
||||
CRSentInvitationToContact u _c customUserProfile -> ttyUser u $ viewSentInvitation customUserProfile testView
|
||||
@@ -217,7 +217,7 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
|
||||
CRContactAlreadyExists u c -> ttyUser u [ttyFullContact c <> ": contact already exists"]
|
||||
CRContactRequestAlreadyAccepted u c -> ttyUser u [ttyFullContact c <> ": sent you a duplicate contact request, but you are already connected, no action needed"]
|
||||
CRBusinessRequestAlreadyAccepted u g -> ttyUser u [ttyFullGroup g <> ": sent you a duplicate connection request, but you are already connected, no action needed"]
|
||||
CRUserContactLinkCreated u cReq -> ttyUser u $ connReqContact_ "Your new chat address is created!" cReq
|
||||
CRUserContactLinkCreated u ccLink -> ttyUser u $ connReqContact_ "Your new chat address is created!" ccLink
|
||||
CRUserContactLinkDeleted u -> ttyUser u viewUserContactLinkDeleted
|
||||
CRUserAcceptedGroupSent u _g _ -> ttyUser u [] -- [ttyGroup' g <> ": joining the group..."]
|
||||
CRGroupLinkConnecting u g _ -> ttyUser u [ttyGroup' g <> ": joining the group..."]
|
||||
@@ -318,8 +318,8 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
|
||||
CRGroupUpdated u g g' m -> ttyUser u $ viewGroupUpdated g g' m
|
||||
CRGroupProfile u g -> ttyUser u $ viewGroupProfile g
|
||||
CRGroupDescription u g -> ttyUser u $ viewGroupDescription g
|
||||
CRGroupLinkCreated u g cReq mRole -> ttyUser u $ groupLink_ "Group link is created!" g cReq mRole
|
||||
CRGroupLink u g cReq mRole -> ttyUser u $ groupLink_ "Group link:" g cReq mRole
|
||||
CRGroupLinkCreated u g ccLink mRole -> ttyUser u $ groupLink_ "Group link is created!" g ccLink mRole
|
||||
CRGroupLink u g ccLink mRole -> ttyUser u $ groupLink_ "Group link:" g ccLink mRole
|
||||
CRGroupLinkDeleted u g -> ttyUser u $ viewGroupLinkDeleted g
|
||||
CRAcceptingGroupJoinRequestMember _ g m -> [ttyFullMember m <> ": accepting request to join group " <> ttyGroup' g <> "..."]
|
||||
CRNoMemberContactCreating u g m -> ttyUser u ["member " <> ttyGroup' g <> " " <> ttyMember m <> " does not have direct connection, creating"]
|
||||
@@ -916,14 +916,17 @@ viewInvalidConnReq =
|
||||
plain updateStr
|
||||
]
|
||||
|
||||
viewConnReqInvitation :: ConnReqInvitation -> [StyledString]
|
||||
viewConnReqInvitation cReq =
|
||||
viewConnReqInvitation :: CreatedLinkInvitation -> [StyledString]
|
||||
viewConnReqInvitation (CCLink cReq shortLink) =
|
||||
[ "pass this invitation link to your contact (via another channel): ",
|
||||
"",
|
||||
(plain . strEncode) (simplexChatInvitation cReq),
|
||||
plain $ maybe cReqStr strEncode shortLink,
|
||||
"",
|
||||
"and ask them to connect: " <> highlight' "/c <invitation_link_above>"
|
||||
]
|
||||
<> ["The invitation link for old clients: " <> plain cReqStr | isJust shortLink]
|
||||
where
|
||||
cReqStr = strEncode $ simplexChatInvitation cReq
|
||||
|
||||
simplexChatInvitation :: ConnReqInvitation -> ConnReqInvitation
|
||||
simplexChatInvitation (CRInvitationUri crData e2e) = CRInvitationUri crData {crScheme = simplexChat} e2e
|
||||
@@ -978,21 +981,29 @@ viewForwardPlan count itemIds = maybe [forwardCount] $ \fc -> [confirmation fc,
|
||||
| otherwise = plain $ show len <> " message(s) out of " <> show count <> " can be forwarded"
|
||||
len = length itemIds
|
||||
|
||||
connReqContact_ :: StyledString -> ConnReqContact -> [StyledString]
|
||||
connReqContact_ intro cReq =
|
||||
connReqContact_ :: StyledString -> CreatedLinkContact -> [StyledString]
|
||||
connReqContact_ intro (CCLink cReq shortLink) =
|
||||
[ intro,
|
||||
"",
|
||||
(plain . strEncode) (simplexChatContact cReq),
|
||||
plain $ maybe cReqStr strEncode shortLink,
|
||||
"",
|
||||
"Anybody can send you contact requests with: " <> highlight' "/c <contact_link_above>",
|
||||
"to show it again: " <> highlight' "/sa",
|
||||
"to share with your contacts: " <> highlight' "/profile_address on",
|
||||
"to delete it: " <> highlight' "/da" <> " (accepted contacts will remain connected)"
|
||||
]
|
||||
<> ["The contact link for old clients: " <> plain cReqStr | isJust shortLink]
|
||||
where
|
||||
cReqStr = strEncode $ simplexChatContact cReq
|
||||
|
||||
simplexChatContact :: ConnReqContact -> ConnReqContact
|
||||
simplexChatContact (CRContactUri crData) = CRContactUri crData {crScheme = simplexChat}
|
||||
|
||||
simplexChatContact' :: ConnLinkContact -> ConnLinkContact
|
||||
simplexChatContact' = \case
|
||||
CLFull (CRContactUri crData) -> CLFull $ CRContactUri crData {crScheme = simplexChat}
|
||||
l@(CLShort _) -> l
|
||||
|
||||
autoAcceptStatus_ :: Maybe AutoAccept -> [StyledString]
|
||||
autoAcceptStatus_ = \case
|
||||
Just AutoAccept {businessAddress, acceptIncognito, autoReply} ->
|
||||
@@ -1005,16 +1016,19 @@ autoAcceptStatus_ = \case
|
||||
| otherwise = ""
|
||||
_ -> ["auto_accept off"]
|
||||
|
||||
groupLink_ :: StyledString -> GroupInfo -> ConnReqContact -> GroupMemberRole -> [StyledString]
|
||||
groupLink_ intro g cReq mRole =
|
||||
groupLink_ :: StyledString -> GroupInfo -> CreatedLinkContact -> GroupMemberRole -> [StyledString]
|
||||
groupLink_ intro g (CCLink cReq shortLink) mRole =
|
||||
[ intro,
|
||||
"",
|
||||
(plain . strEncode) (simplexChatContact cReq),
|
||||
plain $ maybe cReqStr strEncode shortLink,
|
||||
"",
|
||||
"Anybody can connect to you and join group as " <> showRole mRole <> " with: " <> highlight' "/c <group_link_above>",
|
||||
"to show it again: " <> highlight ("/show link #" <> viewGroupName g),
|
||||
"to delete it: " <> highlight ("/delete link #" <> viewGroupName g) <> " (joined members will remain connected to you)"
|
||||
]
|
||||
<> ["The group link for old clients: " <> plain cReqStr | isJust shortLink]
|
||||
where
|
||||
cReqStr = strEncode $ simplexChatContact cReq
|
||||
|
||||
viewGroupLinkDeleted :: GroupInfo -> [StyledString]
|
||||
viewGroupLinkDeleted g =
|
||||
@@ -1450,7 +1464,7 @@ viewContactInfo :: Contact -> Maybe ConnectionStats -> Maybe Profile -> [StyledS
|
||||
viewContactInfo ct@Contact {contactId, profile = LocalProfile {localAlias, contactLink}, activeConn, uiThemes, customData} stats incognitoProfile =
|
||||
["contact ID: " <> sShow contactId]
|
||||
<> maybe [] viewConnectionStats stats
|
||||
<> maybe [] (\l -> ["contact address: " <> (plain . strEncode) (simplexChatContact l)]) contactLink
|
||||
<> maybe [] (\l -> ["contact address: " <> (plain . strEncode) (simplexChatContact' l)]) contactLink
|
||||
<> maybe
|
||||
["you've shared main profile with this contact"]
|
||||
(\p -> ["you've shared incognito profile with this contact: " <> incognitoProfile' p])
|
||||
@@ -1482,7 +1496,7 @@ viewGroupMemberInfo GroupInfo {groupId} m@GroupMember {groupMemberId, memberProf
|
||||
"member ID: " <> sShow groupMemberId
|
||||
]
|
||||
<> maybe ["member not connected"] viewConnectionStats stats
|
||||
<> maybe [] (\l -> ["contact address: " <> (plain . strEncode) (simplexChatContact l)]) contactLink
|
||||
<> maybe [] (\l -> ["contact address: " <> (plain . strEncode) (simplexChatContact' l)]) contactLink
|
||||
<> ["alias: " <> plain localAlias | localAlias /= ""]
|
||||
<> [viewConnectionVerified (memberSecurityCode m) | isJust stats]
|
||||
<> maybe [] (\ac -> [viewPeerChatVRange (peerChatVRange ac)]) activeConn
|
||||
@@ -1711,21 +1725,24 @@ viewConnectionIncognitoUpdated PendingContactConnection {pccConnId, customUserPr
|
||||
| otherwise = ["connection " <> sShow pccConnId <> " changed to non incognito"]
|
||||
|
||||
viewConnectionUserChanged :: User -> PendingContactConnection -> User -> PendingContactConnection -> [StyledString]
|
||||
viewConnectionUserChanged User {localDisplayName = n} PendingContactConnection {pccConnId, connReqInv} User {localDisplayName = n'} PendingContactConnection {connReqInv = connReqInv'} =
|
||||
case (connReqInv, connReqInv') of
|
||||
(Just cReqInv, Just cReqInv')
|
||||
| cReqInv /= cReqInv' -> [userChangedStr <> ", new link:"] <> newLink cReqInv'
|
||||
viewConnectionUserChanged User {localDisplayName = n} PendingContactConnection {pccConnId, connLinkInv} User {localDisplayName = n'} PendingContactConnection {connLinkInv = connLinkInv'} =
|
||||
case (connLinkInv, connLinkInv') of
|
||||
(Just ccLink, Just ccLink')
|
||||
| ccLink /= ccLink' -> [userChangedStr <> ", new link:"] <> newLink ccLink'
|
||||
_ -> [userChangedStr]
|
||||
where
|
||||
userChangedStr = "connection " <> sShow pccConnId <> " changed from user " <> plain n <> " to user " <> plain n'
|
||||
newLink cReqInv =
|
||||
newLink (CCLink cReq shortLink) =
|
||||
[ "",
|
||||
(plain . strEncode) (simplexChatInvitation cReqInv),
|
||||
plain $ maybe cReqStr strEncode shortLink,
|
||||
""
|
||||
]
|
||||
<> ["The invitation link for old clients: " <> plain cReqStr | isJust shortLink]
|
||||
where
|
||||
cReqStr = strEncode $ simplexChatInvitation cReq
|
||||
|
||||
viewConnectionPlan :: ConnectionPlan -> [StyledString]
|
||||
viewConnectionPlan = \case
|
||||
viewConnectionPlan :: ChatConfig -> ConnectionPlan -> [StyledString]
|
||||
viewConnectionPlan ChatConfig {logLevel, testView} = \case
|
||||
CPInvitationLink ilp -> case ilp of
|
||||
ILPOk -> [invLink "ok to connect"]
|
||||
ILPOwnLink -> [invLink "own link"]
|
||||
@@ -1764,6 +1781,7 @@ viewConnectionPlan = \case
|
||||
grpOrBiz GroupInfo {businessChat} = case businessChat of
|
||||
Just _ -> "business"
|
||||
Nothing -> "group"
|
||||
CPError e -> viewChatError False logLevel testView e
|
||||
|
||||
viewContactUpdated :: Contact -> Contact -> [StyledString]
|
||||
viewContactUpdated
|
||||
@@ -2186,8 +2204,8 @@ viewChatError isCmd logLevel testView = \case
|
||||
CEChatNotStarted -> ["error: chat not started"]
|
||||
CEChatNotStopped -> ["error: chat not stopped"]
|
||||
CEChatStoreChanged -> ["error: chat store changed, please restart chat"]
|
||||
CEConnectionPlan connectionPlan -> viewConnectionPlan connectionPlan
|
||||
CEInvalidConnReq -> viewInvalidConnReq
|
||||
CEUnsupportedConnReq -> [ "", "Connection link is not supported by the your app version, please ugrade it.", plain updateStr]
|
||||
CEInvalidChatMessage Connection {connId} msgMeta_ msg e ->
|
||||
[ plain $
|
||||
("chat message error: " <> e <> " (" <> T.unpack (T.take 120 msg) <> ")")
|
||||
|
||||
Reference in New Issue
Block a user