{-# LANGUAGE UnicodeSyntax #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}

{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# HLINT ignore "Redundant <&>" #-}

module MatrixBot.Bot.EventsListener
  ( eventsListener
  ) where

import Control.Monad.IO.Class (MonadIO (liftIO))
import Data.Aeson (eitherDecodeStrict, encodeFile)
import qualified Data.ByteString as BS
import Data.Data (Proxy(Proxy))
import Data.Function (fix)
import Data.Functor ((<&>))
import Data.Functor.Identity (Identity(runIdentity))
import Data.Text (pack)
import qualified MatrixBot.Bot.BotConfig as BotConfig
import MatrixBot.Bot.BotM (BotM)
import MatrixBot.Bot.EventsListener.Handlers.ReactToUsers (reactToUsers)
import MatrixBot.Bot.Retry (retryOnClientError)
import qualified MatrixBot.Log as L
import qualified MatrixBot.MatrixApi as Api
import qualified MatrixBot.MatrixApi.Client as Api
import qualified MatrixBot.MatrixApi.Types.MEventTypes as Api
import qualified MatrixBot.SharedTypes as T
import Servant.API (AuthProtect)
import Servant.Client.Core (AuthenticatedRequest)
import System.Directory (doesFileExist)
import MatrixBot.Bot.Jobs.Queue (HasBotJobsWriter)
import Data.Maybe (fromMaybe)
import MatrixBot.Bot.EventsListener.Handlers.ReplyToMedia (replyToMedia)


-- | Matrix rooms events listener handler
--
-- Read new chunks of events and process them in an infinite loop.
eventsListener
    r m
  . (BotM r m, HasBotJobsWriter r)
   Maybe FilePath
   T.EventsTimeout
   BotConfig.BotConfig
   Api.MatrixApiClient
   AuthenticatedRequest (AuthProtect "access-token")
   m ()
eventsListener :: forall r (m :: * -> *).
(BotM r m, HasBotJobsWriter r) =>
Maybe FilePath
-> EventsTimeout
-> BotConfig
-> MatrixApiClient
-> AuthenticatedRequest (AuthProtect "access-token")
-> m ()
eventsListener Maybe FilePath
eventTokenFile EventsTimeout
eventsTimeout BotConfig
botConfig MatrixApiClient
req AuthenticatedRequest (AuthProtect "access-token")
auth = do
  Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
L.logDebug Text
"Starting Matrix rooms events listener…"

  -- Preserve last handled event checkpoint feature.
  --
  -- Will read last event identity/token from a file and request next chunk of
  -- events from that point. So if bot was offline due to a server reboot or
  -- other reason it will still get the events added to the room during the
  -- bot’s absence.
  --
  -- After each events chunk handle the new token is written to the file.
  --
  -- You can always skip the accumulated events by just deleting that file.
  --
  -- TODO: Implement debouncer feature. If there were a lot of events
  --       accumulated when the bot was offline then there can be a lot of
  --       traffic generated of reading chunks of events, handling them, and
  --       then again and again. So that the server may start blocking your
  --       spamming traffic, and the bot would stuck in a retry loop, and
  --       eventually critically fail when retry limit is reached.
  --       Implement a request debounching feature so that there would be some
  --       configured interval between requests to the Matrix homeserver.
  Maybe EventToken
initialEventToken 
    case Maybe FilePath
eventTokenFile of
      Maybe FilePath
Nothing  do
        Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
L.logWarn Text
"No event token file provided, listening starting from next following events…"
        Maybe EventToken -> m (Maybe EventToken)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe EventToken
forall a. Maybe a
Nothing
      Just FilePath
file  do
        Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
L.logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
          [ Text
"Checking if ", FilePath -> Text
pack (FilePath -> Text)
-> (Maybe FilePath -> FilePath) -> Maybe FilePath -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe FilePath -> FilePath
forall a. Show a => a -> FilePath
show (Maybe FilePath -> Text) -> Maybe FilePath -> Text
forall a b. (a -> b) -> a -> b
$ Maybe FilePath
eventTokenFile
          , Text
" token file exists to read initial event token from…"
          ]

        Bool
exists  IO Bool -> m Bool
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (FilePath -> IO Bool
doesFileExist FilePath
file)

        if Bool -> Bool
not Bool
exists
        then
          (Maybe EventToken
forall a. Maybe a
Nothing Maybe EventToken -> m () -> m (Maybe EventToken)
forall a b. a -> m b -> m a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$) (m () -> m (Maybe EventToken))
-> (Text -> m ()) -> Text -> m (Maybe EventToken)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
L.logDebug (Text -> m (Maybe EventToken)) -> Text -> m (Maybe EventToken)
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
            [ Text
"Event token file ", FilePath -> Text
pack (FilePath -> Text)
-> (Maybe FilePath -> FilePath) -> Maybe FilePath -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe FilePath -> FilePath
forall a. Show a => a -> FilePath
show (Maybe FilePath -> Text) -> Maybe FilePath -> Text
forall a b. (a -> b) -> a -> b
$ Maybe FilePath
eventTokenFile
            , Text
" does not exist, listening starting from next following events…"
            ]
        else
          IO ByteString -> m ByteString
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (FilePath -> IO ByteString
BS.readFile FilePath
file) m ByteString
-> (ByteString -> Either FilePath EventToken)
-> m (Either FilePath EventToken)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> ByteString -> Either FilePath EventToken
forall a. FromJSON a => ByteString -> Either FilePath a
eitherDecodeStrict m (Either FilePath EventToken)
-> (Either FilePath EventToken -> m (Maybe EventToken))
-> m (Maybe EventToken)
forall a b. m a -> (a -> m b) -> m b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
            Right EventToken
x 
              (EventToken -> Maybe EventToken
forall a. a -> Maybe a
Just EventToken
x Maybe EventToken -> m () -> m (Maybe EventToken)
forall a b. a -> m b -> m a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$) (m () -> m (Maybe EventToken))
-> (Text -> m ()) -> Text -> m (Maybe EventToken)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
L.logDebug (Text -> m (Maybe EventToken)) -> Text -> m (Maybe EventToken)
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
                [ Text
"Read and parsed last event token from ", FilePath -> Text
pack (FilePath -> Text) -> (FilePath -> FilePath) -> FilePath -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath
forall a. Show a => a -> FilePath
show (FilePath -> Text) -> FilePath -> Text
forall a b. (a -> b) -> a -> b
$ FilePath
file
                , Text
" file, listening for next events starting from: ", FilePath -> Text
pack (FilePath -> Text)
-> (EventToken -> FilePath) -> EventToken -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> FilePath
forall a. Show a => a -> FilePath
show (Text -> FilePath)
-> (EventToken -> Text) -> EventToken -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EventToken -> Text
T.unEventToken (EventToken -> Text) -> EventToken -> Text
forall a b. (a -> b) -> a -> b
$ EventToken
x
                ]
            Left FilePath
e 
              FilePath -> m (Maybe EventToken)
forall a. FilePath -> m a
forall (m :: * -> *) a. MonadFail m => FilePath -> m a
fail (FilePath -> m (Maybe EventToken))
-> FilePath -> m (Maybe EventToken)
forall a b. (a -> b) -> a -> b
$ [FilePath] -> FilePath
forall a. Monoid a => [a] -> a
mconcat
                [ FilePath
"Failed to read event token from ", FilePath -> FilePath
forall a. Show a => a -> FilePath
show FilePath
file
                , FilePath
" event token file: ", FilePath
e
                ]

  Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
L.logDebug Text
"Listening for events from all rooms filtering them accordingly to the bot config…"

  -- Start an infinite loop of events chunks handling.
  ((Maybe EventToken -> m ()) -> Maybe EventToken -> m ()
forall a b. (a -> b) -> a -> b
$ Maybe EventToken
initialEventToken) ((Maybe EventToken -> m ()) -> m ())
-> (((Maybe EventToken -> m ()) -> Maybe EventToken -> m ())
    -> Maybe EventToken -> m ())
-> ((Maybe EventToken -> m ()) -> Maybe EventToken -> m ())
-> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Maybe EventToken -> m ()) -> Maybe EventToken -> m ())
-> Maybe EventToken -> m ()
forall a. (a -> a) -> a
fix (((Maybe EventToken -> m ()) -> Maybe EventToken -> m ()) -> m ())
-> ((Maybe EventToken -> m ()) -> Maybe EventToken -> m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ \Maybe EventToken -> m ()
again Maybe EventToken
eventToken  do
    Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
L.logDebug Text
"Waiting for next room events chunk…"

    EventsResponse
eventsReponse  m EventsResponse -> m EventsResponse
forall (m :: * -> *) r a.
(MonadMask m, MonadLogger m, MonadIO m, MonadReader r m,
 HasRetryParams r) =>
m a -> m a
retryOnClientError (m EventsResponse -> m EventsResponse)
-> m EventsResponse -> m EventsResponse
forall a b. (a -> b) -> a -> b
$ Maybe EventToken -> m EventsResponse
getNextEvents' Maybe EventToken
eventToken
    Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
L.logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ Text
"Received next events chunk: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> (FilePath -> Text
pack (FilePath -> Text)
-> (EventsResponse -> FilePath) -> EventsResponse -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EventsResponse -> FilePath
forall a. Show a => a -> FilePath
show) EventsResponse
eventsReponse

    let events :: [ClientEvent]
events = EventsResponse -> [ClientEvent]
Api.eventsResponseChunk EventsResponse
eventsReponse
    Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
L.logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ Text
"Handling " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> (FilePath -> Text
pack (FilePath -> Text)
-> ([ClientEvent] -> FilePath) -> [ClientEvent] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> FilePath
forall a. Show a => a -> FilePath
show (Int -> FilePath)
-> ([ClientEvent] -> Int) -> [ClientEvent] -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ClientEvent] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length) [ClientEvent]
events Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" events…"
    (ClientEvent -> m ()) -> [ClientEvent] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (BotConfig -> ClientEvent -> m ()
forall r (m :: * -> *).
(BotM r m, HasBotJobsWriter r) =>
BotConfig -> ClientEvent -> m ()
handleEvent BotConfig
botConfig) [ClientEvent]
events

    let nextEventToken :: EventToken
nextEventToken = EventsResponse -> EventToken
Api.eventsResponseEnd EventsResponse
eventsReponse

    -- Preserve last read event token by saving in to a file.
    case Maybe FilePath
eventTokenFile of
      Maybe FilePath
Nothing  () -> m ()
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
      Just FilePath
_ | Maybe EventToken
eventToken Maybe EventToken -> Maybe EventToken -> Bool
forall a. Eq a => a -> a -> Bool
== EventToken -> Maybe EventToken
forall a. a -> Maybe a
Just EventToken
nextEventToken 
        Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
L.logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
          [ Text
"Received event token ", FilePath -> Text
pack (FilePath -> Text)
-> (EventToken -> FilePath) -> EventToken -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> FilePath
forall a. Show a => a -> FilePath
show (Text -> FilePath)
-> (EventToken -> Text) -> EventToken -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EventToken -> Text
T.unEventToken (EventToken -> Text) -> EventToken -> Text
forall a b. (a -> b) -> a -> b
$ EventToken
nextEventToken
          , Text
"didn’t change since the last time, no need to save it again, skipping save…"
          ]
      Just FilePath
_ | [ClientEvent] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [ClientEvent]
events 
        Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
L.logDebug
          Text
"Received list of events is empty, skipping save of event token to event token file…"
      Just FilePath
file  do
        Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
L.logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
          [ Text
"Saving received event token ", FilePath -> Text
pack (FilePath -> Text)
-> (EventToken -> FilePath) -> EventToken -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> FilePath
forall a. Show a => a -> FilePath
show (Text -> FilePath)
-> (EventToken -> Text) -> EventToken -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EventToken -> Text
T.unEventToken (EventToken -> Text) -> EventToken -> Text
forall a b. (a -> b) -> a -> b
$ EventToken
nextEventToken
          , Text
" to ", FilePath -> Text
pack (FilePath -> Text)
-> (Maybe FilePath -> FilePath) -> Maybe FilePath -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe FilePath -> FilePath
forall a. Show a => a -> FilePath
show (Maybe FilePath -> Text) -> Maybe FilePath -> Text
forall a b. (a -> b) -> a -> b
$ Maybe FilePath
eventTokenFile, Text
" event token file…"
          ]
        IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ FilePath -> EventToken -> IO ()
