diff --git a/src/Simplex/Chat/Terminal/Input.hs b/src/Simplex/Chat/Terminal/Input.hs index b7eebd141a..4262c5cd09 100644 --- a/src/Simplex/Chat/Terminal/Input.hs +++ b/src/Simplex/Chat/Terminal/Input.hs @@ -67,6 +67,7 @@ runInputLoop ct@ChatTerminal {termState, liveMessageState} cc = forever $ do Right r' -> processResp cmd rh r' Left _ -> when (isMessage cmd) $ echo s printRespToTerminal ct cc False rh r + chatResponseNotification ct r mapM_ (startLiveMessage cmd) r where echo s = printToTerminal ct [plain s] diff --git a/src/Simplex/Chat/Terminal/Notification.hs b/src/Simplex/Chat/Terminal/Notification.hs index 87bed5be1a..932eae32ef 100644 --- a/src/Simplex/Chat/Terminal/Notification.hs +++ b/src/Simplex/Chat/Terminal/Notification.hs @@ -6,25 +6,21 @@ module Simplex.Chat.Terminal.Notification (Notification (..), initializeNotifications) where -import Control.Monad (void) import Data.List (isInfixOf) -import Data.Map (Map, fromList) -import qualified Data.Map as M -import Data.Maybe (fromMaybe, isJust) -import Data.Text (Text) +import Data.Maybe (isJust) import qualified Data.Text as T import Simplex.Messaging.Util (catchAll_) import System.Directory (createDirectoryIfMissing, doesFileExist, findExecutable, getAppUserDataDirectory) import System.FilePath (combine) import System.Info (os) -import System.Process (readCreateProcess, shell) +import System.Process (callProcess) -data Notification = Notification {title :: Text, text :: Text} +data Notification = Notification {title :: T.Text, text :: T.Text} initializeNotifications :: IO (Notification -> IO ()) initializeNotifications = hideException <$> case os of - "darwin" -> pure $ notify macScript + "darwin" -> pure macNotify "mingw32" -> initWinNotify "linux" -> doesFileExist "/proc/sys/kernel/osrelease" >>= \case @@ -45,44 +41,36 @@ hideException f a = f a `catchAll_` pure () initLinuxNotify :: IO (Notification -> IO ()) initLinuxNotify = do found <- isJust <$> findExecutable "notify-send" - pure $ if found then notify linuxScript else noNotifications + pure $ if found then linuxNotify else noNotifications -notify :: (Notification -> Text) -> Notification -> IO () -notify script notification = - void $ readCreateProcess (shell . T.unpack $ script notification) "" +linuxNotify :: Notification -> IO () +linuxNotify Notification {title, text} = + callProcess "notify-send" [T.unpack title, T.unpack text] -linuxScript :: Notification -> Text -linuxScript Notification {title, text} = "notify-send '" <> linuxEscape title <> "' '" <> linuxEscape text <> "'" +macNotify :: Notification -> IO () +macNotify Notification {title, text} = + callProcess "osascript" ["-e", "display notification \"" <> macEscape text <> "\" with title \"" <> macEscape title <> "\""] -linuxEscape :: Text -> Text -linuxEscape = replaceAll $ fromList [('\'', "'\\''")] - -macScript :: Notification -> Text -macScript Notification {title, text} = "osascript -e 'display notification \"" <> macEscape text <> "\" with title \"" <> macEscape title <> "\"'" - -macEscape :: Text -> Text -macEscape = replaceAll $ fromList [('"', "\\\""), ('\'', "")] +macEscape :: T.Text -> String +macEscape = concatMap esc . T.unpack + where + esc '\\' = "\\\\" + esc '"' = "\\\"" + esc c = [c] initWslNotify :: IO (Notification -> IO ()) -initWslNotify = notify . wslScript <$> savePowershellScript +initWslNotify = wslNotify <$> savePowershellScript -wslScript :: FilePath -> Notification -> Text -wslScript path Notification {title, text} = "powershell.exe \"" <> T.pack path <> " \\\"" <> wslEscape title <> "\\\" \\\"" <> wslEscape text <> "\\\"\"" - -wslEscape :: Text -> Text -wslEscape = replaceAll $ fromList [('`', "\\`\\`"), ('\\', "\\\\"), ('"', "\\`\\\"")] +wslNotify :: FilePath -> Notification -> IO () +wslNotify path Notification {title, text} = + callProcess "powershell.exe" ["-File", path, T.unpack title, T.unpack text] initWinNotify :: IO (Notification -> IO ()) -initWinNotify = notify . winScript <$> savePowershellScript +initWinNotify = winNotify <$> savePowershellScript -winScript :: FilePath -> Notification -> Text -winScript path Notification {title, text} = "powershell.exe \"" <> T.pack path <> " '" <> winRemoveQuotes title <> "' '" <> winRemoveQuotes text <> "'\"" - -winRemoveQuotes :: Text -> Text -winRemoveQuotes = replaceAll $ fromList [('`', ""), ('\'', ""), ('"', "")] - -replaceAll :: Map Char Text -> Text -> Text -replaceAll rules = T.concatMap $ \c -> T.singleton c `fromMaybe` M.lookup c rules +winNotify :: FilePath -> Notification -> IO () +winNotify path Notification {title, text} = + callProcess "powershell.exe" ["-File", path, T.unpack title, T.unpack text] savePowershellScript :: IO FilePath savePowershellScript = do diff --git a/src/Simplex/Chat/Terminal/Output.hs b/src/Simplex/Chat/Terminal/Output.hs index ca591903ff..33e1fbe803 100644 --- a/src/Simplex/Chat/Terminal/Output.hs +++ b/src/Simplex/Chat/Terminal/Output.hs @@ -208,10 +208,22 @@ chatEventNotification t@ChatTerminal {sendNotification} cc = \case when (groupNtf u g False) $ sendNtf ("#" <> viewGroupName g, "member " <> viewMemberName m <> " is connected") CEvtReceivedContactRequest u UserContactRequest {localDisplayName = n} _ -> when (userNtf u) $ sendNtf (viewName n <> ">", "wants to connect to you") + CEvtDeletedMemberUser _u g m _withMessages -> + sendNtf ("#" <> viewGroupName g, viewMemberName m <> " removed you from the group") _ -> pure () where sendNtf = maybe (\_ -> pure ()) (. uncurry Notification) sendNotification +chatResponseNotification :: ChatTerminal -> Either ChatError ChatResponse -> IO () +chatResponseNotification ChatTerminal {sendNotification} = \case + Right r -> case r of + CRUserContactLinkCreated {} -> sendNtf ("SimpleX", "contact link created") + CRUserProfileUpdated {} -> sendNtf ("SimpleX", "profile updated") + _ -> pure () + Left _ -> pure () + where + sendNtf = maybe (\_ -> pure ()) (. uncurry Notification) sendNotification + msgText :: MsgContent -> Maybe MarkdownList -> Text msgText (MCFile _) _ = "wants to send a file" msgText mc md_ = maybe (msgContentText mc) (mconcat . map hideSecret) md_