mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
core: auto-reply message for user contact addresses (#755)
* core: auto-reply message for user contact addresses * terminal: show auto accept status and message * test
This commit is contained in:
committed by
GitHub
parent
6d25991417
commit
41c9742b0d
@@ -39,6 +39,7 @@ library
|
||||
Simplex.Chat.Migrations.M20220321_chat_item_edited
|
||||
Simplex.Chat.Migrations.M20220404_files_status_fields
|
||||
Simplex.Chat.Migrations.M20220514_profiles_user_id
|
||||
Simplex.Chat.Migrations.M20220626_auto_reply
|
||||
Simplex.Chat.Mobile
|
||||
Simplex.Chat.Options
|
||||
Simplex.Chat.Protocol
|
||||
|
||||
+16
-9
@@ -52,7 +52,7 @@ import Simplex.Chat.Options (ChatOpts (..), smpServersP)
|
||||
import Simplex.Chat.Protocol
|
||||
import Simplex.Chat.Store
|
||||
import Simplex.Chat.Types
|
||||
import Simplex.Chat.Util (safeDecodeUtf8)
|
||||
import Simplex.Chat.Util (safeDecodeUtf8, uncurry3)
|
||||
import Simplex.Messaging.Agent
|
||||
import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..), InitialAgentServers (..), defaultAgentConfig)
|
||||
import Simplex.Messaging.Agent.Protocol
|
||||
@@ -592,9 +592,9 @@ processChatCommand = \case
|
||||
withStore' (`deleteUserContactLink` userId)
|
||||
pure CRUserContactLinkDeleted
|
||||
ShowMyAddress -> withUser $ \User {userId} ->
|
||||
uncurry CRUserContactLink <$> withStore (`getUserContactLink` userId)
|
||||
AddressAutoAccept onOff -> withUser $ \User {userId} -> do
|
||||
uncurry CRUserContactLinkUpdated <$> withStore (\db -> updateUserContactLinkAutoAccept db userId onOff)
|
||||
uncurry3 CRUserContactLink <$> withStore (`getUserContactLink` userId)
|
||||
AddressAutoAccept onOff msgContent -> withUser $ \User {userId} -> do
|
||||
uncurry3 CRUserContactLinkUpdated <$> withStore (\db -> updateUserContactLinkAutoAccept db userId onOff msgContent)
|
||||
AcceptContact cName -> withUser $ \User {userId} -> do
|
||||
connReqId <- withStore $ \db -> getContactRequestIdByName db userId cName
|
||||
processChatCommand $ APIAcceptContact connReqId
|
||||
@@ -996,9 +996,9 @@ acceptFileReceive user@User {userId} RcvFileTransfer {fileId, fileInvitation = F
|
||||
in ifM (doesFileExist f) (tryCombine $ n + 1) (pure f)
|
||||
|
||||
acceptContactRequest :: ChatMonad m => User -> UserContactRequest -> m Contact
|
||||
acceptContactRequest User {userId, profile} UserContactRequest {agentInvitationId = AgentInvId invId, localDisplayName = cName, profileId, profile = p, xContactId} = do
|
||||
acceptContactRequest User {userId, profile} UserContactRequest {agentInvitationId = AgentInvId invId, localDisplayName = cName, profileId, profile = p, userContactLinkId, xContactId} = do
|
||||
connId <- withAgent $ \a -> acceptContact a invId . directMessage $ XInfo profile
|
||||
withStore' $ \db -> createAcceptedContact db userId connId cName profileId p xContactId
|
||||
withStore' $ \db -> createAcceptedContact db userId connId cName profileId p userContactLinkId xContactId
|
||||
|
||||
agentSubscriber :: (MonadUnliftIO m, MonadReader ChatController m) => User -> Bool -> m ()
|
||||
agentSubscriber user subConns = do
|
||||
@@ -1136,7 +1136,7 @@ processAgentMessage (Just user@User {userId, profile}) agentConnId agentMessage
|
||||
_ -> Nothing
|
||||
|
||||
processDirectMessage :: ACommand 'Agent -> Connection -> Maybe Contact -> m ()
|
||||
processDirectMessage agentMsg conn@Connection {connId} = \case
|
||||
processDirectMessage agentMsg conn@Connection {connId, viaUserContactLink} = \case
|
||||
Nothing -> case agentMsg of
|
||||
CONF confId connInfo -> do
|
||||
saveConnInfo conn connInfo
|
||||
@@ -1205,6 +1205,13 @@ processAgentMessage (Just user@User {userId, profile}) agentConnId agentMessage
|
||||
toView $ CRContactConnected ct
|
||||
setActive $ ActiveC c
|
||||
showToast (c <> "> ") "connected"
|
||||
forM_ viaUserContactLink $ \userContactLinkId -> do
|
||||
withStore' (\db -> getUserContactLinkById db userId userContactLinkId) >>= \case
|
||||
Just (_, True, Just mc) -> do
|
||||
msg <- sendDirectContactMessage ct (XMsgNew $ MCSimple (ExtMsgContent mc Nothing))
|
||||
ci <- saveSndChatItem user (CDDirectSnd ct) msg (CISndMsgContent mc) Nothing Nothing
|
||||
toView . CRNewChatItem $ AChatItem SCTDirect SMDSnd (DirectChat ct) ci
|
||||
_ -> pure ()
|
||||
Just (gInfo, m@GroupMember {activeConn}) -> do
|
||||
when (maybe False ((== ConnReady) . connStatus) activeConn) $ do
|
||||
notifyMemberConnected gInfo m
|
||||
@@ -1438,7 +1445,7 @@ processAgentMessage (Just user@User {userId, profile}) agentConnId agentMessage
|
||||
withStore (\db -> createOrUpdateContactRequest db userId userContactLinkId invId p xContactId_) >>= \case
|
||||
CORContact contact -> toView $ CRContactRequestAlreadyAccepted contact
|
||||
CORRequest cReq@UserContactRequest {localDisplayName} -> do
|
||||
(_, autoAccept) <- withStore $ \db -> getUserContactLink db userId
|
||||
(_, autoAccept, _) <- withStore $ \db -> getUserContactLink db userId
|
||||
if autoAccept
|
||||
then acceptContactRequest user cReq >>= toView . CRAcceptingContactRequest
|
||||
else do
|
||||
@@ -2325,7 +2332,7 @@ chatCommandP =
|
||||
<|> ("/address" <|> "/ad") $> CreateMyAddress
|
||||
<|> ("/delete_address" <|> "/da") $> DeleteMyAddress
|
||||
<|> ("/show_address" <|> "/sa") $> ShowMyAddress
|
||||
<|> "/auto_accept " *> (AddressAutoAccept <$> onOffP)
|
||||
<|> "/auto_accept " *> (AddressAutoAccept <$> onOffP <*> optional (A.space *> msgContentP))
|
||||
<|> ("/accept @" <|> "/accept " <|> "/ac @" <|> "/ac ") *> (AcceptContact <$> displayName)
|
||||
<|> ("/reject @" <|> "/reject " <|> "/rc @" <|> "/rc ") *> (RejectContact <$> displayName)
|
||||
<|> ("/markdown" <|> "/m") $> ChatHelp HSMarkdown
|
||||
|
||||
@@ -38,7 +38,7 @@ chatBotRepl welcome answer _user cc = do
|
||||
initializeBotAddress :: ChatController -> IO ()
|
||||
initializeBotAddress cc = do
|
||||
sendChatCmd cc "/show_address" >>= \case
|
||||
CRUserContactLink uri _ -> showBotAddress uri
|
||||
CRUserContactLink uri _ _ -> showBotAddress uri
|
||||
CRChatCmdError (ChatErrorStore SEUserContactLinkNotFound) -> do
|
||||
putStrLn $ "No bot address, creating..."
|
||||
sendChatCmd cc "/address" >>= \case
|
||||
|
||||
@@ -147,7 +147,7 @@ data ChatCommand
|
||||
| CreateMyAddress
|
||||
| DeleteMyAddress
|
||||
| ShowMyAddress
|
||||
| AddressAutoAccept Bool
|
||||
| AddressAutoAccept Bool (Maybe MsgContent)
|
||||
| AcceptContact ContactName
|
||||
| RejectContact ContactName
|
||||
| SendMessage ChatName ByteString
|
||||
@@ -206,8 +206,8 @@ data ChatResponse
|
||||
| CRGroupCreated {groupInfo :: GroupInfo}
|
||||
| CRGroupMembers {group :: Group}
|
||||
| CRContactsList {contacts :: [Contact]}
|
||||
| CRUserContactLink {connReqContact :: ConnReqContact, autoAccept :: Bool}
|
||||
| CRUserContactLinkUpdated {connReqContact :: ConnReqContact, autoAccept :: Bool}
|
||||
| CRUserContactLink {connReqContact :: ConnReqContact, autoAccept :: Bool, autoReply :: Maybe MsgContent}
|
||||
| CRUserContactLinkUpdated {connReqContact :: ConnReqContact, autoAccept :: Bool, autoReply :: Maybe MsgContent}
|
||||
| CRContactRequestRejected {contactRequest :: UserContactRequest}
|
||||
| CRUserAcceptedGroupSent {groupInfo :: GroupInfo}
|
||||
| CRUserDeletedMember {groupInfo :: GroupInfo, member :: GroupMember}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Migrations.M20220626_auto_reply where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220626_auto_reply :: Query
|
||||
m20220626_auto_reply =
|
||||
[sql|
|
||||
ALTER TABLE user_contact_links ADD COLUMN auto_reply_msg_content TEXT DEFAULT NULL;
|
||||
|
||||
ALTER TABLE connections ADD COLUMN via_user_contact_link INTEGER DEFAULT NULL
|
||||
REFERENCES user_contact_links (user_contact_link_id) ON DELETE SET NULL;
|
||||
|]
|
||||
@@ -1,130 +1,167 @@
|
||||
CREATE TABLE migrations (
|
||||
name TEXT NOT NULL,
|
||||
ts TEXT NOT NULL,
|
||||
PRIMARY KEY (name)
|
||||
);
|
||||
CREATE TABLE contact_profiles ( -- remote user profile
|
||||
CREATE TABLE migrations(
|
||||
name TEXT NOT NULL,
|
||||
ts TEXT NOT NULL,
|
||||
PRIMARY KEY(name)
|
||||
);
|
||||
CREATE TABLE contact_profiles(
|
||||
-- remote user profile
|
||||
contact_profile_id INTEGER PRIMARY KEY,
|
||||
display_name TEXT NOT NULL, -- contact name set by remote user (not unique), this name must not contain spaces
|
||||
display_name TEXT NOT NULL, -- contact name set by remote user(not unique), this name must not contain spaces
|
||||
full_name TEXT NOT NULL,
|
||||
properties TEXT NOT NULL DEFAULT '{}' -- JSON with contact profile properties
|
||||
, created_at TEXT CHECK (created_at NOT NULL), updated_at TEXT CHECK (updated_at NOT NULL), image TEXT, user_id INTEGER DEFAULT NULL REFERENCES users ON DELETE CASCADE);
|
||||
CREATE INDEX contact_profiles_index ON contact_profiles (display_name, full_name);
|
||||
CREATE TABLE users (
|
||||
,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL),
|
||||
image TEXT,
|
||||
user_id INTEGER DEFAULT NULL REFERENCES users ON DELETE CASCADE
|
||||
);
|
||||
CREATE INDEX contact_profiles_index ON contact_profiles(
|
||||
display_name,
|
||||
full_name
|
||||
);
|
||||
CREATE TABLE users(
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
contact_id INTEGER NOT NULL UNIQUE REFERENCES contacts ON DELETE CASCADE
|
||||
DEFERRABLE INITIALLY DEFERRED,
|
||||
DEFERRABLE INITIALLY DEFERRED,
|
||||
local_display_name TEXT NOT NULL UNIQUE,
|
||||
active_user INTEGER NOT NULL DEFAULT 0, created_at TEXT CHECK (created_at NOT NULL), updated_at TEXT CHECK (updated_at NOT NULL), -- 1 for active user
|
||||
FOREIGN KEY (user_id, local_display_name)
|
||||
REFERENCES display_names (user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
DEFERRABLE INITIALLY DEFERRED
|
||||
active_user INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL), -- 1 for active user
|
||||
FOREIGN KEY(user_id, local_display_name)
|
||||
REFERENCES display_names(user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
DEFERRABLE INITIALLY DEFERRED
|
||||
);
|
||||
CREATE TABLE display_names (
|
||||
CREATE TABLE display_names(
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
local_display_name TEXT NOT NULL,
|
||||
ldn_base TEXT NOT NULL,
|
||||
ldn_suffix INTEGER NOT NULL DEFAULT 0, created_at TEXT CHECK (created_at NOT NULL), updated_at TEXT CHECK (updated_at NOT NULL),
|
||||
PRIMARY KEY (user_id, local_display_name) ON CONFLICT FAIL,
|
||||
UNIQUE (user_id, ldn_base, ldn_suffix) ON CONFLICT FAIL
|
||||
ldn_suffix INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL),
|
||||
PRIMARY KEY(user_id, local_display_name) ON CONFLICT FAIL,
|
||||
UNIQUE(user_id, ldn_base, ldn_suffix) ON CONFLICT FAIL
|
||||
) WITHOUT ROWID;
|
||||
CREATE TABLE contacts (
|
||||
CREATE TABLE contacts(
|
||||
contact_id INTEGER PRIMARY KEY,
|
||||
contact_profile_id INTEGER REFERENCES contact_profiles ON DELETE SET NULL, -- NULL if it's an incognito profile
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
local_display_name TEXT NOT NULL,
|
||||
is_user INTEGER NOT NULL DEFAULT 0, -- 1 if this contact is a user
|
||||
via_group INTEGER REFERENCES groups (group_id) ON DELETE SET NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT CHECK (updated_at NOT NULL), xcontact_id BLOB,
|
||||
FOREIGN KEY (user_id, local_display_name)
|
||||
REFERENCES display_names (user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
UNIQUE (user_id, local_display_name),
|
||||
UNIQUE (user_id, contact_profile_id)
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
local_display_name TEXT NOT NULL,
|
||||
is_user INTEGER NOT NULL DEFAULT 0, -- 1 if this contact is a user
|
||||
via_group INTEGER REFERENCES groups(group_id) ON DELETE SET NULL,
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL),
|
||||
xcontact_id BLOB,
|
||||
FOREIGN KEY(user_id, local_display_name)
|
||||
REFERENCES display_names(user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
UNIQUE(user_id, local_display_name),
|
||||
UNIQUE(user_id, contact_profile_id)
|
||||
);
|
||||
CREATE TABLE sent_probes (
|
||||
CREATE TABLE sent_probes(
|
||||
sent_probe_id INTEGER PRIMARY KEY,
|
||||
contact_id INTEGER NOT NULL UNIQUE REFERENCES contacts ON DELETE CASCADE,
|
||||
probe BLOB NOT NULL,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE, created_at TEXT CHECK (created_at NOT NULL), updated_at TEXT CHECK (updated_at NOT NULL),
|
||||
UNIQUE (user_id, probe)
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL),
|
||||
UNIQUE(user_id, probe)
|
||||
);
|
||||
CREATE TABLE sent_probe_hashes (
|
||||
CREATE TABLE sent_probe_hashes(
|
||||
sent_probe_hash_id INTEGER PRIMARY KEY,
|
||||
sent_probe_id INTEGER NOT NULL REFERENCES sent_probes ON DELETE CASCADE,
|
||||
contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE CASCADE,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE, created_at TEXT CHECK (created_at NOT NULL), updated_at TEXT CHECK (updated_at NOT NULL),
|
||||
UNIQUE (sent_probe_id, contact_id)
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL),
|
||||
UNIQUE(sent_probe_id, contact_id)
|
||||
);
|
||||
CREATE TABLE received_probes (
|
||||
CREATE TABLE received_probes(
|
||||
received_probe_id INTEGER PRIMARY KEY,
|
||||
contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE CASCADE,
|
||||
probe BLOB,
|
||||
probe_hash BLOB NOT NULL,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE
|
||||
, created_at TEXT CHECK (created_at NOT NULL), updated_at TEXT CHECK (updated_at NOT NULL));
|
||||
,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL)
|
||||
);
|
||||
CREATE TABLE known_servers(
|
||||
server_id INTEGER PRIMARY KEY,
|
||||
host TEXT NOT NULL,
|
||||
port TEXT NOT NULL,
|
||||
key_hash BLOB,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE, created_at TEXT CHECK (created_at NOT NULL), updated_at TEXT CHECK (updated_at NOT NULL),
|
||||
UNIQUE (user_id, host, port)
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL),
|
||||
UNIQUE(user_id, host, port)
|
||||
) WITHOUT ROWID;
|
||||
CREATE TABLE group_profiles ( -- shared group profiles
|
||||
CREATE TABLE group_profiles(
|
||||
-- shared group profiles
|
||||
group_profile_id INTEGER PRIMARY KEY,
|
||||
display_name TEXT NOT NULL, -- this name must not contain spaces
|
||||
full_name TEXT NOT NULL,
|
||||
properties TEXT NOT NULL DEFAULT '{}' -- JSON with user or contact profile
|
||||
, created_at TEXT CHECK (created_at NOT NULL), updated_at TEXT CHECK (updated_at NOT NULL), image TEXT, user_id INTEGER DEFAULT NULL REFERENCES users ON DELETE CASCADE);
|
||||
CREATE TABLE groups (
|
||||
,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL),
|
||||
image TEXT,
|
||||
user_id INTEGER DEFAULT NULL REFERENCES users ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE groups(
|
||||
group_id INTEGER PRIMARY KEY, -- local group ID
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
local_display_name TEXT NOT NULL, -- local group name without spaces
|
||||
group_profile_id INTEGER REFERENCES group_profiles ON DELETE SET NULL, -- shared group profile
|
||||
inv_queue_info BLOB, created_at TEXT CHECK (created_at NOT NULL), updated_at TEXT CHECK (updated_at NOT NULL), -- received
|
||||
FOREIGN KEY (user_id, local_display_name)
|
||||
REFERENCES display_names (user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
UNIQUE (user_id, local_display_name),
|
||||
UNIQUE (user_id, group_profile_id)
|
||||
inv_queue_info BLOB,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL), -- received
|
||||
FOREIGN KEY(user_id, local_display_name)
|
||||
REFERENCES display_names(user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
UNIQUE(user_id, local_display_name),
|
||||
UNIQUE(user_id, group_profile_id)
|
||||
);
|
||||
CREATE INDEX idx_groups_inv_queue_info ON groups (inv_queue_info);
|
||||
CREATE TABLE group_members ( -- group members, excluding the local user
|
||||
CREATE INDEX idx_groups_inv_queue_info ON groups(inv_queue_info);
|
||||
CREATE TABLE group_members(
|
||||
-- group members, excluding the local user
|
||||
group_member_id INTEGER PRIMARY KEY,
|
||||
group_id INTEGER NOT NULL REFERENCES groups ON DELETE CASCADE,
|
||||
member_id BLOB NOT NULL, -- shared member ID, unique per group
|
||||
member_role TEXT NOT NULL, -- owner, admin, member
|
||||
member_category TEXT NOT NULL, -- see GroupMemberCategory
|
||||
member_status TEXT NOT NULL, -- see GroupMemberStatus
|
||||
invited_by INTEGER REFERENCES contacts (contact_id) ON DELETE SET NULL, -- NULL for the members who joined before the current user and for the group creator
|
||||
invited_by INTEGER REFERENCES contacts(contact_id) ON DELETE SET NULL, -- NULL for the members who joined before the current user and for the group creator
|
||||
sent_inv_queue_info BLOB, -- sent
|
||||
group_queue_info BLOB, -- received
|
||||
direct_queue_info BLOB, -- received
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
local_display_name TEXT NOT NULL, -- should be the same as contact
|
||||
contact_profile_id INTEGER NOT NULL REFERENCES contact_profiles ON DELETE CASCADE,
|
||||
contact_id INTEGER REFERENCES contacts ON DELETE CASCADE, created_at TEXT CHECK (created_at NOT NULL), updated_at TEXT CHECK (updated_at NOT NULL),
|
||||
FOREIGN KEY (user_id, local_display_name)
|
||||
REFERENCES display_names (user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
UNIQUE (group_id, member_id)
|
||||
contact_id INTEGER REFERENCES contacts ON DELETE CASCADE,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL),
|
||||
FOREIGN KEY(user_id, local_display_name)
|
||||
REFERENCES display_names(user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
UNIQUE(group_id, member_id)
|
||||
);
|
||||
CREATE TABLE group_member_intros (
|
||||
CREATE TABLE group_member_intros(
|
||||
group_member_intro_id INTEGER PRIMARY KEY,
|
||||
re_group_member_id INTEGER NOT NULL REFERENCES group_members (group_member_id) ON DELETE CASCADE,
|
||||
to_group_member_id INTEGER NOT NULL REFERENCES group_members (group_member_id) ON DELETE CASCADE,
|
||||
re_group_member_id INTEGER NOT NULL REFERENCES group_members(group_member_id) ON DELETE CASCADE,
|
||||
to_group_member_id INTEGER NOT NULL REFERENCES group_members(group_member_id) ON DELETE CASCADE,
|
||||
group_queue_info BLOB,
|
||||
direct_queue_info BLOB,
|
||||
intro_status TEXT NOT NULL, created_at TEXT CHECK (created_at NOT NULL), updated_at TEXT CHECK (updated_at NOT NULL), -- see GroupMemberIntroStatus
|
||||
UNIQUE (re_group_member_id, to_group_member_id)
|
||||
intro_status TEXT NOT NULL,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL), -- see GroupMemberIntroStatus
|
||||
UNIQUE(re_group_member_id, to_group_member_id)
|
||||
);
|
||||
CREATE TABLE files (
|
||||
CREATE TABLE files(
|
||||
file_id INTEGER PRIMARY KEY,
|
||||
contact_id INTEGER REFERENCES contacts ON DELETE CASCADE,
|
||||
group_id INTEGER REFERENCES groups ON DELETE CASCADE,
|
||||
@@ -132,152 +169,203 @@ CREATE TABLE files (
|
||||
file_path TEXT,
|
||||
file_size INTEGER NOT NULL,
|
||||
chunk_size INTEGER NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE
|
||||
, chat_item_id INTEGER DEFAULT NULL REFERENCES chat_items ON DELETE CASCADE, updated_at TEXT CHECK (updated_at NOT NULL), cancelled INTEGER, ci_file_status TEXT);
|
||||
CREATE TABLE snd_files (
|
||||
,
|
||||
chat_item_id INTEGER DEFAULT NULL REFERENCES chat_items ON DELETE CASCADE,
|
||||
updated_at TEXT CHECK(updated_at NOT NULL),
|
||||
cancelled INTEGER,
|
||||
ci_file_status TEXT
|
||||
);
|
||||
CREATE TABLE snd_files(
|
||||
file_id INTEGER NOT NULL REFERENCES files ON DELETE CASCADE,
|
||||
connection_id INTEGER NOT NULL REFERENCES connections ON DELETE CASCADE,
|
||||
file_status TEXT NOT NULL, -- new, accepted, connected, completed
|
||||
group_member_id INTEGER REFERENCES group_members ON DELETE CASCADE, created_at TEXT CHECK (created_at NOT NULL), updated_at TEXT CHECK (updated_at NOT NULL),
|
||||
PRIMARY KEY (file_id, connection_id)
|
||||
group_member_id INTEGER REFERENCES group_members ON DELETE CASCADE,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL),
|
||||
PRIMARY KEY(file_id, connection_id)
|
||||
) WITHOUT ROWID;
|
||||
CREATE TABLE rcv_files (
|
||||
CREATE TABLE rcv_files(
|
||||
file_id INTEGER PRIMARY KEY REFERENCES files ON DELETE CASCADE,
|
||||
file_status TEXT NOT NULL, -- new, accepted, connected, completed
|
||||
group_member_id INTEGER REFERENCES group_members ON DELETE CASCADE,
|
||||
file_queue_info BLOB
|
||||
, created_at TEXT CHECK (created_at NOT NULL), updated_at TEXT CHECK (updated_at NOT NULL));
|
||||
CREATE TABLE snd_file_chunks (
|
||||
,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL)
|
||||
);
|
||||
CREATE TABLE snd_file_chunks(
|
||||
file_id INTEGER NOT NULL,
|
||||
connection_id INTEGER NOT NULL,
|
||||
chunk_number INTEGER NOT NULL,
|
||||
chunk_agent_msg_id INTEGER,
|
||||
chunk_sent INTEGER NOT NULL DEFAULT 0, created_at TEXT CHECK (created_at NOT NULL), updated_at TEXT CHECK (updated_at NOT NULL), -- 0 (sent to agent), 1 (sent to server)
|
||||
FOREIGN KEY (file_id, connection_id) REFERENCES snd_files ON DELETE CASCADE,
|
||||
PRIMARY KEY (file_id, connection_id, chunk_number)
|
||||
chunk_sent INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL), -- 0(sent to agent), 1(sent to server)
|
||||
FOREIGN KEY(file_id, connection_id) REFERENCES snd_files ON DELETE CASCADE,
|
||||
PRIMARY KEY(file_id, connection_id, chunk_number)
|
||||
) WITHOUT ROWID;
|
||||
CREATE TABLE rcv_file_chunks (
|
||||
CREATE TABLE rcv_file_chunks(
|
||||
file_id INTEGER NOT NULL REFERENCES rcv_files ON DELETE CASCADE,
|
||||
chunk_number INTEGER NOT NULL,
|
||||
chunk_agent_msg_id INTEGER NOT NULL,
|
||||
chunk_stored INTEGER NOT NULL DEFAULT 0, created_at TEXT CHECK (created_at NOT NULL), updated_at TEXT CHECK (updated_at NOT NULL), -- 0 (received), 1 (appended to file)
|
||||
PRIMARY KEY (file_id, chunk_number)
|
||||
chunk_stored INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL), -- 0(received), 1(appended to file)
|
||||
PRIMARY KEY(file_id, chunk_number)
|
||||
) WITHOUT ROWID;
|
||||
CREATE TABLE connections ( -- all SMP agent connections
|
||||
CREATE TABLE connections(
|
||||
-- all SMP agent connections
|
||||
connection_id INTEGER PRIMARY KEY,
|
||||
agent_conn_id BLOB NOT NULL UNIQUE,
|
||||
conn_level INTEGER NOT NULL DEFAULT 0,
|
||||
via_contact INTEGER REFERENCES contacts (contact_id) ON DELETE SET NULL,
|
||||
via_contact INTEGER REFERENCES contacts(contact_id) ON DELETE SET NULL,
|
||||
conn_status TEXT NOT NULL,
|
||||
conn_type TEXT NOT NULL, -- contact, member, rcv_file, snd_file
|
||||
user_contact_link_id INTEGER REFERENCES user_contact_links ON DELETE CASCADE,
|
||||
contact_id INTEGER REFERENCES contacts ON DELETE CASCADE,
|
||||
group_member_id INTEGER REFERENCES group_members ON DELETE CASCADE,
|
||||
snd_file_id INTEGER,
|
||||
rcv_file_id INTEGER REFERENCES rcv_files (file_id) ON DELETE CASCADE,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE, updated_at TEXT CHECK (updated_at NOT NULL), via_contact_uri_hash BLOB, xcontact_id BLOB,
|
||||
FOREIGN KEY (snd_file_id, connection_id)
|
||||
REFERENCES snd_files (file_id, connection_id)
|
||||
ON DELETE CASCADE
|
||||
DEFERRABLE INITIALLY DEFERRED
|
||||
rcv_file_id INTEGER REFERENCES rcv_files(file_id) ON DELETE CASCADE,
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
updated_at TEXT CHECK(updated_at NOT NULL),
|
||||
via_contact_uri_hash BLOB,
|
||||
xcontact_id BLOB,
|
||||
via_user_contact_link INTEGER DEFAULT NULL
|
||||
REFERENCES user_contact_links(user_contact_link_id) ON DELETE SET NULL,
|
||||
FOREIGN KEY(snd_file_id, connection_id)
|
||||
REFERENCES snd_files(file_id, connection_id)
|
||||
ON DELETE CASCADE
|
||||
DEFERRABLE INITIALLY DEFERRED
|
||||
);
|
||||
CREATE TABLE user_contact_links (
|
||||
CREATE TABLE user_contact_links(
|
||||
user_contact_link_id INTEGER PRIMARY KEY,
|
||||
conn_req_contact BLOB NOT NULL,
|
||||
local_display_name TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE, updated_at TEXT CHECK (updated_at NOT NULL), auto_accept INTEGER DEFAULT 0,
|
||||
UNIQUE (user_id, local_display_name)
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
updated_at TEXT CHECK(updated_at NOT NULL),
|
||||
auto_accept INTEGER DEFAULT 0,
|
||||
auto_reply_msg_content TEXT DEFAULT NULL,
|
||||
UNIQUE(user_id, local_display_name)
|
||||
);
|
||||
CREATE TABLE contact_requests (
|
||||
CREATE TABLE contact_requests(
|
||||
contact_request_id INTEGER PRIMARY KEY,
|
||||
user_contact_link_id INTEGER NOT NULL REFERENCES user_contact_links
|
||||
ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
agent_invitation_id BLOB NOT NULL,
|
||||
ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
agent_invitation_id BLOB NOT NULL,
|
||||
contact_profile_id INTEGER REFERENCES contact_profiles
|
||||
ON DELETE SET NULL -- NULL if it's an incognito profile
|
||||
DEFERRABLE INITIALLY DEFERRED,
|
||||
local_display_name TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE, updated_at TEXT CHECK (updated_at NOT NULL), xcontact_id BLOB,
|
||||
FOREIGN KEY (user_id, local_display_name)
|
||||
REFERENCES display_names (user_id, local_display_name)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE CASCADE
|
||||
DEFERRABLE INITIALLY DEFERRED,
|
||||
UNIQUE (user_id, local_display_name),
|
||||
UNIQUE (user_id, contact_profile_id)
|
||||
ON DELETE SET NULL -- NULL if it's an incognito profile
|
||||
DEFERRABLE INITIALLY DEFERRED,
|
||||
local_display_name TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE, updated_at TEXT CHECK(updated_at NOT NULL), xcontact_id BLOB,
|
||||
FOREIGN KEY(user_id, local_display_name)
|
||||
REFERENCES display_names(user_id, local_display_name)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE CASCADE
|
||||
DEFERRABLE INITIALLY DEFERRED,
|
||||
UNIQUE(user_id, local_display_name),
|
||||
UNIQUE(user_id, contact_profile_id)
|
||||
);
|
||||
CREATE TABLE messages (
|
||||
CREATE TABLE messages(
|
||||
message_id INTEGER PRIMARY KEY,
|
||||
msg_sent INTEGER NOT NULL, -- 0 for received, 1 for sent
|
||||
chat_msg_event TEXT NOT NULL, -- message event tag (the constructor of CMEventTag)
|
||||
chat_msg_event TEXT NOT NULL, -- message event tag(the constructor of CMEventTag)
|
||||
msg_body BLOB, -- agent message body as received or sent
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
, updated_at TEXT CHECK (updated_at NOT NULL), connection_id INTEGER DEFAULT NULL REFERENCES connections ON DELETE CASCADE, group_id INTEGER DEFAULT NULL REFERENCES groups ON DELETE CASCADE, shared_msg_id BLOB, shared_msg_id_user INTEGER);
|
||||
CREATE TABLE msg_deliveries (
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now'))
|
||||
,
|
||||
updated_at TEXT CHECK(updated_at NOT NULL),
|
||||
connection_id INTEGER DEFAULT NULL REFERENCES connections ON DELETE CASCADE,
|
||||
group_id INTEGER DEFAULT NULL REFERENCES groups ON DELETE CASCADE,
|
||||
shared_msg_id BLOB,
|
||||
shared_msg_id_user INTEGER
|
||||
);
|
||||
CREATE TABLE msg_deliveries(
|
||||
msg_delivery_id INTEGER PRIMARY KEY,
|
||||
message_id INTEGER NOT NULL REFERENCES messages ON DELETE CASCADE, -- non UNIQUE for group messages
|
||||
connection_id INTEGER NOT NULL REFERENCES connections ON DELETE CASCADE,
|
||||
agent_msg_id INTEGER, -- internal agent message ID (NULL while pending)
|
||||
agent_msg_id INTEGER, -- internal agent message ID(NULL while pending)
|
||||
agent_msg_meta TEXT, -- JSON with timestamps etc. sent in MSG, NULL for sent
|
||||
chat_ts TEXT NOT NULL DEFAULT (datetime('now')), created_at TEXT CHECK (created_at NOT NULL), updated_at TEXT CHECK (updated_at NOT NULL), -- broker_ts for received, created_at for sent
|
||||
UNIQUE (connection_id, agent_msg_id)
|
||||
chat_ts TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL), -- broker_ts for received, created_at for sent
|
||||
UNIQUE(connection_id, agent_msg_id)
|
||||
);
|
||||
CREATE TABLE msg_delivery_events (
|
||||
CREATE TABLE msg_delivery_events(
|
||||
msg_delivery_event_id INTEGER PRIMARY KEY,
|
||||
msg_delivery_id INTEGER NOT NULL REFERENCES msg_deliveries ON DELETE CASCADE, -- non UNIQUE for multiple events per msg delivery
|
||||
delivery_status TEXT NOT NULL, -- see MsgDeliveryStatus for allowed values
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT CHECK (updated_at NOT NULL),
|
||||
UNIQUE (msg_delivery_id, delivery_status)
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL),
|
||||
UNIQUE(msg_delivery_id, delivery_status)
|
||||
);
|
||||
CREATE TABLE pending_group_messages (
|
||||
CREATE TABLE pending_group_messages(
|
||||
pending_group_message_id INTEGER PRIMARY KEY,
|
||||
group_member_id INTEGER NOT NULL REFERENCES group_members ON DELETE CASCADE,
|
||||
message_id INTEGER NOT NULL REFERENCES messages ON DELETE CASCADE,
|
||||
group_member_intro_id INTEGER REFERENCES group_member_intros ON DELETE CASCADE,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT(datetime('now'))
|
||||
);
|
||||
CREATE TABLE chat_items (
|
||||
CREATE TABLE chat_items(
|
||||
chat_item_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
contact_id INTEGER REFERENCES contacts ON DELETE CASCADE,
|
||||
group_id INTEGER REFERENCES groups ON DELETE CASCADE,
|
||||
group_member_id INTEGER REFERENCES group_members ON DELETE SET NULL, -- NULL for sent even if group_id is not
|
||||
chat_msg_id INTEGER, -- sent as part of the message that created the item
|
||||
created_by_msg_id INTEGER UNIQUE REFERENCES messages (message_id) ON DELETE SET NULL,
|
||||
created_by_msg_id INTEGER UNIQUE REFERENCES messages(message_id) ON DELETE SET NULL,
|
||||
item_sent INTEGER NOT NULL, -- 0 for received, 1 for sent
|
||||
item_ts TEXT NOT NULL, -- broker_ts of creating message for received, created_at for sent
|
||||
item_deleted INTEGER NOT NULL DEFAULT 0, -- 1 for deleted,
|
||||
item_content TEXT NOT NULL, -- JSON
|
||||
item_text TEXT NOT NULL, -- textual representation
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
, item_status TEXT CHECK (item_status NOT NULL), shared_msg_id BLOB, quoted_shared_msg_id BLOB, quoted_sent_at TEXT, quoted_content TEXT, quoted_sent INTEGER, quoted_member_id BLOB, item_edited INTEGER);
|
||||
CREATE TABLE chat_item_messages (
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT(datetime('now'))
|
||||
,
|
||||
item_status TEXT CHECK(item_status NOT NULL),
|
||||
shared_msg_id BLOB,
|
||||
quoted_shared_msg_id BLOB,
|
||||
quoted_sent_at TEXT,
|
||||
quoted_content TEXT,
|
||||
quoted_sent INTEGER,
|
||||
quoted_member_id BLOB,
|
||||
item_edited INTEGER
|
||||
);
|
||||
CREATE TABLE chat_item_messages(
|
||||
chat_item_id INTEGER NOT NULL REFERENCES chat_items ON DELETE CASCADE,
|
||||
message_id INTEGER NOT NULL UNIQUE REFERENCES messages ON DELETE CASCADE,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
UNIQUE (chat_item_id, message_id)
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
UNIQUE(chat_item_id, message_id)
|
||||
);
|
||||
CREATE INDEX idx_connections_via_contact_uri_hash ON connections (via_contact_uri_hash);
|
||||
CREATE INDEX idx_contact_requests_xcontact_id ON contact_requests (xcontact_id);
|
||||
CREATE INDEX idx_contacts_xcontact_id ON contacts (xcontact_id);
|
||||
CREATE TABLE smp_servers (
|
||||
CREATE INDEX idx_connections_via_contact_uri_hash ON connections(
|
||||
via_contact_uri_hash
|
||||
);
|
||||
CREATE INDEX idx_contact_requests_xcontact_id ON contact_requests(xcontact_id);
|
||||
CREATE INDEX idx_contacts_xcontact_id ON contacts(xcontact_id);
|
||||
CREATE TABLE smp_servers(
|
||||
smp_server_id INTEGER PRIMARY KEY,
|
||||
host TEXT NOT NULL,
|
||||
port TEXT NOT NULL,
|
||||
key_hash BLOB NOT NULL,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
UNIQUE (host, port)
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
UNIQUE(host, port)
|
||||
);
|
||||
CREATE INDEX idx_messages_shared_msg_id ON messages (shared_msg_id);
|
||||
CREATE UNIQUE INDEX idx_messages_direct_shared_msg_id ON messages (connection_id, shared_msg_id_user, shared_msg_id);
|
||||
CREATE UNIQUE INDEX idx_messages_group_shared_msg_id ON messages (group_id, shared_msg_id_user, shared_msg_id);
|
||||
CREATE INDEX idx_chat_items_shared_msg_id ON chat_items (shared_msg_id);
|
||||
CREATE INDEX idx_messages_shared_msg_id ON messages(shared_msg_id);
|
||||
CREATE UNIQUE INDEX idx_messages_direct_shared_msg_id ON messages(
|
||||
connection_id,
|
||||
shared_msg_id_user,
|
||||
shared_msg_id
|
||||
);
|
||||
CREATE UNIQUE INDEX idx_messages_group_shared_msg_id ON messages(
|
||||
group_id,
|
||||
shared_msg_id_user,
|
||||
shared_msg_id
|
||||
);
|
||||
CREATE INDEX idx_chat_items_shared_msg_id ON chat_items(shared_msg_id);
|
||||
|
||||
+61
-43
@@ -42,6 +42,7 @@ module Simplex.Chat.Store
|
||||
getUserContactLinkConnections,
|
||||
deleteUserContactLink,
|
||||
getUserContactLink,
|
||||
getUserContactLinkById,
|
||||
updateUserContactLinkAutoAccept,
|
||||
createOrUpdateContactRequest,
|
||||
getContactRequest,
|
||||
@@ -205,6 +206,7 @@ import Simplex.Chat.Migrations.M20220304_msg_quotes
|
||||
import Simplex.Chat.Migrations.M20220321_chat_item_edited
|
||||
import Simplex.Chat.Migrations.M20220404_files_status_fields
|
||||
import Simplex.Chat.Migrations.M20220514_profiles_user_id
|
||||
import Simplex.Chat.Migrations.M20220626_auto_reply
|
||||
import Simplex.Chat.Protocol
|
||||
import Simplex.Chat.Types
|
||||
import Simplex.Messaging.Agent.Protocol (AgentMsgId, ConnId, InvitationId, MsgMeta (..))
|
||||
@@ -229,7 +231,8 @@ schemaMigrations =
|
||||
("20220304_msg_quotes", m20220304_msg_quotes),
|
||||
("20220321_chat_item_edited", m20220321_chat_item_edited),
|
||||
("20220404_files_status_fields", m20220404_files_status_fields),
|
||||
("20220514_profiles_user_id", m20220514_profiles_user_id)
|
||||
("20220514_profiles_user_id", m20220514_profiles_user_id),
|
||||
("20220626_auto_reply", m20220626_auto_reply)
|
||||
]
|
||||
|
||||
-- | The list of migrations in ascending order by date
|
||||
@@ -335,7 +338,7 @@ getConnReqContactXContactId db userId cReqHash = do
|
||||
-- Contact
|
||||
ct.contact_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, ct.created_at, ct.updated_at,
|
||||
-- Connection
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.conn_status, c.conn_type,
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.conn_status, c.conn_type,
|
||||
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
||||
FROM contacts ct
|
||||
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
||||
@@ -366,24 +369,24 @@ createDirectConnection db userId acId pccConnStatus = do
|
||||
pccConnId <- insertedRowId db
|
||||
pure PendingContactConnection {pccConnId, pccAgentConnId = AgentConnId acId, pccConnStatus, viaContactUri = False, createdAt, updatedAt = createdAt}
|
||||
|
||||
createContactConnection_ :: DB.Connection -> UserId -> ConnId -> Maybe Int64 -> Int -> UTCTime -> IO Connection
|
||||
createContactConnection_ db userId = createConnection_ db userId ConnContact Nothing
|
||||
createMemberContactConnection_ :: DB.Connection -> UserId -> ConnId -> Maybe Int64 -> Int -> UTCTime -> IO Connection
|
||||
createMemberContactConnection_ db userId agentConnId viaContact = createConnection_ db userId ConnContact Nothing agentConnId viaContact Nothing
|
||||
|
||||
createConnection_ :: DB.Connection -> UserId -> ConnType -> Maybe Int64 -> ConnId -> Maybe Int64 -> Int -> UTCTime -> IO Connection
|
||||
createConnection_ db userId connType entityId acId viaContact connLevel currentTs = do
|
||||
createConnection_ :: DB.Connection -> UserId -> ConnType -> Maybe Int64 -> ConnId -> Maybe Int64 -> Maybe Int64 -> Int -> UTCTime -> IO Connection
|
||||
createConnection_ db userId connType entityId acId viaContact viaUserContactLink connLevel currentTs = do
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
INSERT INTO connections (
|
||||
user_id, agent_conn_id, conn_level, via_contact, conn_status, conn_type,
|
||||
user_id, agent_conn_id, conn_level, via_contact, via_user_contact_link, conn_status, conn_type,
|
||||
contact_id, group_member_id, snd_file_id, rcv_file_id, user_contact_link_id, created_at, updated_at
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
( (userId, acId, connLevel, viaContact, ConnNew, connType)
|
||||
( (userId, acId, connLevel, viaContact, viaUserContactLink, ConnNew, connType)
|
||||
:. (ent ConnContact, ent ConnMember, ent ConnSndFile, ent ConnRcvFile, ent ConnUserContact, currentTs, currentTs)
|
||||
)
|
||||
connId <- insertedRowId db
|
||||
pure Connection {connId, agentConnId = AgentConnId acId, connType, entityId, viaContact, connLevel, connStatus = ConnNew, createdAt = currentTs}
|
||||
pure Connection {connId, agentConnId = AgentConnId acId, connType, entityId, viaContact, viaUserContactLink, connLevel, connStatus = ConnNew, createdAt = currentTs}
|
||||
where
|
||||
ent ct = if connType == ct then entityId else Nothing
|
||||
|
||||
@@ -563,7 +566,7 @@ createUserContactLink db userId agentConnId cReq =
|
||||
"INSERT INTO user_contact_links (user_id, conn_req_contact, created_at, updated_at) VALUES (?,?,?,?)"
|
||||
(userId, cReq, currentTs, currentTs)
|
||||
userContactLinkId <- insertedRowId db
|
||||
void $ createConnection_ db userId ConnUserContact (Just userContactLinkId) agentConnId Nothing 0 currentTs
|
||||
void $ createConnection_ db userId ConnUserContact (Just userContactLinkId) agentConnId Nothing Nothing 0 currentTs
|
||||
|
||||
getUserContactLinkConnections :: DB.Connection -> UserId -> ExceptT StoreError IO [Connection]
|
||||
getUserContactLinkConnections db userId =
|
||||
@@ -573,7 +576,7 @@ getUserContactLinkConnections db userId =
|
||||
DB.queryNamed
|
||||
db
|
||||
[sql|
|
||||
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact,
|
||||
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link,
|
||||
c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
||||
FROM connections c
|
||||
JOIN user_contact_links uc ON c.user_contact_link_id = uc.user_contact_link_id
|
||||
@@ -627,24 +630,37 @@ deleteUserContactLink db userId = do
|
||||
[":user_id" := userId]
|
||||
DB.execute db "DELETE FROM user_contact_links WHERE user_id = ? AND local_display_name = ''" (Only userId)
|
||||
|
||||
getUserContactLink :: DB.Connection -> UserId -> ExceptT StoreError IO (ConnReqContact, Bool)
|
||||
getUserContactLink :: DB.Connection -> UserId -> ExceptT StoreError IO (ConnReqContact, Bool, Maybe MsgContent)
|
||||
getUserContactLink db userId =
|
||||
ExceptT . firstRow id SEUserContactLinkNotFound $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT conn_req_contact, auto_accept
|
||||
SELECT conn_req_contact, auto_accept, auto_reply_msg_content
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ?
|
||||
AND local_display_name = ''
|
||||
|]
|
||||
(Only userId)
|
||||
|
||||
updateUserContactLinkAutoAccept :: DB.Connection -> UserId -> Bool -> ExceptT StoreError IO (ConnReqContact, Bool)
|
||||
updateUserContactLinkAutoAccept db userId autoAccept = do
|
||||
(cReqUri, _) <- getUserContactLink db userId
|
||||
getUserContactLinkById :: DB.Connection -> UserId -> Int64 -> IO (Maybe (ConnReqContact, Bool, Maybe MsgContent))
|
||||
getUserContactLinkById db userId userContactLinkId =
|
||||
maybeFirstRow id $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT conn_req_contact, auto_accept, auto_reply_msg_content
|
||||
FROM user_contact_links
|
||||
WHERE user_id = ?
|
||||
AND user_contact_link_id = ?
|
||||
|]
|
||||
(userId, userContactLinkId)
|
||||
|
||||
updateUserContactLinkAutoAccept :: DB.Connection -> UserId -> Bool -> Maybe MsgContent -> ExceptT StoreError IO (ConnReqContact, Bool, Maybe MsgContent)
|
||||
updateUserContactLinkAutoAccept db userId autoAccept msgContent = do
|
||||
(cReqUri, _, _) <- getUserContactLink db userId
|
||||
liftIO updateUserContactLinkAutoAccept_
|
||||
pure (cReqUri, autoAccept)
|
||||
pure (cReqUri, autoAccept, msgContent)
|
||||
where
|
||||
updateUserContactLinkAutoAccept_ :: IO ()
|
||||
updateUserContactLinkAutoAccept_ =
|
||||
@@ -652,11 +668,11 @@ updateUserContactLinkAutoAccept db userId autoAccept = do
|
||||
db
|
||||
[sql|
|
||||
UPDATE user_contact_links
|
||||
SET auto_accept = ?
|
||||
SET auto_accept = ?, auto_reply_msg_content = ?
|
||||
WHERE user_id = ?
|
||||
AND local_display_name = ''
|
||||
|]
|
||||
(autoAccept, userId)
|
||||
(autoAccept, msgContent, userId)
|
||||
|
||||
createOrUpdateContactRequest :: DB.Connection -> UserId -> Int64 -> InvitationId -> Profile -> Maybe XContactId -> ExceptT StoreError IO ContactOrRequest
|
||||
createOrUpdateContactRequest db userId userContactLinkId invId Profile {displayName, fullName, image} xContactId_ =
|
||||
@@ -703,7 +719,7 @@ createOrUpdateContactRequest db userId userContactLinkId invId Profile {displayN
|
||||
-- Contact
|
||||
ct.contact_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, ct.created_at, ct.updated_at,
|
||||
-- Connection
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.conn_status, c.conn_type,
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.conn_status, c.conn_type,
|
||||
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
||||
FROM contacts ct
|
||||
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
||||
@@ -812,8 +828,8 @@ deleteContactRequest db userId contactRequestId = do
|
||||
(userId, userId, contactRequestId)
|
||||
DB.execute db "DELETE FROM contact_requests WHERE user_id = ? AND contact_request_id = ?" (userId, contactRequestId)
|
||||
|
||||
createAcceptedContact :: DB.Connection -> UserId -> ConnId -> ContactName -> Int64 -> Profile -> Maybe XContactId -> IO Contact
|
||||
createAcceptedContact db userId agentConnId localDisplayName profileId profile xContactId = do
|
||||
createAcceptedContact :: DB.Connection -> UserId -> ConnId -> ContactName -> Int64 -> Profile -> Int64 -> Maybe XContactId -> IO Contact
|
||||
createAcceptedContact db userId agentConnId localDisplayName profileId profile userContactLinkId xContactId = do
|
||||
DB.execute db "DELETE FROM contact_requests WHERE user_id = ? AND local_display_name = ?" (userId, localDisplayName)
|
||||
currentTs <- getCurrentTime
|
||||
DB.execute
|
||||
@@ -821,7 +837,7 @@ createAcceptedContact db userId agentConnId localDisplayName profileId profile x
|
||||
"INSERT INTO contacts (user_id, local_display_name, contact_profile_id, created_at, updated_at, xcontact_id) VALUES (?,?,?,?,?,?)"
|
||||
(userId, localDisplayName, profileId, currentTs, currentTs, xContactId)
|
||||
contactId <- insertedRowId db
|
||||
activeConn <- createConnection_ db userId ConnContact (Just contactId) agentConnId Nothing 0 currentTs
|
||||
activeConn <- createConnection_ db userId ConnContact (Just contactId) agentConnId Nothing (Just userContactLinkId) 0 currentTs
|
||||
pure $ Contact {contactId, localDisplayName, profile, activeConn, viaGroup = Nothing, createdAt = currentTs, updatedAt = currentTs}
|
||||
|
||||
getLiveSndFileTransfers :: DB.Connection -> User -> IO [SndFileTransfer]
|
||||
@@ -876,7 +892,7 @@ getPendingConnections db User {userId} =
|
||||
<$> DB.queryNamed
|
||||
db
|
||||
[sql|
|
||||
SELECT connection_id, agent_conn_id, conn_level, via_contact,
|
||||
SELECT connection_id, agent_conn_id, conn_level, via_contact, via_user_contact_link,
|
||||
conn_status, conn_type, contact_id, group_member_id, snd_file_id, rcv_file_id, user_contact_link_id, created_at
|
||||
FROM connections
|
||||
WHERE user_id = :user_id
|
||||
@@ -893,7 +909,7 @@ getContactConnections db userId Contact {contactId} =
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact,
|
||||
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link,
|
||||
c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
||||
FROM connections c
|
||||
JOIN contacts ct ON ct.contact_id = c.contact_id
|
||||
@@ -903,14 +919,16 @@ getContactConnections db userId Contact {contactId} =
|
||||
connections [] = throwError $ SEContactNotFound contactId
|
||||
connections rows = pure $ map toConnection rows
|
||||
|
||||
type ConnectionRow = (Int64, ConnId, Int, Maybe Int64, ConnStatus, ConnType, Maybe Int64, Maybe Int64, Maybe Int64, Maybe Int64, Maybe Int64, UTCTime)
|
||||
type EntityIdsRow = (Maybe Int64, Maybe Int64, Maybe Int64, Maybe Int64, Maybe Int64)
|
||||
|
||||
type MaybeConnectionRow = (Maybe Int64, Maybe ConnId, Maybe Int, Maybe Int64, Maybe ConnStatus, Maybe ConnType, Maybe Int64, Maybe Int64, Maybe Int64, Maybe Int64, Maybe Int64, Maybe UTCTime)
|
||||
type ConnectionRow = (Int64, ConnId, Int, Maybe Int64, Maybe Int64, ConnStatus, ConnType) :. EntityIdsRow :. Only UTCTime
|
||||
|
||||
type MaybeConnectionRow = (Maybe Int64, Maybe ConnId, Maybe Int, Maybe Int64, Maybe Int64, Maybe ConnStatus, Maybe ConnType) :. EntityIdsRow :. Only (Maybe UTCTime)
|
||||
|
||||
toConnection :: ConnectionRow -> Connection
|
||||
toConnection (connId, acId, connLevel, viaContact, connStatus, connType, contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId, createdAt) =
|
||||
toConnection ((connId, acId, connLevel, viaContact, viaUserContactLink, connStatus, connType) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. Only createdAt) =
|
||||
let entityId = entityId_ connType
|
||||
in Connection {connId, agentConnId = AgentConnId acId, connLevel, viaContact, connStatus, connType, entityId, createdAt}
|
||||
in Connection {connId, agentConnId = AgentConnId acId, connLevel, viaContact, viaUserContactLink, connStatus, connType, entityId, createdAt}
|
||||
where
|
||||
entityId_ :: ConnType -> Maybe Int64
|
||||
entityId_ ConnContact = contactId
|
||||
@@ -920,8 +938,8 @@ toConnection (connId, acId, connLevel, viaContact, connStatus, connType, contact
|
||||
entityId_ ConnUserContact = userContactLinkId
|
||||
|
||||
toMaybeConnection :: MaybeConnectionRow -> Maybe Connection
|
||||
toMaybeConnection (Just connId, Just agentConnId, Just connLevel, viaContact, Just connStatus, Just connType, contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId, Just createdAt) =
|
||||
Just $ toConnection (connId, agentConnId, connLevel, viaContact, connStatus, connType, contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId, createdAt)
|
||||
toMaybeConnection ((Just connId, Just agentConnId, Just connLevel, viaContact, viaUserContactLink, Just connStatus, Just connType) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. Only (Just createdAt)) =
|
||||
Just $ toConnection ((connId, agentConnId, connLevel, viaContact, viaUserContactLink, connStatus, connType) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. Only createdAt)
|
||||
toMaybeConnection _ = Nothing
|
||||
|
||||
getMatchingContacts :: DB.Connection -> UserId -> Contact -> IO [Contact]
|
||||
@@ -1085,7 +1103,7 @@ getConnectionEntity db user@User {userId, userContactId} agentConnId = do
|
||||
<$> DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT connection_id, agent_conn_id, conn_level, via_contact,
|
||||
SELECT connection_id, agent_conn_id, conn_level, via_contact, via_user_contact_link,
|
||||
conn_status, conn_type, contact_id, group_member_id, snd_file_id, rcv_file_id, user_contact_link_id, created_at
|
||||
FROM connections
|
||||
WHERE user_id = ? AND agent_conn_id = ?
|
||||
@@ -1217,7 +1235,7 @@ getGroupAndMember db User {userId, userContactId} groupMemberId =
|
||||
-- from GroupMember
|
||||
m.group_member_id, m.group_id, m.member_id, m.member_role, m.member_category, m.member_status,
|
||||
m.invited_by, m.local_display_name, m.contact_id, p.display_name, p.full_name, p.image,
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact,
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link,
|
||||
c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
||||
FROM group_members m
|
||||
JOIN contact_profiles p ON p.contact_profile_id = m.contact_profile_id
|
||||
@@ -1375,7 +1393,7 @@ getGroupMembers db User {userId, userContactId} GroupInfo {groupId} = do
|
||||
SELECT
|
||||
m.group_member_id, m.group_id, m.member_id, m.member_role, m.member_category, m.member_status,
|
||||
m.invited_by, m.local_display_name, m.contact_id, p.display_name, p.full_name, p.image,
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact,
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link,
|
||||
c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
||||
FROM group_members m
|
||||
JOIN contact_profiles p ON p.contact_profile_id = m.contact_profile_id
|
||||
@@ -1617,7 +1635,7 @@ createIntroReMember :: DB.Connection -> User -> GroupInfo -> GroupMember -> Memb
|
||||
createIntroReMember db user@User {userId} gInfo@GroupInfo {groupId} _host@GroupMember {memberContactId, activeConn} memInfo@(MemberInfo _ _ memberProfile) groupAgentConnId directAgentConnId = do
|
||||
let cLevel = 1 + maybe 0 (connLevel :: Connection -> Int) activeConn
|
||||
currentTs <- liftIO getCurrentTime
|
||||
Connection {connId = directConnId} <- liftIO $ createContactConnection_ db userId directAgentConnId memberContactId cLevel currentTs
|
||||
Connection {connId = directConnId} <- liftIO $ createMemberContactConnection_ db userId directAgentConnId memberContactId cLevel currentTs
|
||||
(localDisplayName, contactId, memProfileId) <- createContact_ db userId directConnId memberProfile (Just groupId) currentTs
|
||||
liftIO $ do
|
||||
let newMember =
|
||||
@@ -1639,7 +1657,7 @@ createIntroToMemberContact db userId GroupMember {memberContactId = viaContactId
|
||||
let cLevel = 1 + maybe 0 (connLevel :: Connection -> Int) activeConn
|
||||
currentTs <- getCurrentTime
|
||||
void $ createMemberConnection_ db userId groupMemberId groupAgentConnId viaContactId cLevel currentTs
|
||||
Connection {connId = directConnId} <- createContactConnection_ db userId directAgentConnId viaContactId cLevel currentTs
|
||||
Connection {connId = directConnId} <- createMemberContactConnection_ db userId directAgentConnId viaContactId cLevel currentTs
|
||||
contactId <- createMemberContact_ directConnId currentTs
|
||||
updateMember_ contactId currentTs
|
||||
where
|
||||
@@ -1669,7 +1687,7 @@ createIntroToMemberContact db userId GroupMember {memberContactId = viaContactId
|
||||
[":contact_id" := contactId, ":updated_at" := ts, ":group_member_id" := groupMemberId]
|
||||
|
||||
createMemberConnection_ :: DB.Connection -> UserId -> Int64 -> ConnId -> Maybe Int64 -> Int -> UTCTime -> IO Connection
|
||||
createMemberConnection_ db userId groupMemberId = createConnection_ db userId ConnMember (Just groupMemberId)
|
||||
createMemberConnection_ db userId groupMemberId agentConnId viaContact = createConnection_ db userId ConnMember (Just groupMemberId) agentConnId viaContact Nothing
|
||||
|
||||
createContactMember_ :: IsContact a => DB.Connection -> User -> Int64 -> a -> MemberIdRole -> GroupMemberCategory -> GroupMemberStatus -> InvitedBy -> UTCTime -> IO GroupMember
|
||||
createContactMember_ db user groupId userOrContact MemberIdRole {memberId, memberRole} memberCategory memberStatus invitedBy =
|
||||
@@ -1729,7 +1747,7 @@ getViaGroupMember db User {userId, userContactId} Contact {contactId} =
|
||||
-- via GroupMember
|
||||
m.group_member_id, m.group_id, m.member_id, m.member_role, m.member_category, m.member_status,
|
||||
m.invited_by, m.local_display_name, m.contact_id, p.display_name, p.full_name, p.image,
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact,
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link,
|
||||
c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
||||
FROM group_members m
|
||||
JOIN contacts ct ON ct.contact_id = m.contact_id
|
||||
@@ -1761,7 +1779,7 @@ getViaGroupContact db User {userId} GroupMember {groupMemberId} =
|
||||
[sql|
|
||||
SELECT
|
||||
ct.contact_id, ct.local_display_name, p.display_name, p.full_name, p.image, ct.via_group, ct.created_at, ct.updated_at,
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact,
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link,
|
||||
c.conn_status, c.conn_type, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
||||
FROM contacts ct
|
||||
JOIN contact_profiles p ON ct.contact_profile_id = p.contact_profile_id
|
||||
@@ -1886,7 +1904,7 @@ getChatRefByFileId db User {userId} fileId =
|
||||
createSndFileConnection_ :: DB.Connection -> UserId -> Int64 -> ConnId -> IO Connection
|
||||
createSndFileConnection_ db userId fileId agentConnId = do
|
||||
currentTs <- getCurrentTime
|
||||
createConnection_ db userId ConnSndFile (Just fileId) agentConnId Nothing 0 currentTs
|
||||
createConnection_ db userId ConnSndFile (Just fileId) agentConnId Nothing Nothing 0 currentTs
|
||||
|
||||
updateSndFileStatus :: DB.Connection -> SndFileTransfer -> FileStatus -> IO ()
|
||||
updateSndFileStatus db SndFileTransfer {fileId, connId} status = do
|
||||
@@ -2524,7 +2542,7 @@ getDirectChatPreviews_ db User {userId} = do
|
||||
-- Contact
|
||||
ct.contact_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, ct.created_at, ct.updated_at,
|
||||
-- Connection
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.conn_status, c.conn_type,
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.conn_status, c.conn_type,
|
||||
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at,
|
||||
-- ChatStats
|
||||
COALESCE(ChatStats.UnreadCount, 0), COALESCE(ChatStats.MinUnread, 0),
|
||||
@@ -2852,7 +2870,7 @@ getContact db userId contactId =
|
||||
-- Contact
|
||||
ct.contact_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, ct.created_at, ct.updated_at,
|
||||
-- Connection
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.conn_status, c.conn_type,
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.conn_status, c.conn_type,
|
||||
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
|
||||
FROM contacts ct
|
||||
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
||||
|
||||
@@ -669,7 +669,8 @@ data Connection = Connection
|
||||
{ connId :: Int64,
|
||||
agentConnId :: AgentConnId,
|
||||
connLevel :: Int,
|
||||
viaContact :: Maybe Int64,
|
||||
viaContact :: Maybe Int64, -- group member contact ID, if not direct connection
|
||||
viaUserContactLink :: Maybe Int64, -- user contact link ID, if connected via "user address"
|
||||
connType :: ConnType,
|
||||
connStatus :: ConnStatus,
|
||||
entityId :: Maybe Int64, -- contact, group member, file ID or user contact ID
|
||||
|
||||
@@ -8,3 +8,6 @@ safeDecodeUtf8 :: ByteString -> Text
|
||||
safeDecodeUtf8 = decodeUtf8With onError
|
||||
where
|
||||
onError _ _ = Just '?'
|
||||
|
||||
uncurry3 :: (a -> b -> c -> d) -> ((a, b, c) -> d)
|
||||
uncurry3 f ~(a, b, c) = f a b c
|
||||
|
||||
@@ -79,8 +79,8 @@ responseToView testView = \case
|
||||
HSMarkdown -> markdownInfo
|
||||
CRWelcome user -> chatWelcome user
|
||||
CRContactsList cs -> viewContactsList cs
|
||||
CRUserContactLink cReqUri _ -> connReqContact_ "Your chat address:" cReqUri
|
||||
CRUserContactLinkUpdated _ autoAccept -> ["auto_accept " <> if autoAccept then "on" else "off"]
|
||||
CRUserContactLink cReqUri autoAccept autoReply -> connReqContact_ "Your chat address:" cReqUri <> autoAcceptStatus_ autoAccept autoReply
|
||||
CRUserContactLinkUpdated _ autoAccept autoReply -> autoAcceptStatus_ autoAccept autoReply
|
||||
CRContactRequestRejected UserContactRequest {localDisplayName = c} -> [ttyContact c <> ": contact request rejected"]
|
||||
CRGroupCreated g -> viewGroupCreated g
|
||||
CRGroupMembers g -> viewGroupMembers g
|
||||
@@ -361,6 +361,11 @@ connReqContact_ intro cReq =
|
||||
"to delete it: " <> highlight' "/da" <> " (accepted contacts will remain connected)"
|
||||
]
|
||||
|
||||
autoAcceptStatus_ :: Bool -> Maybe MsgContent -> [StyledString]
|
||||
autoAcceptStatus_ autoAccept autoReply =
|
||||
("auto_accept " <> if autoAccept then "on" else "off") :
|
||||
maybe [] ((["auto reply:"] <>) . ttyMsgContent) autoReply
|
||||
|
||||
viewReceivedContactRequest :: ContactName -> Profile -> [StyledString]
|
||||
viewReceivedContactRequest c Profile {fullName} =
|
||||
[ ttyFullName c fullName <> " wants to connect to you!",
|
||||
|
||||
@@ -82,6 +82,7 @@ chatTests = do
|
||||
it "deduplicate contact requests with profile change" testDeduplicateContactRequestsProfileChange
|
||||
it "reject contact and delete contact link" testRejectContactAndDeleteUserContact
|
||||
it "delete connection requests when contact link deleted" testDeleteConnectionRequests
|
||||
it "auto-reply message" testAutoReplyMessage
|
||||
describe "SMP servers" $
|
||||
it "get and set SMP servers" testGetSetSMPServers
|
||||
describe "async connection handshake" $ do
|
||||
@@ -1772,6 +1773,7 @@ testRejectContactAndDeleteUserContact = testChat3 aliceProfile bobProfile cathPr
|
||||
|
||||
alice ##> "/sa"
|
||||
cLink' <- getContactLink alice False
|
||||
alice <## "auto_accept off"
|
||||
cLink' `shouldBe` cLink
|
||||
|
||||
alice ##> "/da"
|
||||
@@ -1803,6 +1805,28 @@ testDeleteConnectionRequests = testChat3 aliceProfile bobProfile cathProfile $
|
||||
cath ##> ("/c " <> cLink')
|
||||
alice <#? cath
|
||||
|
||||
testAutoReplyMessage :: IO ()
|
||||
testAutoReplyMessage = testChat2 aliceProfile bobProfile $
|
||||
\alice bob -> do
|
||||
alice ##> "/ad"
|
||||
cLink <- getContactLink alice True
|
||||
alice ##> "/auto_accept on text hello!"
|
||||
alice <## "auto_accept on"
|
||||
alice <## "auto reply:"
|
||||
alice <## "hello!"
|
||||
|
||||
bob ##> ("/c " <> cLink)
|
||||
bob <## "connection request sent!"
|
||||
alice <## "bob (Bob): accepting contact request..."
|
||||
concurrentlyN_
|
||||
[ do
|
||||
bob <## "alice (Alice): contact is connected"
|
||||
bob <# "alice> hello!",
|
||||
do
|
||||
alice <## "bob (Bob): contact is connected"
|
||||
alice <# "@bob hello!"
|
||||
]
|
||||
|
||||
testGetSetSMPServers :: IO ()
|
||||
testGetSetSMPServers =
|
||||
testChat2 aliceProfile bobProfile $
|
||||
|
||||
+1
-1
@@ -25,6 +25,6 @@ testVerifySchemaDump =
|
||||
void $ readCreateProcess (shell $ "touch " <> schema) ""
|
||||
savedSchema <- readFile schema
|
||||
savedSchema `seq` pure ()
|
||||
void $ readCreateProcess (shell $ "sqlite3 " <> testDB <> " .schema > " <> schema) ""
|
||||
void $ readCreateProcess (shell $ "sqlite3 " <> testDB <> " '.schema --indent' > " <> schema) ""
|
||||
currentSchema <- readFile schema
|
||||
savedSchema `shouldBe` currentSchema
|
||||
|
||||
Reference in New Issue
Block a user