forall a. ToJSON a => FilePath -> a -> IO ()
encodeFile FilePath
file EventToken
nextEventToken

    Maybe EventToken -> m ()
again (Maybe EventToken -> m ())
-> (EventToken -> Maybe EventToken) -> EventToken -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EventToken -> Maybe EventToken
forall a. a -> Maybe a
Just (EventToken -> m ()) -> EventToken -> m ()
forall a b. (a -> b) -> a -> b
$ EventToken
nextEventToken

  where
    getNextEvents'  Maybe T.EventToken  m Api.EventsResponse
    getNextEvents' :: Maybe EventToken -> m EventsResponse
getNextEvents' = EventsTimeout
-> MatrixApiClient
-> AuthenticatedRequest (AuthProtect "access-token")
-> Maybe EventToken
-> m EventsResponse
forall r (m :: * -> *).
BotM r m =>
EventsTimeout
-> MatrixApiClient
-> AuthenticatedRequest (AuthProtect "access-token")
-> Maybe EventToken
-> m EventsResponse
getNextEvents EventsTimeout
eventsTimeout MatrixApiClient
req AuthenticatedRequest (AuthProtect "access-token")
auth


-- | Get next chunk of Matrix room events
--
-- Blocking operation. Will wait for new events to appear.
-- But will return an empty list of events if timeout is hit.
getNextEvents
   BotM r m
   T.EventsTimeout
   Api.MatrixApiClient
   AuthenticatedRequest (AuthProtect "access-token")
   Maybe T.EventToken
   m Api.EventsResponse
getNextEvents :: forall r (m :: * -> *).
BotM r m =>
EventsTimeout
-> MatrixApiClient
-> AuthenticatedRequest (AuthProtect "access-token")
-> Maybe EventToken
-> m EventsResponse
getNextEvents EventsTimeout
eventsTimeout MatrixApiClient
req AuthenticatedRequest (AuthProtect "access-token")
auth Maybe EventToken
eventToken =
  MatrixApiClient
-> forall api (m :: * -> *) a.
   (MonadIO m, MonadFail m, Show a, MonadThrow m, MonadLogger m,
    HasClient (Free ClientF) api, HasClient ClientM api) =>
   Proxy api
   -> (forall (clientM :: * -> *).
       HasClient clientM api =>
       Client clientM api -> clientM a)
   -> m a
Api.runMatrixApiClient' MatrixApiClient
req (forall t. Proxy t
forall {k} (t :: k). Proxy t
Proxy @Api.EventsApi) ((forall {clientM :: * -> *}.
  HasClient clientM EventsApi =>
  Client clientM EventsApi -> clientM EventsResponse)
 -> m EventsResponse)
-> (forall {clientM :: * -> *}.
    HasClient clientM EventsApi =>
    Client clientM EventsApi -> clientM EventsResponse)
-> m EventsResponse
forall a b. (a -> b) -> a -> b
$ \Client clientM EventsApi
f  Client clientM EventsApi
AuthenticatedRequest (AuthProtect "access-token")
-> Maybe EventToken
-> Maybe RoomId
-> Maybe Milliseconds
-> clientM EventsResponse
f
    AuthenticatedRequest (AuthProtect "access-token")
auth
    Maybe EventToken
eventToken
    Maybe RoomId
forall a. Maybe a
Nothing -- Listening for events from all rooms
    (Milliseconds -> Maybe Milliseconds
forall a. a -> Maybe a
Just (Milliseconds -> Maybe Milliseconds)
-> (EventsTimeout -> Milliseconds)
-> EventsTimeout
-> Maybe Milliseconds
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Seconds -> Milliseconds
T.secondsToMilliseconds (Seconds -> Milliseconds)
-> (EventsTimeout -> Seconds) -> EventsTimeout -> Milliseconds
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EventsTimeout -> Seconds
T.unEventsTimeout (EventsTimeout -> Maybe Milliseconds)
-> EventsTimeout -> Maybe Milliseconds
forall a b. (a -> b) -> a -> b
$ EventsTimeout
eventsTimeout)


-- | Process one read event and react to it if event filters are matching.
handleEvent  (BotM r m, HasBotJobsWriter r)  BotConfig.BotConfig  Api.ClientEvent  m ()
handleEvent :: forall r (m :: * -> *).
(BotM r m, HasBotJobsWriter r) =>
BotConfig -> ClientEvent -> m ()
handleEvent BotConfig
botConfig ClientEvent
ev = do
  Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
L.logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ Text
"Handling event: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> (FilePath -> Text
pack (FilePath -> Text)
-> (ClientEvent -> FilePath) -> ClientEvent -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ClientEvent -> FilePath
forall a. Show a => a -> FilePath
show) ClientEvent
ev

  case ClientEvent
ev of
    Api.ClientEventMRoomMessage ClientEventGeneric Identity
g MRoomMessageClientEvent
m  do
      let
        roomId :: RoomId
roomId = Identity RoomId -> RoomId
forall a. Identity a -> a
runIdentity (Identity RoomId -> RoomId)
-> (ClientEventGeneric Identity -> Identity RoomId)
-> ClientEventGeneric Identity
-> RoomId
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ClientEventGeneric Identity -> Identity RoomId
forall (f :: * -> *). ClientEventGeneric f -> f RoomId
Api.clientEventGenericRoomId (ClientEventGeneric Identity -> RoomId)
-> ClientEventGeneric Identity -> RoomId
forall a b. (a -> b) -> a -> b
$ ClientEventGeneric Identity
g
        userId :: Mxid
userId = Identity Mxid -> Mxid
forall a. Identity a -> a
runIdentity (Identity Mxid -> Mxid)
-> (ClientEventGeneric Identity -> Identity Mxid)
-> ClientEventGeneric Identity
-> Mxid
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ClientEventGeneric Identity -> Identity Mxid
forall (f :: * -> *). ClientEventGeneric f -> f Mxid
Api.clientEventGenericSender (ClientEventGeneric Identity -> Mxid)
-> ClientEventGeneric Identity -> Mxid
forall a b. (a -> b) -> a -> b
$ ClientEventGeneric Identity
g
        eventId :: EventId
eventId = Identity EventId -> EventId
forall a. Identity a -> a
runIdentity (Identity EventId -> EventId)
-> (ClientEventGeneric Identity -> Identity EventId)
-> ClientEventGeneric Identity
-> EventId
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ClientEventGeneric Identity -> Identity EventId
forall (f :: * -> *). ClientEventGeneric f -> f EventId
Api.clientEventGenericEventId (ClientEventGeneric Identity -> EventId)
-> ClientEventGeneric Identity -> EventId
forall a b. (a -> b) -> a -> b
$ ClientEventGeneric Identity
g

      Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
