mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2025-06-29 04:39:53 +00:00
core: logging of chat events (#4216)
* core: update simplexmq (persist server errors) * fix * same config * logging * logging 2 * log * log 2 * finally * catch better * more logs * logs * fix * more logging, context from PROHIBITED * warning * more logs * logs3 * logs4 * logs in simplexmq * log locks from simplemq * log queue size * log sendMessagesB in simplexmq * update simplexmq * logs5 * logs6 * logs7 * logs8 * logs8 * logs9 * logs10 * log11 * log12 * fix test * more logs * logging * clean up * refactor * simplify * tags * log level * remove network errors from the log * rename
This commit is contained in:
parent
dae0b63c22
commit
291df6e9d0
16 changed files with 139 additions and 63 deletions
|
@ -35,8 +35,10 @@ import Simplex.Chat.Messages.CIContent
|
||||||
import Simplex.Chat.Protocol (MsgContent (..))
|
import Simplex.Chat.Protocol (MsgContent (..))
|
||||||
import Simplex.Chat.Types
|
import Simplex.Chat.Types
|
||||||
import Simplex.Chat.Types.Shared
|
import Simplex.Chat.Types.Shared
|
||||||
|
import Simplex.Messaging.Agent.Protocol (AgentErrorType (..))
|
||||||
import Simplex.Messaging.Encoding.String
|
import Simplex.Messaging.Encoding.String
|
||||||
import Simplex.Messaging.Util ((<$?>))
|
import Simplex.Messaging.Protocol (BrokerErrorType (..))
|
||||||
|
import Simplex.Messaging.Util (tshow, (<$?>))
|
||||||
|
|
||||||
data DirectoryEvent
|
data DirectoryEvent
|
||||||
= DEContactConnected Contact
|
= DEContactConnected Contact
|
||||||
|
@ -53,6 +55,7 @@ data DirectoryEvent
|
||||||
| DEItemEditIgnored Contact
|
| DEItemEditIgnored Contact
|
||||||
| DEItemDeleteIgnored Contact
|
| DEItemDeleteIgnored Contact
|
||||||
| DEContactCommand Contact ChatItemId ADirectoryCmd
|
| DEContactCommand Contact ChatItemId ADirectoryCmd
|
||||||
|
| DELogChatResponse Text
|
||||||
deriving (Show)
|
deriving (Show)
|
||||||
|
|
||||||
crDirectoryEvent :: ChatResponse -> Maybe DirectoryEvent
|
crDirectoryEvent :: ChatResponse -> Maybe DirectoryEvent
|
||||||
|
@ -77,6 +80,13 @@ crDirectoryEvent = \case
|
||||||
where
|
where
|
||||||
ciId = chatItemId' ci
|
ciId = chatItemId' ci
|
||||||
err = ADC SDRUser DCUnknownCommand
|
err = ADC SDRUser DCUnknownCommand
|
||||||
|
CRMessageError {severity, errorMessage} -> Just $ DELogChatResponse $ "message error: " <> severity <> ", " <> errorMessage
|
||||||
|
CRChatCmdError {chatError} -> Just $ DELogChatResponse $ "chat cmd error: " <> tshow chatError
|
||||||
|
CRChatError {chatError} -> case chatError of
|
||||||
|
ChatErrorAgent {agentError = BROKER _ NETWORK} -> Nothing
|
||||||
|
ChatErrorAgent {agentError = BROKER _ TIMEOUT} -> Nothing
|
||||||
|
_ -> Just $ DELogChatResponse $ "chat error: " <> tshow chatError
|
||||||
|
CRChatErrors {chatErrors} -> Just $ DELogChatResponse $ "chat errors: " <> T.intercalate ", " (map tshow chatErrors)
|
||||||
_ -> Nothing
|
_ -> Nothing
|
||||||
|
|
||||||
data DirectoryRole = DRUser | DRSuperUser
|
data DirectoryRole = DRUser | DRSuperUser
|
||||||
|
|
|
@ -102,6 +102,7 @@ directoryService st DirectoryOpts {superUsers, serviceName, searchResults, testi
|
||||||
case sUser of
|
case sUser of
|
||||||
SDRUser -> deUserCommand env ct ciId cmd
|
SDRUser -> deUserCommand env ct ciId cmd
|
||||||
SDRSuperUser -> deSuperUserCommand ct ciId cmd
|
SDRSuperUser -> deSuperUserCommand ct ciId cmd
|
||||||
|
DELogChatResponse r -> logInfo r
|
||||||
where
|
where
|
||||||
withSuperUsers action = void . forkIO $ forM_ superUsers $ \KnownContact {contactId} -> action contactId
|
withSuperUsers action = void . forkIO $ forM_ superUsers $ \KnownContact {contactId} -> action contactId
|
||||||
notifySuperUsers s = withSuperUsers $ \contactId -> sendMessage' cc contactId s
|
notifySuperUsers s = withSuperUsers $ \contactId -> sendMessage' cc contactId s
|
||||||
|
|
|
@ -39,8 +39,8 @@ import Data.Text (Text)
|
||||||
import Simplex.Chat.Types
|
import Simplex.Chat.Types
|
||||||
import Simplex.Messaging.Encoding.String
|
import Simplex.Messaging.Encoding.String
|
||||||
import Simplex.Messaging.Util (ifM)
|
import Simplex.Messaging.Util (ifM)
|
||||||
import System.IO (Handle, IOMode (..), openFile, BufferMode (..), hSetBuffering)
|
import System.Directory (doesFileExist, renameFile)
|
||||||
import System.Directory (renameFile, doesFileExist)
|
import System.IO (BufferMode (..), Handle, IOMode (..), hSetBuffering, openFile)
|
||||||
|
|
||||||
data DirectoryStore = DirectoryStore
|
data DirectoryStore = DirectoryStore
|
||||||
{ groupRegs :: TVar [GroupReg],
|
{ groupRegs :: TVar [GroupReg],
|
||||||
|
@ -112,7 +112,7 @@ addGroupReg st ct GroupInfo {groupId} grStatus = do
|
||||||
let ugrId = 1 + foldl' maxUgrId 0 grs
|
let ugrId = 1 + foldl' maxUgrId 0 grs
|
||||||
grData' = grData {userGroupRegId_ = ugrId}
|
grData' = grData {userGroupRegId_ = ugrId}
|
||||||
gr' = gr {userGroupRegId = ugrId}
|
gr' = gr {userGroupRegId = ugrId}
|
||||||
in (grData', gr' : grs)
|
in (grData', gr' : grs)
|
||||||
ctId = contactId' ct
|
ctId = contactId' ct
|
||||||
maxUgrId mx GroupReg {dbContactId, userGroupRegId}
|
maxUgrId mx GroupReg {dbContactId, userGroupRegId}
|
||||||
| dbContactId == ctId && userGroupRegId > mx = userGroupRegId
|
| dbContactId == ctId && userGroupRegId > mx = userGroupRegId
|
||||||
|
@ -311,14 +311,15 @@ readDirectoryData f =
|
||||||
Right r -> case r of
|
Right r -> case r of
|
||||||
GRCreate gr@GroupRegData {dbGroupId_ = gId} -> do
|
GRCreate gr@GroupRegData {dbGroupId_ = gId} -> do
|
||||||
when (isJust $ M.lookup gId m) $
|
when (isJust $ M.lookup gId m) $
|
||||||
putStrLn $ "Warning: duplicate group with ID " <> show gId <> ", group replaced."
|
putStrLn $
|
||||||
|
"Warning: duplicate group with ID " <> show gId <> ", group replaced."
|
||||||
pure $ M.insert gId gr m
|
pure $ M.insert gId gr m
|
||||||
GRUpdateStatus gId groupRegStatus_ -> case M.lookup gId m of
|
GRUpdateStatus gId groupRegStatus_ -> case M.lookup gId m of
|
||||||
Just gr -> pure $ M.insert gId gr {groupRegStatus_} m
|
Just gr -> pure $ M.insert gId gr {groupRegStatus_} m
|
||||||
Nothing -> m <$ putStrLn ("Warning: no group with ID " <> show gId <>", status update ignored.")
|
Nothing -> m <$ putStrLn ("Warning: no group with ID " <> show gId <> ", status update ignored.")
|
||||||
GRUpdateOwner gId grOwnerId -> case M.lookup gId m of
|
GRUpdateOwner gId grOwnerId -> case M.lookup gId m of
|
||||||
Just gr -> pure $ M.insert gId gr {dbOwnerMemberId_ = Just grOwnerId} m
|
Just gr -> pure $ M.insert gId gr {dbOwnerMemberId_ = Just grOwnerId} m
|
||||||
Nothing -> m <$ putStrLn ("Warning: no group with ID " <> show gId <>", owner update ignored.")
|
Nothing -> m <$ putStrLn ("Warning: no group with ID " <> show gId <> ", owner update ignored.")
|
||||||
|
|
||||||
writeDirectoryData :: FilePath -> [GroupRegData] -> IO Handle
|
writeDirectoryData :: FilePath -> [GroupRegData] -> IO Handle
|
||||||
writeDirectoryData f grs = do
|
writeDirectoryData f grs = do
|
||||||
|
|
|
@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd
|
||||||
source-repository-package
|
source-repository-package
|
||||||
type: git
|
type: git
|
||||||
location: https://github.com/simplex-chat/simplexmq.git
|
location: https://github.com/simplex-chat/simplexmq.git
|
||||||
tag: e7a73a4c89ed02e248e2d77e267037c9d4433820
|
tag: bd67844169d2206d8543c01e6ed966315115b0e3
|
||||||
|
|
||||||
source-repository-package
|
source-repository-package
|
||||||
type: git
|
type: git
|
||||||
|
|
23
package.yaml
23
package.yaml
|
@ -152,12 +152,31 @@ tests:
|
||||||
ghc-options:
|
ghc-options:
|
||||||
# - -haddock
|
# - -haddock
|
||||||
- -O2
|
- -O2
|
||||||
- -Wall
|
- -Weverything
|
||||||
|
- -Wno-missing-exported-signatures
|
||||||
|
- -Wno-missing-import-lists
|
||||||
|
- -Wno-missed-specialisations
|
||||||
|
- -Wno-all-missed-specialisations
|
||||||
|
- -Wno-unsafe
|
||||||
|
- -Wno-safe
|
||||||
|
- -Wno-missing-local-signatures
|
||||||
|
- -Wno-missing-kind-signatures
|
||||||
|
- -Wno-missing-deriving-strategies
|
||||||
|
- -Wno-monomorphism-restriction
|
||||||
|
- -Wno-prepositive-qualified-module
|
||||||
|
- -Wno-unused-packages
|
||||||
|
- -Wno-implicit-prelude
|
||||||
|
- -Wno-missing-safe-haskell-mode
|
||||||
|
- -Wno-missing-export-lists
|
||||||
|
- -Wno-partial-fields
|
||||||
- -Wcompat
|
- -Wcompat
|
||||||
|
- -Werror=incomplete-record-updates
|
||||||
- -Werror=incomplete-patterns
|
- -Werror=incomplete-patterns
|
||||||
|
- -Werror=missing-methods
|
||||||
|
- -Werror=incomplete-uni-patterns
|
||||||
|
- -Werror=tabs
|
||||||
- -Wredundant-constraints
|
- -Wredundant-constraints
|
||||||
- -Wincomplete-record-updates
|
- -Wincomplete-record-updates
|
||||||
- -Wincomplete-uni-patterns
|
|
||||||
- -Wunused-type-patterns
|
- -Wunused-type-patterns
|
||||||
|
|
||||||
default-extensions:
|
default-extensions:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"https://github.com/simplex-chat/simplexmq.git"."e7a73a4c89ed02e248e2d77e267037c9d4433820" = "1zrsvnx8qnkvlxhkikl97bmi5nyian8wq20pn330159cviihfxl1";
|
"https://github.com/simplex-chat/simplexmq.git"."bd67844169d2206d8543c01e6ed966315115b0e3" = "1g218q15hrg21h8gyidavfys5zx8dzmxq7iwfm5bfaw71grpd7pn";
|
||||||
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
|
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
|
||||||
"https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d";
|
"https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d";
|
||||||
"https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl";
|
"https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl";
|
||||||
|
|
|
@ -190,7 +190,7 @@ library
|
||||||
src
|
src
|
||||||
default-extensions:
|
default-extensions:
|
||||||
StrictData
|
StrictData
|
||||||
ghc-options: -O2 -Wall -Wcompat -Werror=incomplete-patterns -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns -Wunused-type-patterns
|
ghc-options: -O2 -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-missing-kind-signatures -Wno-missing-deriving-strategies -Wno-monomorphism-restriction -Wno-prepositive-qualified-module -Wno-unused-packages -Wno-implicit-prelude -Wno-missing-safe-haskell-mode -Wno-missing-export-lists -Wno-partial-fields -Wcompat -Werror=incomplete-record-updates -Werror=incomplete-patterns -Werror=missing-methods -Werror=incomplete-uni-patterns -Werror=tabs -Wredundant-constraints -Wincomplete-record-updates -Wunused-type-patterns
|
||||||
build-depends:
|
build-depends:
|
||||||
aeson ==2.2.*
|
aeson ==2.2.*
|
||||||
, ansi-terminal >=0.10 && <0.12
|
, ansi-terminal >=0.10 && <0.12
|
||||||
|
@ -252,7 +252,7 @@ executable simplex-bot
|
||||||
apps/simplex-bot
|
apps/simplex-bot
|
||||||
default-extensions:
|
default-extensions:
|
||||||
StrictData
|
StrictData
|
||||||
ghc-options: -O2 -Wall -Wcompat -Werror=incomplete-patterns -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns -Wunused-type-patterns -threaded
|
ghc-options: -O2 -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-missing-kind-signatures -Wno-missing-deriving-strategies -Wno-monomorphism-restriction -Wno-prepositive-qualified-module -Wno-unused-packages -Wno-implicit-prelude -Wno-missing-safe-haskell-mode -Wno-missing-export-lists -Wno-partial-fields -Wcompat -Werror=incomplete-record-updates -Werror=incomplete-patterns -Werror=missing-methods -Werror=incomplete-uni-patterns -Werror=tabs -Wredundant-constraints -Wincomplete-record-updates -Wunused-type-patterns -threaded
|
||||||
build-depends:
|
build-depends:
|
||||||
aeson ==2.2.*
|
aeson ==2.2.*
|
||||||
, ansi-terminal >=0.10 && <0.12
|
, ansi-terminal >=0.10 && <0.12
|
||||||
|
@ -315,7 +315,7 @@ executable simplex-bot-advanced
|
||||||
apps/simplex-bot-advanced
|
apps/simplex-bot-advanced
|
||||||
default-extensions:
|
default-extensions:
|
||||||
StrictData
|
StrictData
|
||||||
ghc-options: -O2 -Wall -Wcompat -Werror=incomplete-patterns -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns -Wunused-type-patterns -threaded
|
ghc-options: -O2 -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-missing-kind-signatures -Wno-missing-deriving-strategies -Wno-monomorphism-restriction -Wno-prepositive-qualified-module -Wno-unused-packages -Wno-implicit-prelude -Wno-missing-safe-haskell-mode -Wno-missing-export-lists -Wno-partial-fields -Wcompat -Werror=incomplete-record-updates -Werror=incomplete-patterns -Werror=missing-methods -Werror=incomplete-uni-patterns -Werror=tabs -Wredundant-constraints -Wincomplete-record-updates -Wunused-type-patterns -threaded
|
||||||
build-depends:
|
build-depends:
|
||||||
aeson ==2.2.*
|
aeson ==2.2.*
|
||||||
, ansi-terminal >=0.10 && <0.12
|
, ansi-terminal >=0.10 && <0.12
|
||||||
|
@ -381,7 +381,7 @@ executable simplex-broadcast-bot
|
||||||
Broadcast.Bot
|
Broadcast.Bot
|
||||||
Broadcast.Options
|
Broadcast.Options
|
||||||
Paths_simplex_chat
|
Paths_simplex_chat
|
||||||
ghc-options: -O2 -Wall -Wcompat -Werror=incomplete-patterns -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns -Wunused-type-patterns -threaded
|
ghc-options: -O2 -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-missing-kind-signatures -Wno-missing-deriving-strategies -Wno-monomorphism-restriction -Wno-prepositive-qualified-module -Wno-unused-packages -Wno-implicit-prelude -Wno-missing-safe-haskell-mode -Wno-missing-export-lists -Wno-partial-fields -Wcompat -Werror=incomplete-record-updates -Werror=incomplete-patterns -Werror=missing-methods -Werror=incomplete-uni-patterns -Werror=tabs -Wredundant-constraints -Wincomplete-record-updates -Wunused-type-patterns -threaded
|
||||||
build-depends:
|
build-depends:
|
||||||
aeson ==2.2.*
|
aeson ==2.2.*
|
||||||
, ansi-terminal >=0.10 && <0.12
|
, ansi-terminal >=0.10 && <0.12
|
||||||
|
@ -445,7 +445,7 @@ executable simplex-chat
|
||||||
apps/simplex-chat
|
apps/simplex-chat
|
||||||
default-extensions:
|
default-extensions:
|
||||||
StrictData
|
StrictData
|
||||||
ghc-options: -O2 -Wall -Wcompat -Werror=incomplete-patterns -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns -Wunused-type-patterns -threaded
|
ghc-options: -O2 -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-missing-kind-signatures -Wno-missing-deriving-strategies -Wno-monomorphism-restriction -Wno-prepositive-qualified-module -Wno-unused-packages -Wno-implicit-prelude -Wno-missing-safe-haskell-mode -Wno-missing-export-lists -Wno-partial-fields -Wcompat -Werror=incomplete-record-updates -Werror=incomplete-patterns -Werror=missing-methods -Werror=incomplete-uni-patterns -Werror=tabs -Wredundant-constraints -Wincomplete-record-updates -Wunused-type-patterns -threaded
|
||||||
build-depends:
|
build-depends:
|
||||||
aeson ==2.2.*
|
aeson ==2.2.*
|
||||||
, ansi-terminal >=0.10 && <0.12
|
, ansi-terminal >=0.10 && <0.12
|
||||||
|
@ -515,7 +515,7 @@ executable simplex-directory-service
|
||||||
Directory.Service
|
Directory.Service
|
||||||
Directory.Store
|
Directory.Store
|
||||||
Paths_simplex_chat
|
Paths_simplex_chat
|
||||||
ghc-options: -O2 -Wall -Wcompat -Werror=incomplete-patterns -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns -Wunused-type-patterns -threaded
|
ghc-options: -O2 -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-missing-kind-signatures -Wno-missing-deriving-strategies -Wno-monomorphism-restriction -Wno-prepositive-qualified-module -Wno-unused-packages -Wno-implicit-prelude -Wno-missing-safe-haskell-mode -Wno-missing-export-lists -Wno-partial-fields -Wcompat -Werror=incomplete-record-updates -Werror=incomplete-patterns -Werror=missing-methods -Werror=incomplete-uni-patterns -Werror=tabs -Wredundant-constraints -Wincomplete-record-updates -Wunused-type-patterns -threaded
|
||||||
build-depends:
|
build-depends:
|
||||||
aeson ==2.2.*
|
aeson ==2.2.*
|
||||||
, ansi-terminal >=0.10 && <0.12
|
, ansi-terminal >=0.10 && <0.12
|
||||||
|
@ -610,7 +610,7 @@ test-suite simplex-chat-test
|
||||||
apps/simplex-directory-service/src
|
apps/simplex-directory-service/src
|
||||||
default-extensions:
|
default-extensions:
|
||||||
StrictData
|
StrictData
|
||||||
ghc-options: -O2 -Wall -Wcompat -Werror=incomplete-patterns -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns -Wunused-type-patterns -threaded
|
ghc-options: -O2 -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-missing-kind-signatures -Wno-missing-deriving-strategies -Wno-monomorphism-restriction -Wno-prepositive-qualified-module -Wno-unused-packages -Wno-implicit-prelude -Wno-missing-safe-haskell-mode -Wno-missing-export-lists -Wno-partial-fields -Wcompat -Werror=incomplete-record-updates -Werror=incomplete-patterns -Werror=missing-methods -Werror=incomplete-uni-patterns -Werror=tabs -Wredundant-constraints -Wincomplete-record-updates -Wunused-type-patterns -threaded
|
||||||
build-depends:
|
build-depends:
|
||||||
QuickCheck ==2.14.*
|
QuickCheck ==2.14.*
|
||||||
, aeson ==2.2.*
|
, aeson ==2.2.*
|
||||||
|
|
|
@ -229,6 +229,7 @@ newChatController
|
||||||
smpAgent <- getSMPAgentClient aCfg {tbqSize} servers agentStore backgroundMode
|
smpAgent <- getSMPAgentClient aCfg {tbqSize} servers agentStore backgroundMode
|
||||||
agentAsync <- newTVarIO Nothing
|
agentAsync <- newTVarIO Nothing
|
||||||
random <- liftIO C.newRandom
|
random <- liftIO C.newRandom
|
||||||
|
eventSeq <- newTVarIO 0
|
||||||
inputQ <- newTBQueueIO tbqSize
|
inputQ <- newTBQueueIO tbqSize
|
||||||
outputQ <- newTBQueueIO tbqSize
|
outputQ <- newTBQueueIO tbqSize
|
||||||
connNetworkStatuses <- atomically TM.empty
|
connNetworkStatuses <- atomically TM.empty
|
||||||
|
@ -266,6 +267,7 @@ newChatController
|
||||||
chatStore,
|
chatStore,
|
||||||
chatStoreChanged,
|
chatStoreChanged,
|
||||||
random,
|
random,
|
||||||
|
eventSeq,
|
||||||
inputQ,
|
inputQ,
|
||||||
outputQ,
|
outputQ,
|
||||||
connNetworkStatuses,
|
connNetworkStatuses,
|
||||||
|
@ -3317,7 +3319,10 @@ deleteGroupLink_ user gInfo conn = do
|
||||||
agentSubscriber :: CM' ()
|
agentSubscriber :: CM' ()
|
||||||
agentSubscriber = do
|
agentSubscriber = do
|
||||||
q <- asks $ subQ . smpAgent
|
q <- asks $ subQ . smpAgent
|
||||||
forever $ atomically (readTBQueue q) >>= process
|
forever (atomically (readTBQueue q) >>= process)
|
||||||
|
`E.catchAny` \e -> do
|
||||||
|
toView' $ CRChatError Nothing $ ChatErrorAgent (CRITICAL True $ "Message reception stopped: " <> show e) Nothing
|
||||||
|
E.throwIO e
|
||||||
where
|
where
|
||||||
process :: (ACorrId, EntityId, APartyCmd 'Agent) -> CM' ()
|
process :: (ACorrId, EntityId, APartyCmd 'Agent) -> CM' ()
|
||||||
process (corrId, entId, APC e msg) = run $ case e of
|
process (corrId, entId, APC e msg) = run $ case e of
|
||||||
|
@ -3937,7 +3942,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||||
-- TODO only acknowledge without saving message?
|
-- TODO only acknowledge without saving message?
|
||||||
-- probably this branch is never executed, so there should be no reason
|
-- probably this branch is never executed, so there should be no reason
|
||||||
-- to save message if contact hasn't been created yet - chat item isn't created anyway
|
-- to save message if contact hasn't been created yet - chat item isn't created anyway
|
||||||
withAckMessage' agentConnId meta $
|
withAckMessage' "new contact msg" agentConnId meta $
|
||||||
void $
|
void $
|
||||||
saveDirectRcvMSG conn meta msgBody
|
saveDirectRcvMSG conn meta msgBody
|
||||||
SENT msgId _proxy ->
|
SENT msgId _proxy ->
|
||||||
|
@ -3968,14 +3973,18 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||||
forM_ contData $ \(hostConnId, xGrpMemIntroCont) ->
|
forM_ contData $ \(hostConnId, xGrpMemIntroCont) ->
|
||||||
sendXGrpMemInv hostConnId (Just directConnReq) xGrpMemIntroCont
|
sendXGrpMemInv hostConnId (Just directConnReq) xGrpMemIntroCont
|
||||||
CRContactUri _ -> throwChatError $ CECommandError "unexpected ConnectionRequestUri type"
|
CRContactUri _ -> throwChatError $ CECommandError "unexpected ConnectionRequestUri type"
|
||||||
MSG msgMeta _msgFlags msgBody ->
|
MSG msgMeta _msgFlags msgBody -> do
|
||||||
withAckMessage agentConnId msgMeta True $ do
|
tags <- newTVarIO []
|
||||||
|
withAckMessage "contact msg" agentConnId msgMeta True (Just tags) $ \eInfo -> do
|
||||||
let MsgMeta {pqEncryption} = msgMeta
|
let MsgMeta {pqEncryption} = msgMeta
|
||||||
(ct', conn') <- updateContactPQRcv user ct conn pqEncryption
|
(ct', conn') <- updateContactPQRcv user ct conn pqEncryption
|
||||||
checkIntegrityCreateItem (CDDirectRcv ct') msgMeta `catchChatError` \_ -> pure ()
|
checkIntegrityCreateItem (CDDirectRcv ct') msgMeta `catchChatError` \_ -> pure ()
|
||||||
(conn'', msg@RcvMessage {chatMsgEvent = ACME _ event}) <- saveDirectRcvMSG conn' msgMeta msgBody
|
(conn'', msg@RcvMessage {chatMsgEvent = ACME _ event}) <- saveDirectRcvMSG conn' msgMeta msgBody
|
||||||
|
let tag = toCMEventTag event
|
||||||
|
atomically $ writeTVar tags [tshow tag]
|
||||||
|
logInfo $ "contact msg=" <> tshow tag <> " " <> eInfo
|
||||||
let ct'' = ct' {activeConn = Just conn''} :: Contact
|
let ct'' = ct' {activeConn = Just conn''} :: Contact
|
||||||
assertDirectAllowed user MDRcv ct'' $ toCMEventTag event
|
assertDirectAllowed user MDRcv ct'' tag
|
||||||
case event of
|
case event of
|
||||||
XMsgNew mc -> newContentMessage ct'' mc msg msgMeta
|
XMsgNew mc -> newContentMessage ct'' mc msg msgMeta
|
||||||
XMsgFileDescr sharedMsgId fileDescr -> messageFileDescription ct'' sharedMsgId fileDescr
|
XMsgFileDescr sharedMsgId fileDescr -> messageFileDescription ct'' sharedMsgId fileDescr
|
||||||
|
@ -4000,9 +4009,9 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||||
BFileChunk sharedMsgId chunk -> bFileChunk ct'' sharedMsgId chunk msgMeta
|
BFileChunk sharedMsgId chunk -> bFileChunk ct'' sharedMsgId chunk msgMeta
|
||||||
_ -> messageError $ "unsupported message: " <> T.pack (show event)
|
_ -> messageError $ "unsupported message: " <> T.pack (show event)
|
||||||
let Contact {chatSettings = ChatSettings {sendRcpts}} = ct''
|
let Contact {chatSettings = ChatSettings {sendRcpts}} = ct''
|
||||||
pure $ fromMaybe (sendRcptsContacts user) sendRcpts && hasDeliveryReceipt (toCMEventTag event)
|
pure $ fromMaybe (sendRcptsContacts user) sendRcpts && hasDeliveryReceipt tag
|
||||||
RCVD msgMeta msgRcpt ->
|
RCVD msgMeta msgRcpt ->
|
||||||
withAckMessage' agentConnId msgMeta $
|
withAckMessage' "contact rcvd" agentConnId msgMeta $
|
||||||
directMsgReceived ct conn msgMeta msgRcpt
|
directMsgReceived ct conn msgMeta msgRcpt
|
||||||
CONF confId pqSupport _ connInfo -> do
|
CONF confId pqSupport _ connInfo -> do
|
||||||
conn' <- processCONFpqSupport conn pqSupport
|
conn' <- processCONFpqSupport conn pqSupport
|
||||||
|
@ -4381,19 +4390,26 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||||
void $ sendDirectMemberMessage imConn (XGrpMemCon memberId) groupId
|
void $ sendDirectMemberMessage imConn (XGrpMemCon memberId) groupId
|
||||||
_ -> messageWarning "sendXGrpMemCon: member category GCPreMember or GCPostMember is expected"
|
_ -> messageWarning "sendXGrpMemCon: member category GCPreMember or GCPostMember is expected"
|
||||||
MSG msgMeta _msgFlags msgBody -> do
|
MSG msgMeta _msgFlags msgBody -> do
|
||||||
withAckMessage agentConnId msgMeta True $ do
|
tags <- newTVarIO []
|
||||||
|
withAckMessage "group msg" agentConnId msgMeta True (Just tags) $ \eInfo -> do
|
||||||
checkIntegrityCreateItem (CDGroupRcv gInfo m) msgMeta `catchChatError` \_ -> pure ()
|
checkIntegrityCreateItem (CDGroupRcv gInfo m) msgMeta `catchChatError` \_ -> pure ()
|
||||||
forM_ aChatMsgs $ \case
|
forM_ aChatMsgs $ \case
|
||||||
Right (ACMsg _ chatMsg) ->
|
Right (ACMsg _ chatMsg) ->
|
||||||
processEvent chatMsg `catchChatError` \e -> toView $ CRChatError (Just user) e
|
processEvent tags eInfo chatMsg `catchChatError` \e -> toView $ CRChatError (Just user) e
|
||||||
Left e -> toView $ CRChatError (Just user) (ChatError . CEException $ "error parsing chat message: " <> e)
|
Left e -> do
|
||||||
forwardMsg_ `catchChatError` \_ -> pure ()
|
atomically $ modifyTVar' tags ("error" :)
|
||||||
|
logInfo $ "group msg=error " <> eInfo <> " " <> tshow e
|
||||||
|
toView $ CRChatError (Just user) (ChatError . CEException $ "error parsing chat message: " <> e)
|
||||||
|
forwardMsg_ `catchChatError` (toView . CRChatError (Just user))
|
||||||
checkSendRcpt $ rights aChatMsgs
|
checkSendRcpt $ rights aChatMsgs
|
||||||
where
|
where
|
||||||
aChatMsgs = parseChatMessages msgBody
|
aChatMsgs = parseChatMessages msgBody
|
||||||
brokerTs = metaBrokerTs msgMeta
|
brokerTs = metaBrokerTs msgMeta
|
||||||
processEvent :: MsgEncodingI e => ChatMessage e -> CM ()
|
processEvent :: TVar [Text] -> Text -> MsgEncodingI e => ChatMessage e -> CM ()
|
||||||
processEvent chatMsg = do
|
processEvent tags eInfo chatMsg@ChatMessage {chatMsgEvent} = do
|
||||||
|
let tag = toCMEventTag chatMsgEvent
|
||||||
|
atomically $ modifyTVar' tags (tshow tag :)
|
||||||
|
logInfo $ "group msg=" <> tshow tag <> " " <> eInfo
|
||||||
(m', conn', msg@RcvMessage {chatMsgEvent = ACME _ event}) <- saveGroupRcvMsg user groupId m conn msgMeta msgBody chatMsg
|
(m', conn', msg@RcvMessage {chatMsgEvent = ACME _ event}) <- saveGroupRcvMsg user groupId m conn msgMeta msgBody chatMsg
|
||||||
case event of
|
case event of
|
||||||
XMsgNew mc -> memberCanSend m' $ newGroupContentMessage gInfo m' mc msg brokerTs False
|
XMsgNew mc -> memberCanSend m' $ newGroupContentMessage gInfo m' mc msg brokerTs False
|
||||||
|
@ -4424,7 +4440,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||||
XInfoProbeCheck probeHash -> xInfoProbeCheck (COMGroupMember m') probeHash
|
XInfoProbeCheck probeHash -> xInfoProbeCheck (COMGroupMember m') probeHash
|
||||||
XInfoProbeOk probe -> xInfoProbeOk (COMGroupMember m') probe
|
XInfoProbeOk probe -> xInfoProbeOk (COMGroupMember m') probe
|
||||||
BFileChunk sharedMsgId chunk -> bFileChunkGroup gInfo sharedMsgId chunk msgMeta
|
BFileChunk sharedMsgId chunk -> bFileChunkGroup gInfo sharedMsgId chunk msgMeta
|
||||||
_ -> messageError $ "unsupported message: " <> T.pack (show event)
|
_ -> messageError $ "unsupported message: " <> tshow event
|
||||||
checkSendRcpt :: [AChatMessage] -> CM Bool
|
checkSendRcpt :: [AChatMessage] -> CM Bool
|
||||||
checkSendRcpt aMsgs = do
|
checkSendRcpt aMsgs = do
|
||||||
currentMemCount <- withStore' $ \db -> getGroupCurrentMembersCount db user gInfo
|
currentMemCount <- withStore' $ \db -> getGroupCurrentMembersCount db user gInfo
|
||||||
|
@ -4458,7 +4474,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||||
sendGroupMessage' user gInfo ms msg
|
sendGroupMessage' user gInfo ms msg
|
||||||
_ -> pure ()
|
_ -> pure ()
|
||||||
RCVD msgMeta msgRcpt ->
|
RCVD msgMeta msgRcpt ->
|
||||||
withAckMessage' agentConnId msgMeta $
|
withAckMessage' "group rcvd" agentConnId msgMeta $
|
||||||
groupMsgReceived gInfo m conn msgMeta msgRcpt
|
groupMsgReceived gInfo m conn msgMeta msgRcpt
|
||||||
SENT msgId proxy -> do
|
SENT msgId proxy -> do
|
||||||
sentMsgDeliveryEvent conn msgId
|
sentMsgDeliveryEvent conn msgId
|
||||||
|
@ -4582,7 +4598,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||||
lookupChatItemByFileId db vr user fileId
|
lookupChatItemByFileId db vr user fileId
|
||||||
toView $ CRSndFileRcvCancelled user ci ft
|
toView $ CRSndFileRcvCancelled user ci ft
|
||||||
_ -> throwChatError $ CEFileSend fileId err
|
_ -> throwChatError $ CEFileSend fileId err
|
||||||
MSG meta _ _ -> withAckMessage' agentConnId meta $ pure ()
|
MSG meta _ _ ->
|
||||||
|
withAckMessage' "file msg" agentConnId meta $ pure ()
|
||||||
OK ->
|
OK ->
|
||||||
-- [async agent commands] continuation on receiving OK
|
-- [async agent commands] continuation on receiving OK
|
||||||
when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData -> pure ()
|
when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData -> pure ()
|
||||||
|
@ -4658,7 +4675,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||||
RcvChunkOk ->
|
RcvChunkOk ->
|
||||||
if B.length chunk /= fromInteger chunkSize
|
if B.length chunk /= fromInteger chunkSize
|
||||||
then badRcvFileChunk ft "incorrect chunk size"
|
then badRcvFileChunk ft "incorrect chunk size"
|
||||||
else withAckMessage' agentConnId meta $ appendFileChunk ft chunkNo chunk False
|
else withAckMessage' "file msg" agentConnId meta $ appendFileChunk ft chunkNo chunk False
|
||||||
RcvChunkFinal ->
|
RcvChunkFinal ->
|
||||||
if B.length chunk > fromInteger chunkSize
|
if B.length chunk > fromInteger chunkSize
|
||||||
then badRcvFileChunk ft "incorrect chunk size"
|
then badRcvFileChunk ft "incorrect chunk size"
|
||||||
|
@ -4672,7 +4689,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||||
getChatItemByFileId db vr user fileId
|
getChatItemByFileId db vr user fileId
|
||||||
toView $ CRRcvFileComplete user ci
|
toView $ CRRcvFileComplete user ci
|
||||||
forM_ conn_ $ \conn -> deleteAgentConnectionAsync user (aConnId conn)
|
forM_ conn_ $ \conn -> deleteAgentConnectionAsync user (aConnId conn)
|
||||||
RcvChunkDuplicate -> withAckMessage' agentConnId meta $ pure ()
|
RcvChunkDuplicate -> withAckMessage' "file msg" agentConnId meta $ pure ()
|
||||||
RcvChunkError -> badRcvFileChunk ft $ "incorrect chunk number " <> show chunkNo
|
RcvChunkError -> badRcvFileChunk ft $ "incorrect chunk number " <> show chunkNo
|
||||||
|
|
||||||
processUserContactRequest :: ACommand 'Agent e -> ConnectionEntity -> Connection -> UserContact -> CM ()
|
processUserContactRequest :: ACommand 'Agent e -> ConnectionEntity -> Connection -> UserContact -> CM ()
|
||||||
|
@ -4756,25 +4773,45 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
|
||||||
withStore' $ \db -> updateCommandStatus db user cmdId CSError
|
withStore' $ \db -> updateCommandStatus db user cmdId CSError
|
||||||
throwChatError . CEAgentCommandError $ msg
|
throwChatError . CEAgentCommandError $ msg
|
||||||
|
|
||||||
withAckMessage' :: ConnId -> MsgMeta -> CM () -> CM ()
|
withAckMessage' :: Text -> ConnId -> MsgMeta -> CM () -> CM ()
|
||||||
withAckMessage' cId msgMeta action = do
|
withAckMessage' label cId msgMeta action = do
|
||||||
withAckMessage cId msgMeta False $ action $> False
|
withAckMessage label cId msgMeta False Nothing $ \_ -> action $> False
|
||||||
|
|
||||||
withAckMessage :: ConnId -> MsgMeta -> Bool -> CM Bool -> CM ()
|
withAckMessage :: Text -> ConnId -> MsgMeta -> Bool -> Maybe (TVar [Text]) -> (Text -> CM Bool) -> CM ()
|
||||||
withAckMessage cId msgMeta showCritical action =
|
withAckMessage label cId msgMeta showCritical tags action = do
|
||||||
-- [async agent commands] command should be asynchronous
|
-- [async agent commands] command should be asynchronous
|
||||||
-- TODO catching error and sending ACK after an error, particularly if it is a database error, will result in the message not processed (and no notification to the user).
|
-- TODO catching error and sending ACK after an error, particularly if it is a database error, will result in the message not processed (and no notification to the user).
|
||||||
-- Possible solutions are:
|
-- Possible solutions are:
|
||||||
-- 1) retry processing several times
|
-- 1) retry processing several times
|
||||||
-- 2) stabilize database
|
-- 2) stabilize database
|
||||||
-- 3) show screen of death to the user asking to restart
|
-- 3) show screen of death to the user asking to restart
|
||||||
tryChatError action >>= \case
|
eInfo <- eventInfo
|
||||||
Right withRcpt -> ackMsg msgMeta $ if withRcpt then Just "" else Nothing
|
logInfo $ label <> ": " <> eInfo
|
||||||
|
tryChatError (action eInfo) >>= \case
|
||||||
|
Right withRcpt ->
|
||||||
|
withLog (eInfo <> " ok") $ ackMsg msgMeta $ if withRcpt then Just "" else Nothing
|
||||||
-- If showCritical is True, then these errors don't result in ACK and show user visible alert
|
-- If showCritical is True, then these errors don't result in ACK and show user visible alert
|
||||||
-- This prevents losing the message that failed to be processed.
|
-- This prevents losing the message that failed to be processed.
|
||||||
Left (ChatErrorStore SEDBBusyError {message}) | showCritical -> throwError $ ChatErrorAgent (CRITICAL True message) Nothing
|
Left (ChatErrorStore SEDBBusyError {message}) | showCritical -> throwError $ ChatErrorAgent (CRITICAL True message) Nothing
|
||||||
Left e -> ackMsg msgMeta Nothing >> throwError e
|
Left e -> do
|
||||||
|
withLog (eInfo <> " error: " <> tshow e) $ ackMsg msgMeta Nothing
|
||||||
|
throwError e
|
||||||
where
|
where
|
||||||
|
eventInfo = do
|
||||||
|
v <- asks eventSeq
|
||||||
|
eId <- atomically $ stateTVar v $ \i -> (i + 1, i + 1)
|
||||||
|
pure $ "conn_id=" <> tshow cId <> " event_id=" <> tshow eId
|
||||||
|
withLog eInfo' ack = do
|
||||||
|
ts <- showTags
|
||||||
|
logInfo $ T.unwords [label, "ack:", ts, eInfo']
|
||||||
|
ack
|
||||||
|
logInfo $ T.unwords [label, "ack=success:", ts, eInfo']
|
||||||
|
showTags = do
|
||||||
|
ts <- maybe (pure []) readTVarIO tags
|
||||||
|
pure $ case ts of
|
||||||
|
[] -> "no_chat_messages"
|
||||||
|
[t] -> "chat_message=" <> t
|
||||||
|
_ -> "chat_message_batch=" <> T.intercalate "," (reverse ts)
|
||||||
ackMsg :: MsgMeta -> Maybe MsgReceiptInfo -> CM ()
|
ackMsg :: MsgMeta -> Maybe MsgReceiptInfo -> CM ()
|
||||||
ackMsg MsgMeta {recipient = (msgId, _)} rcpt = withAgent $ \a -> ackMessageAsync a "" cId msgId rcpt
|
ackMsg MsgMeta {recipient = (msgId, _)} rcpt = withAgent $ \a -> ackMessageAsync a "" cId msgId rcpt
|
||||||
|
|
||||||
|
@ -6608,6 +6645,8 @@ deliverMessagesB msgReqs = do
|
||||||
where
|
where
|
||||||
updatePQ = updateConnPQSndEnabled db connId pqSndEnabled'
|
updatePQ = updateConnPQSndEnabled db connId pqSndEnabled'
|
||||||
|
|
||||||
|
-- TODO combine profile update and message into one batch
|
||||||
|
-- Take into account that it may not fit, and that we currently don't support sending multiple messages to the same connection in one call.
|
||||||
sendGroupMessage :: MsgEncodingI e => User -> GroupInfo -> [GroupMember] -> ChatMsgEvent e -> CM (SndMessage, [GroupMember])
|
sendGroupMessage :: MsgEncodingI e => User -> GroupInfo -> [GroupMember] -> ChatMsgEvent e -> CM (SndMessage, [GroupMember])
|
||||||
sendGroupMessage user gInfo members chatMsgEvent = do
|
sendGroupMessage user gInfo members chatMsgEvent = do
|
||||||
when shouldSendProfileUpdate $
|
when shouldSendProfileUpdate $
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
{-# LANGUAGE StrictData #-}
|
{-# LANGUAGE StrictData #-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
{-# LANGUAGE TypeApplications #-}
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
{-# OPTIONS_GHC -fno-warn-implicit-lift #-}
|
||||||
|
|
||||||
module Simplex.Chat.Controller where
|
module Simplex.Chat.Controller where
|
||||||
|
|
||||||
|
@ -205,6 +206,7 @@ data ChatController = ChatController
|
||||||
chatStore :: SQLiteStore,
|
chatStore :: SQLiteStore,
|
||||||
chatStoreChanged :: TVar Bool, -- if True, chat should be fully restarted
|
chatStoreChanged :: TVar Bool, -- if True, chat should be fully restarted
|
||||||
random :: TVar ChaChaDRG,
|
random :: TVar ChaChaDRG,
|
||||||
|
eventSeq :: TVar Int,
|
||||||
inputQ :: TBQueue String,
|
inputQ :: TBQueue String,
|
||||||
outputQ :: TBQueue (Maybe CorrId, Maybe RemoteHostId, ChatResponse),
|
outputQ :: TBQueue (Maybe CorrId, Maybe RemoteHostId, ChatResponse),
|
||||||
connNetworkStatuses :: TMap AgentConnId NetworkStatus,
|
connNetworkStatuses :: TMap AgentConnId NetworkStatus,
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
{-# LANGUAGE TypeOperators #-}
|
{-# LANGUAGE TypeOperators #-}
|
||||||
{-# LANGUAGE UndecidableInstances #-}
|
{-# LANGUAGE UndecidableInstances #-}
|
||||||
{-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}
|
{-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}
|
||||||
|
{-# OPTIONS_GHC -fno-warn-operator-whitespace #-}
|
||||||
|
|
||||||
module Simplex.Chat.Messages where
|
module Simplex.Chat.Messages where
|
||||||
|
|
||||||
|
@ -455,10 +456,10 @@ deriving instance Show ACIReaction
|
||||||
data JSONCIReaction c d = JSONCIReaction {chatInfo :: ChatInfo c, chatReaction :: CIReaction c d}
|
data JSONCIReaction c d = JSONCIReaction {chatInfo :: ChatInfo c, chatReaction :: CIReaction c d}
|
||||||
|
|
||||||
type family ChatTypeQuotable (a :: ChatType) :: Constraint where
|
type family ChatTypeQuotable (a :: ChatType) :: Constraint where
|
||||||
ChatTypeQuotable CTDirect = ()
|
ChatTypeQuotable 'CTDirect = ()
|
||||||
ChatTypeQuotable CTGroup = ()
|
ChatTypeQuotable 'CTGroup = ()
|
||||||
ChatTypeQuotable a =
|
ChatTypeQuotable a =
|
||||||
(Int ~ Bool, TypeError (Type.Text "ChatType " :<>: ShowType a :<>: Type.Text " cannot be quoted"))
|
(Int ~ Bool, TypeError ('Type.Text "ChatType " ':<>: 'ShowType a ':<>: 'Type.Text " cannot be quoted"))
|
||||||
|
|
||||||
data CIQDirection (c :: ChatType) where
|
data CIQDirection (c :: ChatType) where
|
||||||
CIQDirectSnd :: CIQDirection 'CTDirect
|
CIQDirectSnd :: CIQDirection 'CTDirect
|
||||||
|
|
|
@ -838,7 +838,7 @@ toLocalChatItem currentTs ((itemId, itemTs, AMsgDirection msgDir, itemContentTex
|
||||||
ciMeta content status =
|
ciMeta content status =
|
||||||
let itemDeleted' = case itemDeleted of
|
let itemDeleted' = case itemDeleted of
|
||||||
DBCINotDeleted -> Nothing
|
DBCINotDeleted -> Nothing
|
||||||
_ -> Just (CIDeleted @CTLocal deletedTs)
|
_ -> Just (CIDeleted @'CTLocal deletedTs)
|
||||||
itemEdited' = fromMaybe False itemEdited
|
itemEdited' = fromMaybe False itemEdited
|
||||||
itemForwarded = toCIForwardedFrom forwardedFromRow
|
itemForwarded = toCIForwardedFrom forwardedFromRow
|
||||||
in mkCIMeta itemId content itemText status sentViaProxy sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed itemLive currentTs itemTs Nothing createdAt updatedAt
|
in mkCIMeta itemId content itemText status sentViaProxy sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed itemLive currentTs itemTs Nothing createdAt updatedAt
|
||||||
|
@ -1458,7 +1458,7 @@ toDirectChatItem currentTs (((itemId, itemTs, AMsgDirection msgDir, itemContentT
|
||||||
ciMeta content status =
|
ciMeta content status =
|
||||||
let itemDeleted' = case itemDeleted of
|
let itemDeleted' = case itemDeleted of
|
||||||
DBCINotDeleted -> Nothing
|
DBCINotDeleted -> Nothing
|
||||||
_ -> Just (CIDeleted @CTDirect deletedTs)
|
_ -> Just (CIDeleted @'CTDirect deletedTs)
|
||||||
itemEdited' = fromMaybe False itemEdited
|
itemEdited' = fromMaybe False itemEdited
|
||||||
itemForwarded = toCIForwardedFrom forwardedFromRow
|
itemForwarded = toCIForwardedFrom forwardedFromRow
|
||||||
in mkCIMeta itemId content itemText status sentViaProxy sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed itemLive currentTs itemTs Nothing createdAt updatedAt
|
in mkCIMeta itemId content itemText status sentViaProxy sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed itemLive currentTs itemTs Nothing createdAt updatedAt
|
||||||
|
@ -1520,7 +1520,7 @@ toGroupChatItem currentTs userContactId (((itemId, itemTs, AMsgDirection msgDir,
|
||||||
DBCINotDeleted -> Nothing
|
DBCINotDeleted -> Nothing
|
||||||
DBCIBlocked -> Just (CIBlocked deletedTs)
|
DBCIBlocked -> Just (CIBlocked deletedTs)
|
||||||
DBCIBlockedByAdmin -> Just (CIBlockedByAdmin deletedTs)
|
DBCIBlockedByAdmin -> Just (CIBlockedByAdmin deletedTs)
|
||||||
_ -> Just (maybe (CIDeleted @CTGroup deletedTs) (CIModerated deletedTs) deletedByGroupMember_)
|
_ -> Just (maybe (CIDeleted @'CTGroup deletedTs) (CIModerated deletedTs) deletedByGroupMember_)
|
||||||
itemEdited' = fromMaybe False itemEdited
|
itemEdited' = fromMaybe False itemEdited
|
||||||
itemForwarded = toCIForwardedFrom forwardedFromRow
|
itemForwarded = toCIForwardedFrom forwardedFromRow
|
||||||
in mkCIMeta itemId content itemText status sentViaProxy sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed itemLive currentTs itemTs forwardedByMember createdAt updatedAt
|
in mkCIMeta itemId content itemText status sentViaProxy sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed itemLive currentTs itemTs forwardedByMember createdAt updatedAt
|
||||||
|
@ -1919,7 +1919,7 @@ markGroupChatItemDeleted db User {userId} GroupInfo {groupId} ci@ChatItem {meta}
|
||||||
let itemId = chatItemId' ci
|
let itemId = chatItemId' ci
|
||||||
(deletedByGroupMemberId, itemDeleted) = case byGroupMember_ of
|
(deletedByGroupMemberId, itemDeleted) = case byGroupMember_ of
|
||||||
Just m@GroupMember {groupMemberId} -> (Just groupMemberId, Just $ CIModerated (Just deletedTs) m)
|
Just m@GroupMember {groupMemberId} -> (Just groupMemberId, Just $ CIModerated (Just deletedTs) m)
|
||||||
_ -> (Nothing, Just $ CIDeleted @CTGroup (Just deletedTs))
|
_ -> (Nothing, Just $ CIDeleted @'CTGroup (Just deletedTs))
|
||||||
insertChatItemMessage_ db itemId msgId currentTs
|
insertChatItemMessage_ db itemId msgId currentTs
|
||||||
DB.execute
|
DB.execute
|
||||||
db
|
db
|
||||||
|
|
|
@ -2028,14 +2028,16 @@ viewChatError logLevel testView = \case
|
||||||
DBErrorOpen e -> ["error opening database after encryption: " <> sqliteError' e]
|
DBErrorOpen e -> ["error opening database after encryption: " <> sqliteError' e]
|
||||||
e -> ["chat database error: " <> sShow e]
|
e -> ["chat database error: " <> sShow e]
|
||||||
ChatErrorAgent err entity_ -> case err of
|
ChatErrorAgent err entity_ -> case err of
|
||||||
CMD PROHIBITED -> [withConnEntity <> "error: command is prohibited"]
|
CMD PROHIBITED cxt -> [withConnEntity <> plain ("error: command is prohibited, " <> cxt)]
|
||||||
SMP _ SMP.AUTH ->
|
SMP _ SMP.AUTH ->
|
||||||
[ withConnEntity
|
[ withConnEntity
|
||||||
<> "error: connection authorization failed - this could happen if connection was deleted,\
|
<> "error: connection authorization failed - this could happen if connection was deleted,\
|
||||||
\ secured with different credentials, or due to a bug - please re-create the connection"
|
\ secured with different credentials, or due to a bug - please re-create the connection"
|
||||||
]
|
]
|
||||||
|
BROKER _ NETWORK -> []
|
||||||
|
BROKER _ TIMEOUT -> []
|
||||||
AGENT A_DUPLICATE -> [withConnEntity <> "error: AGENT A_DUPLICATE" | logLevel == CLLDebug]
|
AGENT A_DUPLICATE -> [withConnEntity <> "error: AGENT A_DUPLICATE" | logLevel == CLLDebug]
|
||||||
AGENT A_PROHIBITED -> [withConnEntity <> "error: AGENT A_PROHIBITED" | logLevel <= CLLWarning]
|
AGENT (A_PROHIBITED e) -> [withConnEntity <> "error: AGENT A_PROHIBITED, " <> plain e | logLevel <= CLLWarning]
|
||||||
CONN NOT_FOUND -> [withConnEntity <> "error: CONN NOT_FOUND" | logLevel <= CLLWarning]
|
CONN NOT_FOUND -> [withConnEntity <> "error: CONN NOT_FOUND" | logLevel <= CLLWarning]
|
||||||
CRITICAL restart e -> [plain $ "critical error: " <> e] <> ["please restart the app" | restart]
|
CRITICAL restart e -> [plain $ "critical error: " <> e] <> ["please restart the app" | restart]
|
||||||
INTERNAL e -> [plain $ "internal error: " <> e]
|
INTERNAL e -> [plain $ "internal error: " <> e]
|
||||||
|
|
|
@ -431,7 +431,8 @@ serverCfg =
|
||||||
smpHandshakeTimeout = 1000000,
|
smpHandshakeTimeout = 1000000,
|
||||||
controlPort = Nothing,
|
controlPort = Nothing,
|
||||||
smpAgentCfg = defaultSMPClientAgentConfig,
|
smpAgentCfg = defaultSMPClientAgentConfig,
|
||||||
allowSMPProxy = False
|
allowSMPProxy = False,
|
||||||
|
serverClientConcurrency = 16
|
||||||
}
|
}
|
||||||
|
|
||||||
withSmpServer :: IO () -> IO ()
|
withSmpServer :: IO () -> IO ()
|
||||||
|
|
|
@ -2310,12 +2310,12 @@ testAbortSwitchContact tmp = do
|
||||||
alice <## "bob: you started changing address"
|
alice <## "bob: you started changing address"
|
||||||
-- repeat switch is prohibited
|
-- repeat switch is prohibited
|
||||||
alice ##> "/switch bob"
|
alice ##> "/switch bob"
|
||||||
alice <## "error: command is prohibited"
|
alice <## "error: command is prohibited, switchConnectionAsync: already switching"
|
||||||
-- stop switch
|
-- stop switch
|
||||||
alice #$> ("/abort switch bob", id, "switch aborted")
|
alice #$> ("/abort switch bob", id, "switch aborted")
|
||||||
-- repeat switch stop is prohibited
|
-- repeat switch stop is prohibited
|
||||||
alice ##> "/abort switch bob"
|
alice ##> "/abort switch bob"
|
||||||
alice <## "error: command is prohibited"
|
alice <## "error: command is prohibited, abortConnectionSwitch: not allowed"
|
||||||
withTestChatContactConnected tmp "bob" $ \bob -> do
|
withTestChatContactConnected tmp "bob" $ \bob -> do
|
||||||
bob <## "alice started changing address for you"
|
bob <## "alice started changing address for you"
|
||||||
-- alice changes address again
|
-- alice changes address again
|
||||||
|
@ -2356,12 +2356,12 @@ testAbortSwitchGroupMember tmp = do
|
||||||
alice <## "#team: you started changing address for bob"
|
alice <## "#team: you started changing address for bob"
|
||||||
-- repeat switch is prohibited
|
-- repeat switch is prohibited
|
||||||
alice ##> "/switch #team bob"
|
alice ##> "/switch #team bob"
|
||||||
alice <## "error: command is prohibited"
|
alice <## "error: command is prohibited, switchConnectionAsync: already switching"
|
||||||
-- stop switch
|
-- stop switch
|
||||||
alice #$> ("/abort switch #team bob", id, "switch aborted")
|
alice #$> ("/abort switch #team bob", id, "switch aborted")
|
||||||
-- repeat switch stop is prohibited
|
-- repeat switch stop is prohibited
|
||||||
alice ##> "/abort switch #team bob"
|
alice ##> "/abort switch #team bob"
|
||||||
alice <## "error: command is prohibited"
|
alice <## "error: command is prohibited, abortConnectionSwitch: not allowed"
|
||||||
withTestChatContactConnected tmp "bob" $ \bob -> do
|
withTestChatContactConnected tmp "bob" $ \bob -> do
|
||||||
bob <## "#team: connected to server(s)"
|
bob <## "#team: connected to server(s)"
|
||||||
bob <## "#team: alice started changing address for you"
|
bob <## "#team: alice started changing address for you"
|
||||||
|
@ -2485,7 +2485,7 @@ setupDesynchronizedRatchet tmp alice = do
|
||||||
withTestChat tmp "bob_old" $ \bob -> do
|
withTestChat tmp "bob_old" $ \bob -> do
|
||||||
bob <## "1 contacts connected (use /cs for the list)"
|
bob <## "1 contacts connected (use /cs for the list)"
|
||||||
bob ##> "/sync alice"
|
bob ##> "/sync alice"
|
||||||
bob <## "error: command is prohibited"
|
bob <## "error: command is prohibited, synchronizeRatchet: not allowed"
|
||||||
alice #> "@bob 1"
|
alice #> "@bob 1"
|
||||||
bob <## "alice: decryption error (connection out of sync), synchronization required"
|
bob <## "alice: decryption error (connection out of sync), synchronization required"
|
||||||
bob <## "use /sync alice to synchronize"
|
bob <## "use /sync alice to synchronize"
|
||||||
|
@ -2495,7 +2495,7 @@ setupDesynchronizedRatchet tmp alice = do
|
||||||
bob ##> "/tail @alice 1"
|
bob ##> "/tail @alice 1"
|
||||||
bob <# "alice> decryption error, possibly due to the device change (header, 3 messages)"
|
bob <# "alice> decryption error, possibly due to the device change (header, 3 messages)"
|
||||||
bob ##> "@alice 1"
|
bob ##> "@alice 1"
|
||||||
bob <## "error: command is prohibited"
|
bob <## "error: command is prohibited, sendMessagesB: send prohibited"
|
||||||
(alice </)
|
(alice </)
|
||||||
where
|
where
|
||||||
copyDb from to = do
|
copyDb from to = do
|
||||||
|
|
|
@ -3266,7 +3266,7 @@ setupDesynchronizedRatchet tmp alice = do
|
||||||
bob <## "1 contacts connected (use /cs for the list)"
|
bob <## "1 contacts connected (use /cs for the list)"
|
||||||
bob <## "#team: connected to server(s)"
|
bob <## "#team: connected to server(s)"
|
||||||
bob ##> "/sync #team alice"
|
bob ##> "/sync #team alice"
|
||||||
bob <## "error: command is prohibited"
|
bob <## "error: command is prohibited, synchronizeRatchet: not allowed"
|
||||||
alice #> "#team 1"
|
alice #> "#team 1"
|
||||||
bob <## "#team alice: decryption error (connection out of sync), synchronization required"
|
bob <## "#team alice: decryption error (connection out of sync), synchronization required"
|
||||||
bob <## "use /sync #team alice to synchronize"
|
bob <## "use /sync #team alice to synchronize"
|
||||||
|
@ -3294,7 +3294,7 @@ testGroupSyncRatchet tmp =
|
||||||
bob <## "1 contacts connected (use /cs for the list)"
|
bob <## "1 contacts connected (use /cs for the list)"
|
||||||
bob <## "#team: connected to server(s)"
|
bob <## "#team: connected to server(s)"
|
||||||
bob `send` "#team 1"
|
bob `send` "#team 1"
|
||||||
bob <## "error: command is prohibited" -- silence?
|
bob <## "error: command is prohibited, sendMessagesB: send prohibited" -- silence?
|
||||||
bob <# "#team 1"
|
bob <# "#team 1"
|
||||||
(alice </)
|
(alice </)
|
||||||
-- synchronize bob and alice
|
-- synchronize bob and alice
|
||||||
|
|
|
@ -294,7 +294,7 @@ testFileCApi fileName tmp = do
|
||||||
let sz' = fromIntegral sz
|
let sz' = fromIntegral sz
|
||||||
contents <- create sz' $ \toPtr -> copyBytes toPtr (ptr' `plusPtr` 5) sz'
|
contents <- create sz' $ \toPtr -> copyBytes toPtr (ptr' `plusPtr` 5) sz'
|
||||||
contents `shouldBe` src
|
contents `shouldBe` src
|
||||||
sz' `shouldBe` fromIntegral len
|
sz' `shouldBe` len
|
||||||
|
|
||||||
testMissingFileCApi :: FilePath -> IO ()
|
testMissingFileCApi :: FilePath -> IO ()
|
||||||
testMissingFileCApi tmp = do
|
testMissingFileCApi tmp = do
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue