Merge branch 'master' into master-ghc8107

This commit is contained in:
Evgeny Poberezkin
2023-11-24 19:00:30 +00:00
78 changed files with 2625 additions and 340 deletions
+2 -2
View File
@@ -6066,8 +6066,9 @@ chatCommandP =
("/help groups" <|> "/help group" <|> "/hg") $> ChatHelp HSGroups,
("/help contacts" <|> "/help contact" <|> "/hc") $> ChatHelp HSContacts,
("/help address" <|> "/ha") $> ChatHelp HSMyAddress,
"/help incognito" $> ChatHelp HSIncognito,
("/help incognito" <|> "/hi") $> ChatHelp HSIncognito,
("/help messages" <|> "/hm") $> ChatHelp HSMessages,
("/help remote" <|> "/hr") $> ChatHelp HSRemote,
("/help settings" <|> "/hs") $> ChatHelp HSSettings,
("/help db" <|> "/hd") $> ChatHelp HSDatabase,
("/help" <|> "/h") $> ChatHelp HSMain,
@@ -6174,7 +6175,6 @@ chatCommandP =
"/set disappear " *> (SetUserTimedMessages <$> (("yes" $> True) <|> ("no" $> False))),
("/incognito" <* optional (A.space *> onOffP)) $> ChatHelp HSIncognito,
"/set device name " *> (SetLocalDeviceName <$> textP),
-- "/create remote host" $> CreateRemoteHost,
"/list remote hosts" $> ListRemoteHosts,
"/switch remote host " *> (SwitchRemoteHost <$> ("local" $> Nothing <|> (Just <$> A.decimal))),
"/start remote host " *> (StartRemoteHost <$> ("new" $> Nothing <|> (Just <$> ((,) <$> A.decimal <*> (" multicast=" *> onOffP <|> pure False))))),
+22 -5
View File
@@ -203,7 +203,7 @@ data ChatController = ChatController
contactMergeEnabled :: TVar Bool
}
data HelpSection = HSMain | HSFiles | HSGroups | HSContacts | HSMyAddress | HSIncognito | HSMarkdown | HSMessages | HSSettings | HSDatabase
data HelpSection = HSMain | HSFiles | HSGroups | HSContacts | HSMyAddress | HSIncognito | HSMarkdown | HSMessages | HSRemote | HSSettings | HSDatabase
deriving (Show)
data ChatCommand
@@ -662,14 +662,14 @@ data ChatResponse
| CRRemoteHostSessionCode {remoteHost_ :: Maybe RemoteHostInfo, sessionCode :: Text}
| CRNewRemoteHost {remoteHost :: RemoteHostInfo}
| CRRemoteHostConnected {remoteHost :: RemoteHostInfo}
| CRRemoteHostStopped {remoteHostId_ :: Maybe RemoteHostId}
| CRRemoteHostStopped {remoteHostId_ :: Maybe RemoteHostId, rhsState :: RemoteHostSessionState, rhStopReason :: RemoteHostStopReason}
| CRRemoteFileStored {remoteHostId :: RemoteHostId, remoteFileSource :: CryptoFile}
| CRRemoteCtrlList {remoteCtrls :: [RemoteCtrlInfo]}
| CRRemoteCtrlFound {remoteCtrl :: RemoteCtrlInfo, ctrlAppInfo_ :: Maybe CtrlAppInfo, appVersion :: AppVersion, compatible :: Bool}
| CRRemoteCtrlConnecting {remoteCtrl_ :: Maybe RemoteCtrlInfo, ctrlAppInfo :: CtrlAppInfo, appVersion :: AppVersion}
| CRRemoteCtrlSessionCode {remoteCtrl_ :: Maybe RemoteCtrlInfo, sessionCode :: Text}
| CRRemoteCtrlConnected {remoteCtrl :: RemoteCtrlInfo}
| CRRemoteCtrlStopped
| CRRemoteCtrlStopped {rcsState :: RemoteCtrlSessionState, rcStopReason :: RemoteCtrlStopReason}
| CRSQLResult {rows :: [Text]}
| CRSlowSQLQueries {chatQueries :: [SlowSQLQuery], agentQueries :: [SlowSQLQuery]}
| CRDebugLocks {chatLockName :: Maybe String, agentLocks :: AgentLocks}
@@ -700,14 +700,14 @@ allowRemoteEvent = \case
CRRemoteHostSessionCode {} -> False
CRNewRemoteHost _ -> False
CRRemoteHostConnected _ -> False
CRRemoteHostStopped _ -> False
CRRemoteHostStopped {} -> False
CRRemoteFileStored {} -> False
CRRemoteCtrlList _ -> False
CRRemoteCtrlFound {} -> False
CRRemoteCtrlConnecting {} -> False
CRRemoteCtrlSessionCode {} -> False
CRRemoteCtrlConnected _ -> False
CRRemoteCtrlStopped -> False
CRRemoteCtrlStopped {} -> False
CRSQLResult _ -> False
CRSlowSQLQueries {} -> False
_ -> True
@@ -1083,6 +1083,12 @@ data RemoteHostError
| RHEProtocolError RemoteProtocolError
deriving (Show, Exception)
data RemoteHostStopReason
= RHSRConnectionFailed ChatError
| RHSRCrashed ChatError
| RHSRDisconnected
deriving (Show, Exception)
-- TODO review errors, some of it can be covered by HTTP2 errors
data RemoteCtrlError
= RCEInactive -- ^ No session is running
@@ -1098,6 +1104,13 @@ data RemoteCtrlError
| RCEProtocolError {protocolError :: RemoteProtocolError}
deriving (Show, Exception)
data RemoteCtrlStopReason
= RCSRDiscoveryFailed ChatError
| RCSRConnectionFailed ChatError
| RCSRSetupFailed ChatError
| RCSRDisconnected
deriving (Show, Exception)
data ArchiveError
= AEImport {chatError :: ChatError}
| AEImportFile {file :: String, chatError :: ChatError}
@@ -1323,6 +1336,10 @@ $(JQ.deriveJSON (sumTypeJSON $ dropPrefix "RCS") ''RemoteCtrlSessionState)
$(JQ.deriveJSON defaultJSON ''RemoteCtrlInfo)
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "RCSR") ''RemoteCtrlStopReason)
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "RHSR") ''RemoteHostStopReason)
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "CR") ''ChatResponse)
$(JQ.deriveFromJSON defaultJSON ''ArchiveConfig)
+30 -1
View File
@@ -10,6 +10,7 @@ module Simplex.Chat.Help
myAddressHelpInfo,
incognitoHelpInfo,
messagesHelpInfo,
remoteHelpInfo,
markdownInfo,
settingsInfo,
databaseHelpInfo,
@@ -87,7 +88,7 @@ chatHelpInfo =
green "Create your address: " <> highlight "/address",
"",
green "Other commands:",
indent <> highlight "/help <topic> " <> " - help on: " <> listHighlight ["groups", "contacts", "messages", "files", "address", "settings", "db"],
indent <> highlight "/help <topic> " <> " - help on: " <> listHighlight ["groups", "contacts", "messages", "files", "address", "incognito", "remote", "settings", "db"],
indent <> highlight "/profile " <> " - show / update user profile",
indent <> highlight "/delete <contact>" <> " - delete contact and all messages with them",
indent <> highlight "/chats " <> " - most recent chats",
@@ -272,6 +273,34 @@ messagesHelpInfo =
indent <> highlight "! #team (hi) <new msg> " <> " - to edit your message in the group #team"
]
remoteHelpInfo :: [StyledString]
remoteHelpInfo =
map
styleMarkdown
[ green "Remote control",
"You can use CLI as a remote controller for a mobile app or as a remote host for a desktop app (or another CLI).",
"For example, you can run CLI on a server and use it from a desktop computer, connecting via SSH port forwarding.",
"",
indent <> highlight "/set device name <name> " <> " - set CLI name for remote connections",
"",
green "Using as remote controller",
indent <> highlight "/start remote host new " <> " - pair and connect a new remote host",
indent <> highlight "/start remote host <id> [multicast=on] " <> " - start connection with a known (paired) remote host",
indent <> highlight "/stop remote host new " <> " - cancel pairing with a new remote host",
indent <> highlight "/stop remote host <id> " <> " - stop connection with connected remote host",
indent <> highlight "/switch remote host local " <> " - switch to using local database",
indent <> highlight "/switch remote host <id> " <> " - switch to connected remote host",
indent <> highlight "/list remote hosts " <> " - list known remote hosts",
indent <> highlight "/delete remote host <id> " <> " - delete (unpair) known remote hosts - also deletes all host files from controller",
"",
green "Using as remote host",
indent <> highlight "/connect remote ctrl <session_address> " <> " - connect to remote controller via the adress from /start remote host",
indent <> highlight "/stop remote ctrl " <> " - stop connection with remote controller",
indent <> highlight "/find remote ctrl " <> " - find known remote controller on the local network (it should be started with multicast=on)",
indent <> highlight "/list remote ctrls " <> " - list known remote controllers",
indent <> highlight "/delete remote ctrl <id> " <> " - delete known remote controller"
]
markdownInfo :: [StyledString]
markdownInfo =
map
+24 -22
View File
@@ -169,12 +169,12 @@ startRemoteHost rh_ = do
handleConnectError :: ChatMonad m => RHKey -> SessionSeq -> m a -> m a
handleConnectError rhKey sessSeq action = action `catchChatError` \err -> do
logError $ "startRemoteHost.rcConnectHost crashed: " <> tshow err
cancelRemoteHostSession (Just sessSeq) rhKey
cancelRemoteHostSession (Just (sessSeq, RHSRConnectionFailed err)) rhKey
throwError err
handleHostError :: ChatMonad m => SessionSeq -> TVar RHKey -> m () -> m ()
handleHostError sessSeq rhKeyVar action = action `catchChatError` \err -> do
logError $ "startRemoteHost.waitForHostSession crashed: " <> tshow err
readTVarIO rhKeyVar >>= cancelRemoteHostSession (Just sessSeq)
readTVarIO rhKeyVar >>= cancelRemoteHostSession (Just (sessSeq, RHSRCrashed err))
waitForHostSession :: ChatMonad m => Maybe RemoteHostInfo -> RHKey -> SessionSeq -> TVar RHKey -> RCStepTMVar (ByteString, TLS, RCStepTMVar (RCHostSession, RCHostHello, RCHostPairing)) -> m ()
waitForHostSession remoteHost_ rhKey sseq rhKeyVar vars = do
(sessId, tls, vars') <- timeoutThrow (ChatErrorRemoteHost rhKey RHETimeout) 60000000 $ takeRCStep vars
@@ -220,7 +220,7 @@ startRemoteHost rh_ = do
onDisconnected :: ChatMonad m => RHKey -> SessionSeq -> m ()
onDisconnected rhKey sseq = do
logDebug $ "HTTP2 client disconnected: " <> tshow (rhKey, sseq)
cancelRemoteHostSession (Just sseq) rhKey
cancelRemoteHostSession (Just (sseq, RHSRDisconnected)) rhKey
pollEvents :: ChatMonad m => RemoteHostId -> RemoteHostClient -> m ()
pollEvents rhId rhClient = do
oq <- asks outputQ
@@ -246,24 +246,25 @@ closeRemoteHost rhKey = do
logNote $ "Closing remote host session for " <> tshow rhKey
cancelRemoteHostSession Nothing rhKey
cancelRemoteHostSession :: ChatMonad m => Maybe SessionSeq -> RHKey -> m ()
cancelRemoteHostSession sseq_ rhKey = do
cancelRemoteHostSession :: ChatMonad m => Maybe (SessionSeq, RemoteHostStopReason) -> RHKey -> m ()
cancelRemoteHostSession handlerInfo_ rhKey = do
sessions <- asks remoteHostSessions
crh <- asks currentRemoteHost
deregistered <- atomically $
TM.lookup rhKey sessions >>= \case
Nothing -> pure Nothing
Just (sessSeq, _) | maybe False (/= sessSeq) sseq_ -> pure Nothing -- ignore cancel from a ghost session handler
Just (sessSeq, _) | maybe False (/= sessSeq) (fst <$> handlerInfo_) -> pure Nothing -- ignore cancel from a ghost session handler
Just (_, rhs) -> do
TM.delete rhKey sessions
modifyTVar' crh $ \cur -> if (RHId <$> cur) == Just rhKey then Nothing else cur -- only wipe the closing RH
pure $ Just rhs
forM_ deregistered $ \session -> do
liftIO $ cancelRemoteHost handlingError session `catchAny` (logError . tshow)
when handlingError $ toView $ CRRemoteHostStopped rhId_
forM_ (snd <$> handlerInfo_) $ \rhStopReason ->
toView $ CRRemoteHostStopped {remoteHostId_, rhsState = rhsSessionState session, rhStopReason}
where
handlingError = isJust sseq_
rhId_ = case rhKey of
handlingError = isJust handlerInfo_
remoteHostId_ = case rhKey of
RHNew -> Nothing
RHId rhId -> Just rhId
@@ -395,7 +396,7 @@ findKnownRemoteCtrl = do
sseq <- startRemoteCtrlSession
foundCtrl <- newEmptyTMVarIO
cmdOk <- newEmptyTMVarIO
action <- async $ handleCtrlError sseq "findKnownRemoteCtrl.discover" $ do
action <- async $ handleCtrlError sseq RCSRDiscoveryFailed "findKnownRemoteCtrl.discover" $ do
atomically $ takeTMVar cmdOk
(RCCtrlPairing {ctrlFingerprint}, inv@(RCVerifiedInvitation RCInvitation {app})) <-
timeoutThrow (ChatErrorRemoteCtrl RCETimeout) discoveryTimeout . withAgent $ \a -> rcDiscoverCtrl a pairings
@@ -441,7 +442,7 @@ startRemoteCtrlSession = do
Right sseq <$ writeTVar session (Just (sseq, RCSessionStarting))
connectRemoteCtrl :: ChatMonad m => RCVerifiedInvitation -> SessionSeq -> m (Maybe RemoteCtrlInfo, CtrlAppInfo)
connectRemoteCtrl verifiedInv@(RCVerifiedInvitation inv@RCInvitation {ca, app}) sseq = handleCtrlError sseq "connectRemoteCtrl" $ do
connectRemoteCtrl verifiedInv@(RCVerifiedInvitation inv@RCInvitation {ca, app}) sseq = handleCtrlError sseq RCSRConnectionFailed "connectRemoteCtrl" $ do
ctrlInfo@CtrlAppInfo {deviceName = ctrlDeviceName} <- parseCtrlAppInfo app
v <- checkAppVersion ctrlInfo
rc_ <- withStore' $ \db -> getRemoteCtrlByFingerprint db ca
@@ -452,7 +453,7 @@ connectRemoteCtrl verifiedInv@(RCVerifiedInvitation inv@RCInvitation {ca, app})
cmdOk <- newEmptyTMVarIO
rcsWaitSession <- async $ do
atomically $ takeTMVar cmdOk
handleCtrlError sseq "waitForCtrlSession" $ waitForCtrlSession rc_ ctrlDeviceName rcsClient vars
handleCtrlError sseq RCSRConnectionFailed "waitForCtrlSession" $ waitForCtrlSession rc_ ctrlDeviceName rcsClient vars
updateRemoteCtrlSession sseq $ \case
RCSessionStarting -> Right RCSessionConnecting {remoteCtrlId_ = remoteCtrlId' <$> rc_, rcsClient, rcsWaitSession}
_ -> Left $ ChatErrorRemoteCtrl RCEBadState
@@ -602,7 +603,7 @@ verifyRemoteCtrlSession execChatCommand sessCode' = do
Nothing -> throwError $ ChatErrorRemoteCtrl RCEInactive
Just (sseq, RCSessionPendingConfirmation {rcsClient, ctrlDeviceName = ctrlName, sessionCode, rcsWaitConfirmation}) -> pure (sseq, rcsClient, ctrlName, sessionCode, rcsWaitConfirmation)
_ -> throwError $ ChatErrorRemoteCtrl RCEBadState
handleCtrlError sseq "verifyRemoteCtrlSession" $ do
handleCtrlError sseq RCSRSetupFailed "verifyRemoteCtrlSession" $ do
let verified = sameVerificationCode sessCode' sessionCode
timeoutThrow (ChatErrorRemoteCtrl RCETimeout) networkIOTimeout . liftIO $ confirmCtrlSession client verified -- signal verification result before crashing
unless verified $ throwError $ ChatErrorRemoteCtrl $ RCEProtocolError PRESessionCode
@@ -630,31 +631,32 @@ verifyRemoteCtrlSession execChatCommand sessCode' = do
monitor sseq server = do
res <- waitCatch server
logInfo $ "HTTP2 server stopped: " <> tshow res
cancelActiveRemoteCtrl (Just sseq)
cancelActiveRemoteCtrl $ Just (sseq, RCSRDisconnected)
stopRemoteCtrl :: ChatMonad m => m ()
stopRemoteCtrl = cancelActiveRemoteCtrl Nothing
handleCtrlError :: ChatMonad m => SessionSeq -> Text -> m a -> m a
handleCtrlError sseq name action =
handleCtrlError :: ChatMonad m => SessionSeq -> (ChatError -> RemoteCtrlStopReason) -> Text -> m a -> m a
handleCtrlError sseq mkReason name action =
action `catchChatError` \e -> do
logError $ name <> " remote ctrl error: " <> tshow e
cancelActiveRemoteCtrl (Just sseq)
cancelActiveRemoteCtrl $ Just (sseq, mkReason e)
throwError e
-- | Stop session controller, unless session update key is present but stale
cancelActiveRemoteCtrl :: ChatMonad m => Maybe SessionSeq -> m ()
cancelActiveRemoteCtrl sseq_ = handleAny (logError . tshow) $ do
cancelActiveRemoteCtrl :: ChatMonad m => Maybe (SessionSeq, RemoteCtrlStopReason) -> m ()
cancelActiveRemoteCtrl handlerInfo_ = handleAny (logError . tshow) $ do
var <- asks remoteCtrlSession
session_ <- atomically $ readTVar var >>= \case
Nothing -> pure Nothing
Just (oldSeq, _) | maybe False (/= oldSeq) sseq_ -> pure Nothing
Just (oldSeq, _) | maybe False (/= oldSeq) (fst <$> handlerInfo_) -> pure Nothing
Just (_, s) -> Just s <$ writeTVar var Nothing
forM_ session_ $ \session -> do
liftIO $ cancelRemoteCtrl handlingError session
when handlingError $ toView CRRemoteCtrlStopped
forM_ (snd <$> handlerInfo_) $ \rcStopReason ->
toView CRRemoteCtrlStopped {rcsState = rcsSessionState session, rcStopReason}
where
handlingError = isJust sseq_
handlingError = isJust handlerInfo_
cancelRemoteCtrl :: Bool -> RemoteCtrlSession -> IO ()
cancelRemoteCtrl handlingError = \case
+5 -3
View File
@@ -58,13 +58,15 @@ runInputLoop ct@ChatTerminal {termState, liveMessageState} cc = forever $ do
rh' = if either (const False) allowRemoteCommand cmd then rh else Nothing
unless (isMessage cmd) $ echo s
r <- runReaderT (execChatCommand rh' bs) cc
processResp s cmd r
processResp s cmd rh r
printRespToTerminal ct cc False rh r
startLiveMessage cmd r
where
echo s = printToTerminal ct [plain s]
processResp s cmd = \case
CRActiveUser _ -> setActive ct ""
processResp s cmd rh = \case
CRActiveUser u -> case rh of
Nothing -> setActive ct ""
Just rhId -> updateRemoteUser ct u rhId
CRChatItems u chatName_ _ -> whenCurrUser cc u $ mapM_ (setActive ct . chatActiveTo) chatName_
CRNewChatItem u (AChatItem _ SMDSnd cInfo _) -> whenCurrUser cc u $ setActiveChat ct cInfo
CRChatItemUpdated u (AChatItem _ SMDSnd cInfo _) -> whenCurrUser cc u $ setActiveChat ct cInfo
+39 -12
View File
@@ -10,6 +10,8 @@
module Simplex.Chat.Terminal.Output where
import Control.Concurrent (ThreadId)
import Control.Logger.Simple
import Control.Monad
import Control.Monad.Catch (MonadMask)
import Control.Monad.Except
import Control.Monad.Reader
@@ -18,21 +20,23 @@ import Data.Text (Text)
import qualified Data.Text as T
import Data.Time.Clock (getCurrentTime)
import Data.Time.LocalTime (getCurrentTimeZone)
import Simplex.Chat (processChatCommand)
import Simplex.Chat (execChatCommand, processChatCommand)
import Simplex.Chat.Controller
import Simplex.Chat.Markdown
import Simplex.Chat.Messages
import Simplex.Chat.Messages.CIContent (CIContent(..), SMsgDirection (..))
import Simplex.Chat.Options
import Simplex.Chat.Protocol (MsgContent (..), msgContentText)
import Simplex.Chat.Remote.Types (RemoteHostId)
import Simplex.Chat.Remote.Types (RHKey (..), RemoteHostId, RemoteHostInfo (..), RemoteHostSession (..))
import Simplex.Chat.Styled
import Simplex.Chat.Terminal.Notification (Notification (..), initializeNotifications)
import Simplex.Chat.Types
import Simplex.Chat.View
import Simplex.Messaging.Agent.Protocol
import Simplex.Messaging.Encoding.String
import Simplex.Messaging.Util (safeDecodeUtf8)
import Simplex.Messaging.TMap (TMap)
import qualified Simplex.Messaging.TMap as TM
import Simplex.Messaging.Util (safeDecodeUtf8, tshow)
import System.Console.ANSI.Types
import System.IO (IOMode (..), hPutStrLn, withFile)
import System.Mem.Weak (Weak)
@@ -48,7 +52,8 @@ data ChatTerminal = ChatTerminal
nextMessageRow :: TVar Int,
termLock :: TMVar (),
sendNotification :: Maybe (Notification -> IO ()),
activeTo :: TVar String
activeTo :: TVar String,
currentRemoteUsers :: TMap RemoteHostId User
}
data TerminalState = TerminalState
@@ -103,6 +108,7 @@ newChatTerminal t opts = do
nextMessageRow <- newTVarIO lastRow
sendNotification <- if muteNotifications opts then pure Nothing else Just <$> initializeNotifications
activeTo <- newTVarIO ""
currentRemoteUsers <- newTVarIO mempty
-- threadDelay 500000 -- this delay is the same as timeout in getTerminalSize
pure
ChatTerminal
@@ -113,7 +119,8 @@ newChatTerminal t opts = do
nextMessageRow,
termLock,
sendNotification,
activeTo
activeTo,
currentRemoteUsers
}
mkTermState :: TerminalState
@@ -142,12 +149,14 @@ runTerminalOutput ct cc@ChatController {outputQ, showLiveItems, logFilePath} = d
case r of
CRNewChatItem u ci -> markChatItemRead u ci
CRChatItemUpdated u ci -> markChatItemRead u ci
CRRemoteHostConnected {remoteHost = RemoteHostInfo {remoteHostId}} -> getRemoteUser remoteHostId
CRRemoteHostStopped {remoteHostId_} -> mapM_ removeRemoteUser remoteHostId_
_ -> pure ()
let printResp = case logFilePath of
Just path -> if logResponseToFile r then logResponse path else printToTerminal ct
_ -> printToTerminal ct
liveItems <- readTVarIO showLiveItems
responseString cc liveItems outputRH r >>= printResp
responseString ct cc liveItems outputRH r >>= printResp
responseNotification ct cc r
where
markChatItemRead u (AChatItem _ _ chat ci@ChatItem {chatDir, meta = CIMeta {itemStatus}}) =
@@ -158,6 +167,10 @@ runTerminalOutput ct cc@ChatController {outputQ, showLiveItems, logFilePath} = d
void $ runReaderT (runExceptT $ processChatCommand (APIChatRead chatRef (Just (itemId, itemId)))) cc
_ -> pure ()
logResponse path s = withFile path AppendMode $ \h -> mapM_ (hPutStrLn h . unStyle) s
getRemoteUser rhId = runReaderT (execChatCommand (Just rhId) "/user") cc >>= \case
CRActiveUser {user} -> updateRemoteUser ct user rhId
cr -> logError $ "Unexpected reply while getting remote user: " <> tshow cr
removeRemoteUser rhId = atomically $ TM.delete rhId (currentRemoteUsers ct)
responseNotification :: ChatTerminal -> ChatController -> ChatResponse -> IO ()
responseNotification t@ChatTerminal {sendNotification} cc = \case
@@ -254,15 +267,29 @@ whenCurrUser cc u a = do
sameUser User {userId = uId} = maybe False $ \User {userId} -> userId == uId
printRespToTerminal :: ChatTerminal -> ChatController -> Bool -> Maybe RemoteHostId -> ChatResponse -> IO ()
printRespToTerminal ct cc liveItems outputRH r = responseString cc liveItems outputRH r >>= printToTerminal ct
printRespToTerminal ct cc liveItems outputRH r = responseString ct cc liveItems outputRH r >>= printToTerminal ct
responseString :: ChatController -> Bool -> Maybe RemoteHostId -> ChatResponse -> IO [StyledString]
responseString cc liveItems outputRH r = do
currentRH <- readTVarIO $ currentRemoteHost cc
user <- readTVarIO $ currentUser cc -- XXX: local user, should be subsumed by remote when connected
responseString :: ChatTerminal -> ChatController -> Bool -> Maybe RemoteHostId -> ChatResponse -> IO [StyledString]
responseString ct cc liveItems outputRH r = do
cu <- getCurrentUser ct cc
ts <- getCurrentTime
tz <- getCurrentTimeZone
pure $ responseToView (currentRH, user) (config cc) liveItems ts tz outputRH r
pure $ responseToView cu (config cc) liveItems ts tz outputRH r
updateRemoteUser :: ChatTerminal -> User -> RemoteHostId -> IO ()
updateRemoteUser ct user rhId = atomically $ TM.insert rhId user (currentRemoteUsers ct)
getCurrentUser :: ChatTerminal -> ChatController -> IO (Maybe RemoteHostId, Maybe User)
getCurrentUser ct cc = atomically $ do
localUser_ <- readTVar (currentUser cc)
readTVar (currentRemoteHost cc) >>= \case
Nothing -> pure (Nothing, localUser_)
Just rhId ->
TM.lookup (RHId rhId) (remoteHostSessions cc) >>= \case
Just (_, RHSessionConnected {}) -> do
hostUser_ <- TM.lookup rhId (currentRemoteUsers ct)
pure (Just rhId, hostUser_)
_ -> pure (Nothing, localUser_)
printToTerminal :: ChatTerminal -> [StyledString] -> IO ()
printToTerminal ct s =
+6 -4
View File
@@ -135,6 +135,7 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
HSIncognito -> incognitoHelpInfo
HSMessages -> messagesHelpInfo
HSMarkdown -> markdownInfo
HSRemote -> remoteHelpInfo
HSSettings -> settingsInfo
HSDatabase -> databaseHelpInfo
CRWelcome user -> chatWelcome user
@@ -298,8 +299,8 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
]
CRNewRemoteHost RemoteHostInfo {remoteHostId = rhId, hostDeviceName} -> ["new remote host " <> sShow rhId <> " added: " <> plain hostDeviceName]
CRRemoteHostConnected RemoteHostInfo {remoteHostId = rhId} -> ["remote host " <> sShow rhId <> " connected"]
CRRemoteHostStopped rhId_ ->
[ maybe "new remote host" (mappend "remote host " . sShow) rhId_ <> " stopped"
CRRemoteHostStopped {remoteHostId_} ->
[ maybe "new remote host" (mappend "remote host " . sShow) remoteHostId_ <> " stopped"
]
CRRemoteFileStored rhId (CryptoFile filePath cfArgs_) ->
[plain $ "file " <> filePath <> " stored on remote host " <> show rhId]
@@ -309,8 +310,9 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
[ "remote controller " <> sShow remoteCtrlId <> " found: "
<> maybe (deviceName <> "not compatible") (\info -> viewRemoteCtrl info appVersion compatible) ctrlAppInfo_
]
<> [ "use " <> highlight ("/confirm remote ctrl " <> show remoteCtrlId) <> " to connect" | isJust ctrlAppInfo_ && compatible]
where
deviceName = if T.null ctrlDeviceName then "" else plain ctrlDeviceName <> ", "
deviceName = if T.null ctrlDeviceName then "" else plain ctrlDeviceName <> ", "
CRRemoteCtrlConnecting {remoteCtrl_, ctrlAppInfo, appVersion} ->
[ (maybe "connecting new remote controller" (\RemoteCtrlInfo {remoteCtrlId} -> "connecting remote controller " <> sShow remoteCtrlId) remoteCtrl_ <> ": ")
<> viewRemoteCtrl ctrlAppInfo appVersion True
@@ -322,7 +324,7 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
]
CRRemoteCtrlConnected RemoteCtrlInfo {remoteCtrlId = rcId, ctrlDeviceName} ->
["remote controller " <> sShow rcId <> " session started with " <> plain ctrlDeviceName]
CRRemoteCtrlStopped -> ["remote controller stopped"]
CRRemoteCtrlStopped {} -> ["remote controller stopped"]
CRSQLResult rows -> map plain rows
CRSlowSQLQueries {chatQueries, agentQueries} ->
let viewQuery SlowSQLQuery {query, queryStats = SlowQueryStats {count, timeMax, timeAvg}} =