L.logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
        [ Text
"Received "
        , MEventType -> Text
forall s. IsString s => MEventType -> s
Api.mEventTypeToString (MEventType -> Text)
-> (MRoomMessageClientEvent -> MEventType)
-> MRoomMessageClientEvent
-> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MEventTypeOneOf '[ 'MRoomMessageType] -> MEventType
forall (t :: MEventType) (ts :: [MEventType]).
MEventTypeOneOf (t : ts) -> MEventType
Api.mEventTypeOneOfToMEventType (MEventTypeOneOf '[ 'MRoomMessageType] -> MEventType)
-> (MRoomMessageClientEvent
    -> MEventTypeOneOf '[ 'MRoomMessageType])
-> MRoomMessageClientEvent
-> MEventType
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MRoomMessageClientEvent -> MEventTypeOneOf '[ 'MRoomMessageType]
Api.mRoomMessageClientEventType (MRoomMessageClientEvent -> Text)
-> MRoomMessageClientEvent -> Text
forall a b. (a -> b) -> a -> b
$ MRoomMessageClientEvent
m
        , Text
" event type from room ", RoomId -> Text
T.printRoomId RoomId
roomId
        , Text
" from user ", Mxid -> Text
T.printMxid Mxid
userId
        , Text
" (event ID: ", EventId -> Text
T.unEventId EventId
eventId, Text
")"
        ]

      [BotConfigReactToUsers] -> RoomId -> Mxid -> EventId -> m ()
forall r (m :: * -> *).
(BotM r m, HasBotJobsWriter r) =>
[BotConfigReactToUsers] -> RoomId -> Mxid -> EventId -> m ()
reactToUsers
        ([BotConfigReactToUsers]
-> Maybe [BotConfigReactToUsers] -> [BotConfigReactToUsers]
forall a. a -> Maybe a -> a
fromMaybe [] (Maybe [BotConfigReactToUsers] -> [BotConfigReactToUsers])
-> Maybe [BotConfigReactToUsers] -> [BotConfigReactToUsers]
forall a b. (a -> b) -> a -> b
$ BotConfig -> Maybe [BotConfigReactToUsers]
BotConfig.botConfigReactToUsers BotConfig
botConfig)
        RoomId
roomId
        Mxid
userId
        EventId
eventId

      [BotConfigReplyToMedia]
-> RoomId
-> Mxid
-> EventId
-> MRoomMessageClientEventContent
-> m ()
forall r (m :: * -> *).
(BotM r m, HasBotJobsWriter r) =>
[BotConfigReplyToMedia]
-> RoomId
-> Mxid
-> EventId
-> MRoomMessageClientEventContent
-> m ()
replyToMedia
        ([BotConfigReplyToMedia]
-> Maybe [BotConfigReplyToMedia] -> [BotConfigReplyToMedia]
forall a. a -> Maybe a -> a
fromMaybe [] (Maybe [BotConfigReplyToMedia] -> [BotConfigReplyToMedia])
-> Maybe [BotConfigReplyToMedia] -> [BotConfigReplyToMedia]
forall a b. (a -> b) -> a -> b
$ BotConfig -> Maybe [BotConfigReplyToMedia]
BotConfig.botConfigReplyToMedia BotConfig
botConfig)
        RoomId
roomId
        Mxid
userId
        EventId
eventId
        (MRoomMessageClientEvent -> MRoomMessageClientEventContent
Api.mRoomMessageClientEventContent MRoomMessageClientEvent
m)

    Api.ClientEventOther ClientEventGeneric Maybe
g Object
_ 
      Text -> m ()
forall (m :: * -> *). (MonadLogger m, HasCallStack) => Text -> m ()
L.logDebug (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ Text
"No need to handle " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> ClientEventGeneric Maybe -> Text
forall (f :: * -> *). ClientEventGeneric f -> Text
Api.clientEventGenericType ClientEventGeneric Maybe
g Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" event type (skipped)"