{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UnicodeSyntax #-}
{-# LANGUAGE OverloadedRecordDot #-}
module MatrixBot.App
( runApp
) where
import GHC.Generics (Generic)
import Data.Aeson (ToJSON (..), FromJSON (..), eitherDecodeFileStrict)
import Data.Aeson.Text (encodeToLazyText)
import Data.String (IsString)
import Data.Text (Text, pack)
import Data.Text.Lazy (toStrict)
import qualified Data.Text.IO as TextIO
import Control.Lens.Lens (lens)
import Control.Monad.IO.Class
import Control.Monad.IO.Unlift (MonadUnliftIO)
import qualified Control.Exception.Safe as E
import qualified Control.Monad.Logger as ML
import qualified Control.Monad.Reader as MR
import System.Exit (ExitCode (..))
import System.IO
import MatrixBot.AesonUtils (myGenericToJSON, myGenericParseJSON)
import MatrixBot.Log
import MatrixBot.MatrixApi (EventResponse)
import qualified MatrixBot.Auth as Auth
import qualified MatrixBot.Bot as Bot
import qualified MatrixBot.Options as O
import qualified MatrixBot.SharedTypes as T
import MatrixBot.Bot.Jobs.Handlers.SendMessage (sendMessage, MessageEdit (..))
import qualified MatrixBot.Bot.Jobs.Queue as BotJobsQueue
import qualified Control.Lens as Lens
import qualified UnliftIO as UIO
type AppM m =
( MonadIO m
, MonadUnliftIO m
, MonadFail m
, E.MonadMask m
)
runApp ∷ AppM m ⇒ m ()
runApp :: forall (m :: * -> *). AppM m => m ()
runApp = m ()
forall {m :: * -> *}.
(MonadUnliftIO m, MonadFail m, MonadMask m) =>
m ()
go where
go :: m ()
go = do
logStateHandle ← m LogStateHandle
forall (m :: * -> *). MonadIO m => m LogStateHandle
createLogState
withLogger logStateHandle . MR.runReaderT $
E.catch (startApp logStateHandle) (exceptionHandler logStateHandle)
exceptionHandler
∷ (UIO.MonadUnliftIO m, E.MonadThrow m, ML.MonadLogger m)
⇒ LogStateHandle
→ E.SomeException
→ m ()
exceptionHandler :: forall (m :: * -> *).
(MonadUnliftIO m, MonadThrow m, MonadLogger m) =>
LogStateHandle -> SomeException -> m ()
exceptionHandler LogStateHandle
logStateHandle SomeException
e = do
LogStateHandle -> m ()
forall (m :: * -> *).
(MonadUnliftIO m, MonadLogger m) =>
LogStateHandle -> m ()
forceLogInitialization LogStateHandle
logStateHandle
case forall e. Exception e => SomeException -> Maybe e
E.fromException @ExitCode SomeException
e of
Just ExitCode
ExitSuccess → do
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m ()) -> (SomeException -> Text) -> SomeException -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
pack (String -> Text)
-> (SomeException -> String) -> SomeException -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String
"Application exits with: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<>) (String -> String)
-> (SomeException -> String) -> SomeException -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SomeException -> String
forall e. Exception e => e -> String
E.displayException (SomeException -> m ()) -> SomeException -> m ()
forall a b. (a -> b) -> a -> b
$ SomeException
e
SomeException -> m ()
forall (m :: * -> *) e a.
(HasCallStack, MonadThrow m, Exception e) =>
e -> m a
E.throwM SomeException
e
Maybe ExitCode
_ → do
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logError (Text -> m ()) -> (SomeException -> Text) -> SomeException -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
pack (String -> Text)
-> (SomeException -> String) -> SomeException -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String
"Application failed with: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<>) (String -> String)
-> (SomeException -> String) -> SomeException -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SomeException -> String
forall e. Exception e => e -> String
E.displayException (SomeException -> m ()) -> SomeException -> m ()
forall a b. (a -> b) -> a -> b
$ SomeException
e
SomeException -> m ()
forall (m :: * -> *) e a.
(HasCallStack, MonadThrow m, Exception e) =>
e -> m a
E.throwM SomeException
e
startApp ∷ (AppM m, ML.MonadLogger m) ⇒ LogStateHandle → m ()
startApp :: forall (m :: * -> *).
(AppM m, MonadLogger m) =>
LogStateHandle -> m ()
startApp LogStateHandle
logStateHandle = do
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug Text
"Starting the application…"
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug Text
"Parsing the command-line arguments…"
m AppCommand
forall (m :: * -> *). MonadIO m => m AppCommand
O.parseAppCommand m AppCommand -> (AppCommand -> m ()) -> m ()
forall a b. m a -> (a -> m b) -> m b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
O.AppCommandAuth AuthOptions
opts → do
LogStateHandle -> Maybe LogLevel -> m ()
forall (m :: * -> *).
(MonadUnliftIO m, MonadLogger m) =>
LogStateHandle -> Maybe LogLevel -> m ()
setLogLevel LogStateHandle
logStateHandle AuthOptions
opts.authOptionsLogLevel
AuthOptions -> m ()
forall (m :: * -> *).
(MonadIO m, MonadUnliftIO m, MonadFail m, MonadThrow m,
MonadLogger m) =>
AuthOptions -> m ()
runAuth AuthOptions
opts
O.AppCommandStart StartOptions
opts → do
LogStateHandle -> Maybe LogLevel -> m ()
forall (m :: * -> *).
(MonadUnliftIO m, MonadLogger m) =>
LogStateHandle -> Maybe LogLevel -> m ()
setLogLevel LogStateHandle
logStateHandle StartOptions
opts.startOptionsLogLevel
StartOptions -> m ()
forall (m :: * -> *).
(MonadIO m, MonadFail m, MonadMask m, MonadUnliftIO m,
MonadLogger m) =>
StartOptions -> m ()
runStart StartOptions
opts
O.AppCommandSendMessage SendMessageOptions
opts → do
LogStateHandle -> Maybe LogLevel -> m ()
forall (m :: * -> *).
(MonadUnliftIO m, MonadLogger m) =>
LogStateHandle -> Maybe LogLevel -> m ()
setLogLevel LogStateHandle
logStateHandle SendMessageOptions
opts.sendMessageOptionsLogLevel
SendMessageOptions -> m ()
forall (m :: * -> *).
(MonadIO m, MonadUnliftIO m, MonadFail m, MonadThrow m,
MonadLogger m) =>
SendMessageOptions -> m ()
runSendMessage SendMessageOptions
opts
O.AppCommandEditMessage EditMessageOptions
opts → do
LogStateHandle -> Maybe LogLevel -> m ()
forall (m :: * -> *).
(MonadUnliftIO m, MonadLogger m) =>
LogStateHandle -> Maybe LogLevel -> m ()
setLogLevel LogStateHandle
logStateHandle EditMessageOptions
opts.editMessageOptionsLogLevel
EditMessageOptions -> m ()
forall (m :: * -> *).
(MonadIO m, MonadUnliftIO m, MonadFail m, MonadThrow m,
MonadLogger m) =>
EditMessageOptions -> m ()
runEditMessage EditMessageOptions
opts
runAuth
∷ (MonadIO m, MonadUnliftIO m, MonadFail m, E.MonadThrow m, ML.MonadLogger m)
⇒ O.AuthOptions
→ m ()
runAuth :: forall (m :: * -> *).
(MonadIO m, MonadUnliftIO m, MonadFail m, MonadThrow m,
MonadLogger m) =>
AuthOptions -> m ()
runAuth AuthOptions
opts = do
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logInfo Text
"Running authentication…"
let quotedMxid :: Text
quotedMxid = Text -> Text
forall s. (IsString s, Show s) => s -> Text
quoted (Text -> Text) -> (AuthOptions -> Text) -> AuthOptions -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Mxid -> Text
T.printMxid (Mxid -> Text) -> (AuthOptions -> Mxid) -> AuthOptions -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AuthOptions -> Mxid
O.authOptionsMxid (AuthOptions -> Text) -> AuthOptions -> Text
forall a b. (a -> b) -> a -> b
$ AuthOptions
opts
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ Text
"MXID: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
quotedMxid
password ←
case AuthOptions -> Either Password String
O.authOptionsPassword AuthOptions
opts of
Left Password
x →
Password
x Password -> m () -> m Password
forall a b. a -> m b -> m a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text
"Password for " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
quotedMxid Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" was provided as an option argument")
Right String
file → do
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ Text
"Reading password for " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
quotedMxid Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" from " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
forall s. (IsString s, Show s) => s -> Text
quoted String
file Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"…"
handle ← IO Handle -> m Handle
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Handle -> m Handle) -> IO Handle -> m Handle
forall a b. (a -> b) -> a -> b
$ String -> IOMode -> IO Handle
openFile String
file IOMode
ReadMode
liftIO $ T.Password <$> TextIO.hGetLine handle
credentials ← Auth.authenticate (O.authOptionsMxid opts) password
logDebug "Received credentials"
logDebug $ "Saving credentials to " <> (quoted . O.authOptionsOutputFile) opts <> "…"
liftIO . TextIO.writeFile (O.authOptionsOutputFile opts) . toStrict . encodeToLazyText $ credentials
logInfo "Success!"
runStart
∷ (MonadIO m, MonadFail m, E.MonadMask m, MonadUnliftIO m, ML.MonadLogger m)
⇒ O.StartOptions
→ m ()
runStart :: forall (m :: * -> *).
(MonadIO m, MonadFail m, MonadMask m, MonadUnliftIO m,
MonadLogger m) =>
StartOptions -> m ()
runStart StartOptions
opts = do
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logInfo Text
"Initializing the bot…"
let
retryLimit :: RetryLimit
retryLimit = StartOptions -> RetryLimit
O.startOptionsRetryLimit StartOptions
opts
retryDelay :: RetryDelay
retryDelay = StartOptions -> RetryDelay
O.startOptionsRetryDelay StartOptions
opts
eventTokenFile :: Maybe String
eventTokenFile = StartOptions -> Maybe String
O.startOptionsEventTokenFile StartOptions
opts
eventsTimeout :: EventsTimeout
eventsTimeout = StartOptions -> EventsTimeout
O.startOptionsEventsTimeout StartOptions
opts
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"Failed Matrix API call retry limit: "
, String -> Text
pack (String -> Text) -> (RetryLimit -> String) -> RetryLimit -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Natural -> String
forall a. Show a => a -> String
show (Natural -> String)
-> (RetryLimit -> Natural) -> RetryLimit -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RetryLimit -> Natural
T.unRetryLimit (RetryLimit -> Text) -> RetryLimit -> Text
forall a b. (a -> b) -> a -> b
$ RetryLimit
retryLimit
, Text
" (amount of retries before bot fails completely)"
]
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"Failed Matrix API call retry interval: "
, RetryDelay -> Text
forall s. IsString s => RetryDelay -> s
T.printRetryDelaySeconds RetryDelay
retryDelay
]
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"Matrix events listening timeout: "
, String -> Text
pack (String -> Text)
-> (EventsTimeout -> String) -> EventsTimeout -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Integer -> String
forall a. Show a => a -> String
show (Integer -> String)
-> (EventsTimeout -> Integer) -> EventsTimeout -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Seconds -> Integer
T.unSeconds (Seconds -> Integer)
-> (EventsTimeout -> Seconds) -> EventsTimeout -> Integer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EventsTimeout -> Seconds
T.unEventsTimeout (EventsTimeout -> Text) -> EventsTimeout -> Text
forall a b. (a -> b) -> a -> b
$ EventsTimeout
eventsTimeout
, Text
" second(s)"
]
case Maybe String
eventTokenFile of
Maybe String
Nothing →
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logWarn (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"There’s no event token file provided, "
, Text
"will start listening from next following events"
]
Just String
x →
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"Event token file where to read from and save to last event token that is used "
, Text
"as a starting point to get next events from: ", String -> Text
pack (String -> Text) -> (String -> String) -> String -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
forall a. Show a => a -> String
show (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ String
x
]
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"Reading and parsing credentials "
, String -> Text
forall s. (IsString s, Show s) => s -> Text
quoted (String -> Text)
-> (StartOptions -> String) -> StartOptions -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. StartOptions -> String
O.startOptionsCredentialsFile (StartOptions -> Text) -> StartOptions -> Text
forall a b. (a -> b) -> a -> b
$ StartOptions
opts
, Text
" file…"
]
credentials ←
(String -> m Credentials)
-> (Credentials -> m Credentials)
-> Either String Credentials
-> m Credentials
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either String -> m Credentials
forall a. String -> m a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail Credentials -> m Credentials
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either String Credentials -> m Credentials)
-> m (Either String Credentials) -> m Credentials
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< IO (Either String Credentials) -> m (Either String Credentials)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (String -> IO (Either String Credentials)
forall a. FromJSON a => String -> IO (Either String a)
eitherDecodeFileStrict (String -> IO (Either String Credentials))
-> (StartOptions -> String)
-> StartOptions
-> IO (Either String Credentials)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. StartOptions -> String
O.startOptionsCredentialsFile (StartOptions -> IO (Either String Credentials))
-> StartOptions -> IO (Either String Credentials)
forall a b. (a -> b) -> a -> b
$ StartOptions
opts)
let
quotedMxid
= Text -> Text
forall s. (IsString s, Show s) => s -> Text
quoted (Text -> Text) -> (Mxid -> Text) -> Mxid -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Mxid -> Text
T.printMxid
(Mxid -> Text) -> Mxid -> Text
forall a b. (a -> b) -> a -> b
$ Username -> HomeServer -> Mxid
T.Mxid (Credentials -> Username
Auth.credentialsUsername Credentials
credentials) (Credentials -> HomeServer
Auth.credentialsHomeServer Credentials
credentials)
logDebug $ "MXID: " <> quotedMxid
logDebug $ mconcat
[ "Reading and parsing bot configuration from "
, quoted . O.startOptionsBotConfigFile $ opts
, " file…"
]
botConfig ←
either fail pure =<< liftIO (eitherDecodeFileStrict . O.startOptionsBotConfigFile $ opts)
botJobsQueue ← BotJobsQueue.mkBotJobsQueue
logInfo "Listening to events…"
Bot.startTheBot eventTokenFile eventsTimeout botConfig
`MR.runReaderT` BotEnv credentials retryLimit retryDelay botJobsQueue
runSendMessage
∷ (MonadIO m, MonadUnliftIO m, MonadFail m, E.MonadThrow m, ML.MonadLogger m)
⇒ O.SendMessageOptions
→ m ()
runSendMessage :: forall (m :: * -> *).
(MonadIO m, MonadUnliftIO m, MonadFail m, MonadThrow m,
MonadLogger m) =>
SendMessageOptions -> m ()
runSendMessage SendMessageOptions
opts = do
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logInfo Text
"Running send message command…"
let roomId :: RoomId
roomId = SendMessageOptions -> RoomId
O.sendMessageOptionsRoomId SendMessageOptions
opts
credentials ∷ Auth.Credentials ← do
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"Reading and parsing credentials "
, String -> Text
forall s. (IsString s, Show s) => s -> Text
quoted (String -> Text)
-> (SendMessageOptions -> String) -> SendMessageOptions -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SendMessageOptions -> String
O.sendMessageOptionsCredentialsFile (SendMessageOptions -> Text) -> SendMessageOptions -> Text
forall a b. (a -> b) -> a -> b
$ SendMessageOptions
opts
, Text
" file…"
]
(String -> m Credentials)
-> (Credentials -> m Credentials)
-> Either String Credentials
-> m Credentials
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either String -> m Credentials
forall a. String -> m a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail Credentials -> m Credentials
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either String Credentials -> m Credentials)
-> m (Either String Credentials) -> m Credentials
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< IO (Either String Credentials) -> m (Either String Credentials)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (String -> IO (Either String Credentials)
forall a. FromJSON a => String -> IO (Either String a)
eitherDecodeFileStrict (String -> IO (Either String Credentials))
-> (SendMessageOptions -> String)
-> SendMessageOptions
-> IO (Either String Credentials)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SendMessageOptions -> String
O.sendMessageOptionsCredentialsFile (SendMessageOptions -> IO (Either String Credentials))
-> SendMessageOptions -> IO (Either String Credentials)
forall a b. (a -> b) -> a -> b
$ SendMessageOptions
opts)
message ←
case O.sendMessageOptionsMessage opts of
Left Text
x →
(Text
x Text -> m () -> m Text
forall a b. a -> m b -> m a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$) (m () -> m Text) -> (Text -> m ()) -> Text -> m Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m Text) -> Text -> m Text
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"Message to send to ", String -> Text
pack (String -> Text) -> (RoomId -> String) -> RoomId -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
forall a. Show a => a -> String
show (Text -> String) -> (RoomId -> Text) -> RoomId -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RoomId -> Text
T.printRoomId (RoomId -> Text) -> RoomId -> Text
forall a b. (a -> b) -> a -> b
$ RoomId
roomId
, Text
" room was provided as an option argument"
]
Right String
file → do
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"Reading message to send to ", String -> Text
pack (String -> Text) -> (RoomId -> String) -> RoomId -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
forall a. Show a => a -> String
show (Text -> String) -> (RoomId -> Text) -> RoomId -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RoomId -> Text
T.printRoomId (RoomId -> Text) -> RoomId -> Text
forall a b. (a -> b) -> a -> b
$ RoomId
roomId
, Text
" room from ", String -> Text
pack (String -> Text) -> (String -> String) -> String -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
forall a. Show a => a -> String
show (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ String
file, Text
" file…"
]
IO Text -> m Text
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Text -> m Text) -> IO Text -> m Text
forall a b. (a -> b) -> a -> b
$ String -> IO Text
TextIO.readFile String
file
htmlMessage ←
case O.sendMessageOptionsHtmlMessage opts of
Maybe (Either Text String)
Nothing → Maybe Text -> m (Maybe Text)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe Text
forall a. Maybe a
Nothing
Just (Left Text
x) →
(Text -> Maybe Text
forall a. a -> Maybe a
Just Text
x Maybe Text -> m () -> m (Maybe Text)
forall a b. a -> m b -> m a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$) (m () -> m (Maybe Text))
-> (Text -> m ()) -> Text -> m (Maybe Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m (Maybe Text)) -> Text -> m (Maybe Text)
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"HTML-formatted message to send to "
, (String -> Text
pack (String -> Text) -> (RoomId -> String) -> RoomId -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
forall a. Show a => a -> String
show (Text -> String) -> (RoomId -> Text) -> RoomId -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RoomId -> Text
T.printRoomId) RoomId
roomId
, Text
" room was provided as an option argument"
]
Just (Right String
file) → do
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"Reading HTML-formatted message to send to "
, (String -> Text
pack (String -> Text) -> (RoomId -> String) -> RoomId -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
forall a. Show a => a -> String
show (Text -> String) -> (RoomId -> Text) -> RoomId -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RoomId -> Text
T.printRoomId) RoomId
roomId
, Text
" room from "
, (String -> Text
pack (String -> Text) -> (String -> String) -> String -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
forall a. Show a => a -> String
show) String
file
, Text
" file…"
]
(Text -> Maybe Text) -> m Text -> m (Maybe Text)
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Text -> Maybe Text
forall a. a -> Maybe a
Just (m Text -> m (Maybe Text))
-> (IO Text -> m Text) -> IO Text -> m (Maybe Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO Text -> m Text
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Text -> m (Maybe Text)) -> IO Text -> m (Maybe Text)
forall a b. (a -> b) -> a -> b
$ String -> IO Text
TextIO.readFile String
file
replyTo ←
case O.sendMessageOptionsReplyTo opts of
Maybe EventId
Nothing → Maybe EventId -> m (Maybe EventId)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe EventId
forall a. Maybe a
Nothing
Just EventId
x → (EventId -> Maybe EventId
forall a. a -> Maybe a
Just EventId
x Maybe EventId -> m () -> m (Maybe EventId)
forall a b. a -> m b -> m a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$) (m () -> m (Maybe EventId)) -> m () -> m (Maybe EventId)
forall a b. (a -> b) -> a -> b
$ Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat [Text
"Replying to ", (String -> Text
pack (String -> Text) -> (EventId -> String) -> EventId -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EventId -> String
forall a. Show a => a -> String
show) EventId
x]
flip MR.runReaderT credentials $
Bot.withReqAndAuth (T.EventsTimeout . T.Seconds $ 30) $ \MatrixApiClient
req AuthenticatedRequest (AuthProtect "access-token")
auth → do
transactionId ←
case SendMessageOptions -> Maybe TransactionId
O.sendMessageOptionsTransactionId SendMessageOptions
opts of
Maybe TransactionId
Nothing → do
txid ← ReaderT Credentials m TransactionId
forall (m :: * -> *). MonadIO m => m TransactionId
T.genTransactionId
(txid <$) . logDebug $ mconcat
[ "No transaction ID was provided in the command-line options, generated new one: "
, pack . show . T.unTransactionId $ txid
]
Just TransactionId
txid → do
(TransactionId
txid TransactionId
-> ReaderT Credentials m () -> ReaderT Credentials m TransactionId
forall a b. a -> ReaderT Credentials m b -> ReaderT Credentials m a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$) (ReaderT Credentials m () -> ReaderT Credentials m TransactionId)
-> (Text -> ReaderT Credentials m ())
-> Text
-> ReaderT Credentials m TransactionId
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ReaderT Credentials m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> ReaderT Credentials m TransactionId)
-> Text -> ReaderT Credentials m TransactionId
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"Using transaction ID provided in the command-line options: "
, String -> Text
pack (String -> Text)
-> (TransactionId -> String) -> TransactionId -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UUID -> String
forall a. Show a => a -> String
show (UUID -> String)
-> (TransactionId -> UUID) -> TransactionId -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TransactionId -> UUID
T.unTransactionId (TransactionId -> Text) -> TransactionId -> Text
forall a b. (a -> b) -> a -> b
$ TransactionId
txid
]
response ← sendMessage req auth transactionId roomId replyTo htmlMessage message Nothing
logDebug "Printing response and transaction ID to stdout…"
liftIO . TextIO.putStrLn . toStrict . encodeToLazyText $ SendMessageResponse
{ sendMessageResponseTransactionId = transactionId
, sendMessageResponseResponse = response
}
logInfo "Success!"
runEditMessage
∷ (MonadIO m, MonadUnliftIO m, MonadFail m, E.MonadThrow m, ML.MonadLogger m)
⇒ O.EditMessageOptions
→ m ()
runEditMessage :: forall (m :: * -> *).
(MonadIO m, MonadUnliftIO m, MonadFail m, MonadThrow m,
MonadLogger m) =>
EditMessageOptions -> m ()
runEditMessage EditMessageOptions
opts = do
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logInfo Text
"Running edit message command…"
let roomId :: RoomId
roomId = EditMessageOptions -> RoomId
O.editMessageOptionsRoomId EditMessageOptions
opts
credentials ∷ Auth.Credentials ← do
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"Reading and parsing credentials "
, (String -> Text
forall s. (IsString s, Show s) => s -> Text
quoted (String -> Text)
-> (EditMessageOptions -> String) -> EditMessageOptions -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EditMessageOptions -> String
O.editMessageOptionsCredentialsFile) EditMessageOptions
opts
, Text
" file…"
]
(String -> m Credentials)
-> (Credentials -> m Credentials)
-> Either String Credentials
-> m Credentials
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either String -> m Credentials
forall a. String -> m a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail Credentials -> m Credentials
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either String Credentials -> m Credentials)
-> m (Either String Credentials) -> m Credentials
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< IO (Either String Credentials) -> m (Either String Credentials)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (String -> IO (Either String Credentials)
forall a. FromJSON a => String -> IO (Either String a)
eitherDecodeFileStrict (String -> IO (Either String Credentials))
-> (EditMessageOptions -> String)
-> EditMessageOptions
-> IO (Either String Credentials)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EditMessageOptions -> String
O.editMessageOptionsCredentialsFile (EditMessageOptions -> IO (Either String Credentials))
-> EditMessageOptions -> IO (Either String Credentials)
forall a b. (a -> b) -> a -> b
$ EditMessageOptions
opts)
message ←
case O.editMessageOptionsMessage opts of
Left Text
x →
(Text
x Text -> m () -> m Text
forall a b. a -> m b -> m a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$) (m () -> m Text) -> (Text -> m ()) -> Text -> m Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m Text) -> Text -> m Text
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"New message body for the message "
, (String -> Text
pack (String -> Text)
-> (EditMessageOptions -> String) -> EditMessageOptions -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EventId -> String
forall a. Show a => a -> String
show (EventId -> String)
-> (EditMessageOptions -> EventId) -> EditMessageOptions -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EditMessageOptions -> EventId
O.editMessageOptionsMessageId) EditMessageOptions
opts
, Text
" in the room "
, (Text -> Text
forall s. (IsString s, Show s) => s -> Text
quoted (Text -> Text) -> (RoomId -> Text) -> RoomId -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RoomId -> Text
T.printRoomId) RoomId
roomId
, Text
" was provided as an option argument"
]
Right String
file → do
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"Reading new message message body for the message "
, (String -> Text
pack (String -> Text)
-> (EditMessageOptions -> String) -> EditMessageOptions -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EventId -> String
forall a. Show a => a -> String
show (EventId -> String)
-> (EditMessageOptions -> EventId) -> EditMessageOptions -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EditMessageOptions -> EventId
O.editMessageOptionsMessageId) EditMessageOptions
opts
, Text
" in the room "
, (Text -> Text
forall s. (IsString s, Show s) => s -> Text
quoted (Text -> Text) -> (RoomId -> Text) -> RoomId -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RoomId -> Text
T.printRoomId) RoomId
roomId
, Text
" from ", String -> Text
forall s. (IsString s, Show s) => s -> Text
quoted String
file, Text
" file…"
]
IO Text -> m Text
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Text -> m Text) -> IO Text -> m Text
forall a b. (a -> b) -> a -> b
$ String -> IO Text
TextIO.readFile String
file
htmlMessage ←
case O.editMessageOptionsHtmlMessage opts of
Maybe (Either Text String)
Nothing → Maybe Text -> m (Maybe Text)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe Text
forall a. Maybe a
Nothing
Just (Left Text
x) →
(Text -> Maybe Text
forall a. a -> Maybe a
Just Text
x Maybe Text -> m () -> m (Maybe Text)
forall a b. a -> m b -> m a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$) (m () -> m (Maybe Text))
-> (Text -> m ()) -> Text -> m (Maybe Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m (Maybe Text)) -> Text -> m (Maybe Text)
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"New HTML-formatted message body for the message "
, (String -> Text
pack (String -> Text)
-> (EditMessageOptions -> String) -> EditMessageOptions -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EventId -> String
forall a. Show a => a -> String
show (EventId -> String)
-> (EditMessageOptions -> EventId) -> EditMessageOptions -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EditMessageOptions -> EventId
O.editMessageOptionsMessageId) EditMessageOptions
opts
, Text
" in the room "
, (Text -> Text
forall s. (IsString s, Show s) => s -> Text
quoted (Text -> Text) -> (RoomId -> Text) -> RoomId -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RoomId -> Text
T.printRoomId) RoomId
roomId
, Text
" was provided as an option argument"
]
Just (Right String
file) → do
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"Reading new HTML-formatted message body for the message "
, (String -> Text
pack (String -> Text)
-> (EditMessageOptions -> String) -> EditMessageOptions -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EventId -> String
forall a. Show a => a -> String
show (EventId -> String)
-> (EditMessageOptions -> EventId) -> EditMessageOptions -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EditMessageOptions -> EventId
O.editMessageOptionsMessageId) EditMessageOptions
opts
, Text
" in the room "
, (Text -> Text
forall s. (IsString s, Show s) => s -> Text
quoted (Text -> Text) -> (RoomId -> Text) -> RoomId -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RoomId -> Text
T.printRoomId) RoomId
roomId
, Text
" from ", String -> Text
forall s. (IsString s, Show s) => s -> Text
quoted String
file, Text
" file…"
]
(Text -> Maybe Text) -> m Text -> m (Maybe Text)
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Text -> Maybe Text
forall a. a -> Maybe a
Just (m Text -> m (Maybe Text))
-> (IO Text -> m Text) -> IO Text -> m (Maybe Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO Text -> m Text
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Text -> m (Maybe Text)) -> IO Text -> m (Maybe Text)
forall a b. (a -> b) -> a -> b
$ String -> IO Text
TextIO.readFile String
file
compatMessage ←
case O.editMessageOptionsMessageCompat opts of
Maybe (Either Text String)
Nothing → Text -> m Text
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> Text
forall {a}. (Semigroup a, IsString a) => a -> a
compatTextDefaultTemplate Text
message)
Just (Left Text
x) →
(Text
x Text -> m () -> m Text
forall a b. a -> m b -> m a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$) (m () -> m Text) -> (Text -> m ()) -> Text -> m Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m Text) -> Text -> m Text
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"New old API-compatible message body for the message "
, (String -> Text
pack (String -> Text)
-> (EditMessageOptions -> String) -> EditMessageOptions -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EventId -> String
forall a. Show a => a -> String
show (EventId -> String)
-> (EditMessageOptions -> EventId) -> EditMessageOptions -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EditMessageOptions -> EventId
O.editMessageOptionsMessageId) EditMessageOptions
opts
, Text
" in the room "
, (Text -> Text
forall s. (IsString s, Show s) => s -> Text
quoted (Text -> Text) -> (RoomId -> Text) -> RoomId -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RoomId -> Text
T.printRoomId) RoomId
roomId
, Text
" was provided as an option argument"
]
Just (Right String
file) → do
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"Reading new old API-compatible message message body for the message "
, (String -> Text
pack (String -> Text)
-> (EditMessageOptions -> String) -> EditMessageOptions -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EventId -> String
forall a. Show a => a -> String
show (EventId -> String)
-> (EditMessageOptions -> EventId) -> EditMessageOptions -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EditMessageOptions -> EventId
O.editMessageOptionsMessageId) EditMessageOptions
opts
, Text
" in the room "
, (Text -> Text
forall s. (IsString s, Show s) => s -> Text
quoted (Text -> Text) -> (RoomId -> Text) -> RoomId -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RoomId -> Text
T.printRoomId) RoomId
roomId
, Text
" from ", String -> Text
forall s. (IsString s, Show s) => s -> Text
quoted String
file, Text
" file…"
]
IO Text -> m Text
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Text -> m Text) -> IO Text -> m Text
forall a b. (a -> b) -> a -> b
$ String -> IO Text
TextIO.readFile String
file
compatHtmlMessage ←
case (O.editMessageOptionsHtmlMessageCompat opts, htmlMessage) of
(Maybe (Either Text String)
Nothing, Maybe Text
Nothing) → Maybe Text -> m (Maybe Text)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe Text
forall a. Maybe a
Nothing
(Maybe (Either Text String)
Nothing, Just Text
newHtmlBody) → (Maybe Text -> m (Maybe Text)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe Text -> m (Maybe Text))
-> (Text -> Maybe Text) -> Text -> m (Maybe Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Maybe Text
forall a. a -> Maybe a
Just (Text -> Maybe Text) -> (Text -> Text) -> Text -> Maybe Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
forall {a}. (Semigroup a, IsString a) => a -> a
compatHtmlDefaultTemplate) Text
newHtmlBody
(Just (Left Text
x), Maybe Text
_) →
(Text -> Maybe Text
forall a. a -> Maybe a
Just Text
x Maybe Text -> m () -> m (Maybe Text)
forall a b. a -> m b -> m a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$) (m () -> m (Maybe Text))
-> (Text -> m ()) -> Text -> m (Maybe Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m (Maybe Text)) -> Text -> m (Maybe Text)
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"New old API-compatible HTML-formatted message body for the message "
, (String -> Text
pack (String -> Text)
-> (EditMessageOptions -> String) -> EditMessageOptions -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EventId -> String
forall a. Show a => a -> String
show (EventId -> String)
-> (EditMessageOptions -> EventId) -> EditMessageOptions -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EditMessageOptions -> EventId
O.editMessageOptionsMessageId) EditMessageOptions
opts
, Text
" in the room "
, (Text -> Text
forall s. (IsString s, Show s) => s -> Text
quoted (Text -> Text) -> (RoomId -> Text) -> RoomId -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RoomId -> Text
T.printRoomId) RoomId
roomId
, Text
" was provided as an option argument"
]
(Just (Right String
file), Maybe Text
_) → do
Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"Reading new old API-compatible HTML-formatted message body for the message "
, (String -> Text
pack (String -> Text)
-> (EditMessageOptions -> String) -> EditMessageOptions -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EventId -> String
forall a. Show a => a -> String
show (EventId -> String)
-> (EditMessageOptions -> EventId) -> EditMessageOptions -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EditMessageOptions -> EventId
O.editMessageOptionsMessageId) EditMessageOptions
opts
, Text
" in the room "
, (Text -> Text
forall s. (IsString s, Show s) => s -> Text
quoted (Text -> Text) -> (RoomId -> Text) -> RoomId -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RoomId -> Text
T.printRoomId) RoomId
roomId
, Text
" from ", String -> Text
forall s. (IsString s, Show s) => s -> Text
quoted String
file, Text
" file…"
]
(Text -> Maybe Text) -> m Text -> m (Maybe Text)
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Text -> Maybe Text
forall a. a -> Maybe a
Just (m Text -> m (Maybe Text))
-> (IO Text -> m Text) -> IO Text -> m (Maybe Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO Text -> m Text
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Text -> m (Maybe Text)) -> IO Text -> m (Maybe Text)
forall a b. (a -> b) -> a -> b
$ String -> IO Text
TextIO.readFile String
file
replyTo ←
case O.editMessageOptionsReplyTo opts of
Maybe EventId
Nothing → Maybe EventId -> m (Maybe EventId)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe EventId
forall a. Maybe a
Nothing
Just EventId
x → (EventId -> Maybe EventId
forall a. a -> Maybe a
Just EventId
x Maybe EventId -> m () -> m (Maybe EventId)
forall a b. a -> m b -> m a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$) (m () -> m (Maybe EventId)) -> m () -> m (Maybe EventId)
forall a b. (a -> b) -> a -> b
$ Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat [Text
"Replying to ", (String -> Text
pack (String -> Text) -> (EventId -> String) -> EventId -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EventId -> String
forall a. Show a => a -> String
show) EventId
x]
flip MR.runReaderT credentials $
Bot.withReqAndAuth (T.EventsTimeout . T.Seconds $ 30) $ \MatrixApiClient
req AuthenticatedRequest (AuthProtect "access-token")
auth → do
transactionId ←
case EditMessageOptions -> Maybe TransactionId
O.editMessageOptionsTransactionId EditMessageOptions
opts of
Maybe TransactionId
Nothing → do
txid ← ReaderT Credentials m TransactionId
forall (m :: * -> *). MonadIO m => m TransactionId
T.genTransactionId
(txid <$) . logDebug $ mconcat
[ "No transaction ID was provided in the command-line options, generated new one: "
, (pack . show . T.unTransactionId) txid
]
Just TransactionId
txid → do
(TransactionId
txid TransactionId
-> ReaderT Credentials m () -> ReaderT Credentials m TransactionId
forall a b. a -> ReaderT Credentials m b -> ReaderT Credentials m a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$) (ReaderT Credentials m () -> ReaderT Credentials m TransactionId)
-> (Text -> ReaderT Credentials m ())
-> Text
-> ReaderT Credentials m TransactionId
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ReaderT Credentials m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
logDebug (Text -> ReaderT Credentials m TransactionId)
-> Text -> ReaderT Credentials m TransactionId
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
[ Text
"Using transaction ID provided in the command-line options: "
, (String -> Text
pack (String -> Text)
-> (TransactionId -> String) -> TransactionId -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UUID -> String
forall a. Show a => a -> String
show (UUID -> String)
-> (TransactionId -> UUID) -> TransactionId -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TransactionId -> UUID
T.unTransactionId) TransactionId
txid
]
response ←
sendMessage req auth transactionId roomId replyTo compatHtmlMessage compatMessage $
Just MessageEdit
{ messageEditMessageId = O.editMessageOptionsMessageId opts
, messageEditNewText = message
, messageEditNewHtml = htmlMessage
}
logDebug "Printing response and transaction ID to stdout…"
liftIO . TextIO.putStrLn . toStrict . encodeToLazyText $ SendMessageResponse
{ sendMessageResponseTransactionId = transactionId
, sendMessageResponseResponse = response
}
logInfo "Success!"
where
compatTextDefaultTemplate :: a -> a
compatTextDefaultTemplate = (a
"EDIT: " a -> a -> a
forall a. Semigroup a => a -> a -> a
<>)
compatHtmlDefaultTemplate :: a -> a
compatHtmlDefaultTemplate = (a
"<b>EDIT:</b> " a -> a -> a
forall a. Semigroup a => a -> a -> a
<>)
data BotEnv = BotEnv
{ BotEnv -> Credentials
botEnvCredentials ∷ Auth.Credentials
, BotEnv -> RetryLimit
botEnvRetryLimit ∷ T.RetryLimit
, BotEnv -> RetryDelay
botEnvRetryDelay ∷ T.RetryDelay
, BotEnv -> BotJobsQueue
botEnvJobsQueue ∷ BotJobsQueue.BotJobsQueue
}
instance Auth.HasCredentials BotEnv where
credentials :: Lens' BotEnv Credentials
credentials = (BotEnv -> Credentials)
-> (BotEnv -> Credentials -> BotEnv) -> Lens' BotEnv Credentials
forall s a b t. (s -> a) -> (s -> b -> t) -> Lens s t a b
lens BotEnv -> Credentials
botEnvCredentials ((BotEnv -> Credentials -> BotEnv) -> Lens' BotEnv Credentials)
-> (BotEnv -> Credentials -> BotEnv) -> Lens' BotEnv Credentials
forall a b. (a -> b) -> a -> b
$ \BotEnv
x Credentials
v → BotEnv
x { botEnvCredentials = v }
instance T.HasRetryParams BotEnv where
retryLimit :: Lens' BotEnv RetryLimit
retryLimit = (BotEnv -> RetryLimit)
-> (BotEnv -> RetryLimit -> BotEnv) -> Lens' BotEnv RetryLimit
forall s a b t. (s -> a) -> (s -> b -> t) -> Lens s t a b
lens BotEnv -> RetryLimit
botEnvRetryLimit ((BotEnv -> RetryLimit -> BotEnv) -> Lens' BotEnv RetryLimit)
-> (BotEnv -> RetryLimit -> BotEnv) -> Lens' BotEnv RetryLimit
forall a b. (a -> b) -> a -> b
$ \BotEnv
x RetryLimit
v → BotEnv
x { botEnvRetryLimit = v }
retryDelay :: Lens' BotEnv RetryDelay
retryDelay = (BotEnv -> RetryDelay)
-> (BotEnv -> RetryDelay -> BotEnv) -> Lens' BotEnv RetryDelay
forall s a b t. (s -> a) -> (s -> b -> t) -> Lens s t a b
lens BotEnv -> RetryDelay
botEnvRetryDelay ((BotEnv -> RetryDelay -> BotEnv) -> Lens' BotEnv RetryDelay)
-> (BotEnv -> RetryDelay -> BotEnv) -> Lens' BotEnv RetryDelay
forall a b. (a -> b) -> a -> b
$ \BotEnv
x RetryDelay
v → BotEnv
x { botEnvRetryDelay = v }
instance BotJobsQueue.HasBotJobsReader BotEnv where
botJobsReader :: Getter BotEnv (STM BotJob)
botJobsReader = (BotEnv -> BotJobsQueue)
-> (BotJobsQueue -> f BotJobsQueue) -> BotEnv -> f BotEnv
forall (p :: * -> * -> *) (f :: * -> *) s a.
(Profunctor p, Contravariant f) =>
(s -> a) -> Optic' p f s a
Lens.to BotEnv -> BotJobsQueue
botEnvJobsQueue ((BotJobsQueue -> f BotJobsQueue) -> BotEnv -> f BotEnv)
-> ((STM BotJob -> f (STM BotJob))
-> BotJobsQueue -> f BotJobsQueue)
-> (STM BotJob -> f (STM BotJob))
-> BotEnv
-> f BotEnv
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (STM BotJob -> f (STM BotJob)) -> BotJobsQueue -> f BotJobsQueue
forall r. HasBotJobsReader r => Getter r (STM BotJob)
Getter BotJobsQueue (STM BotJob)
BotJobsQueue.botJobsReader
instance BotJobsQueue.HasBotJobsWriter BotEnv where
botJobsWriter :: Getter BotEnv (BotJob -> STM ())
botJobsWriter = (BotEnv -> BotJobsQueue)
-> (BotJobsQueue -> f BotJobsQueue) -> BotEnv -> f BotEnv
forall (p :: * -> * -> *) (f :: * -> *) s a.
(Profunctor p, Contravariant f) =>
(s -> a) -> Optic' p f s a
Lens.to BotEnv -> BotJobsQueue
botEnvJobsQueue ((BotJobsQueue -> f BotJobsQueue) -> BotEnv -> f BotEnv)
-> (((BotJob -> STM ()) -> f (BotJob -> STM ()))
-> BotJobsQueue -> f BotJobsQueue)
-> ((BotJob -> STM ()) -> f (BotJob -> STM ()))
-> BotEnv
-> f BotEnv
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((BotJob -> STM ()) -> f (BotJob -> STM ()))
-> BotJobsQueue -> f BotJobsQueue
forall r. HasBotJobsWriter r => Getter r (BotJob -> STM ())
Getter BotJobsQueue (BotJob -> STM ())
BotJobsQueue.botJobsWriter
data SendMessageResponse = SendMessageResponse
{ SendMessageResponse -> TransactionId
sendMessageResponseTransactionId ∷ T.TransactionId
, SendMessageResponse -> EventResponse
sendMessageResponseResponse ∷ EventResponse
}
deriving stock ((forall x. SendMessageResponse -> Rep SendMessageResponse x)
-> (forall x. Rep SendMessageResponse x -> SendMessageResponse)
-> Generic SendMessageResponse
forall x. Rep SendMessageResponse x -> SendMessageResponse
forall x. SendMessageResponse -> Rep SendMessageResponse x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. SendMessageResponse -> Rep SendMessageResponse x
from :: forall x. SendMessageResponse -> Rep SendMessageResponse x
$cto :: forall x. Rep SendMessageResponse x -> SendMessageResponse
to :: forall x. Rep SendMessageResponse x -> SendMessageResponse
Generic, SendMessageResponse -> SendMessageResponse -> Bool
(SendMessageResponse -> SendMessageResponse -> Bool)
-> (SendMessageResponse -> SendMessageResponse -> Bool)
-> Eq SendMessageResponse
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: SendMessageResponse -> SendMessageResponse -> Bool
== :: SendMessageResponse -> SendMessageResponse -> Bool
$c/= :: SendMessageResponse -> SendMessageResponse -> Bool
/= :: SendMessageResponse -> SendMessageResponse -> Bool
Eq, Int -> SendMessageResponse -> String -> String
[SendMessageResponse] -> String -> String
SendMessageResponse -> String
(Int -> SendMessageResponse -> String -> String)
-> (SendMessageResponse -> String)
-> ([SendMessageResponse] -> String -> String)
-> Show SendMessageResponse
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> SendMessageResponse -> String -> String
showsPrec :: Int -> SendMessageResponse -> String -> String
$cshow :: SendMessageResponse -> String
show :: SendMessageResponse -> String
$cshowList :: [SendMessageResponse] -> String -> String
showList :: [SendMessageResponse] -> String -> String
Show)
instance ToJSON SendMessageResponse where toJSON :: SendMessageResponse -> Value
toJSON = SendMessageResponse -> Value
forall a.
(Generic a, Typeable a, GToJSON' Value Zero (Rep a)) =>
a -> Value
myGenericToJSON
instance FromJSON SendMessageResponse where parseJSON :: Value -> Parser SendMessageResponse
parseJSON = Value -> Parser SendMessageResponse
forall a.
(Generic a, Typeable a, GFromJSON Zero (Rep a)) =>
Value -> Parser a
myGenericParseJSON
quoted ∷ (IsString s, Show s) ⇒ s → Text
quoted :: forall s. (IsString s, Show s) => s -> Text
quoted = String -> Text
pack (String -> Text) -> (s -> String) -> s -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. s -> String
forall a. Show a => a -> String
show