replicant-0.1.0.0: src/Replicant/Bot.hs
{-# LANGUAGE FlexibleContexts #-}
module Replicant.Bot
( botDirectives
, buildBot
, redis
, startBot
, stopBot
) where
import Replicant.Base
import Replicant.Bot.Supervisor (Supervisor, WorkerStatus, halt, monitor)
import Replicant.Plugin
import qualified Replicant.Logging as Log
import Control.Exception.Lifted (try)
import Control.Monad.Trans.Control (control)
import qualified Data.List as L
import qualified Database.Redis as R (Redis, Reply)
import Database.Redis.Namespace (RedisNS, runRedisNS)
redis :: Replicant e m => RedisNS R.Redis (Either R.Reply) a -> m a
redis q = do
conn <- redisPool
ns <- redisNamespace
res <- liftIO $ runRedisNS conn ns q
either redisError return res
botDirectives :: Replicant e m => BotSpec m -> Message -> m ()
botDirectives b msg = do
let applicable = L.filter (\p -> handlerApplies p b msg) (botHandlers b)
-- TODO:
-- * enforce only one match?
-- * respond to _direct_ messages if nothing matches
unless (null applicable) $ do
let names = L.map handlerName applicable
Log.handlerMatch (botRecord b) msg names
forM_ applicable $ \p -> tryRun p b msg
tryRun :: (MonadBaseControl IO m, MonadIO m) => Handler m -> BotSpec m -> Message -> m ()
tryRun h b m = do
result <- try $ runHandler h b m
case result of
-- TODO: respond to user if handler crashes
Left err -> Log.handlerCrash (botRecord b) err
Right _ -> return ()
buildBot :: Adapter m -> [Plugin m] -> Bot -> BotSpec m
buildBot botAdapter botPlugins botRecord = BotSpec{..}
startBot :: Replicant e m => Supervisor m BotId -> (WorkerStatus -> m c) -> BotSpec m -> m ()
startBot supervisor cb spec@BotSpec{..} = monitor supervisor cb (botId botRecord) (bootBot botAdapter spec)
stopBot :: Replicant e m => Supervisor m BotId -> BotId -> m ()
stopBot = halt