replicant-0.1.0.0: src/Replicant/Adapters/CLI.hs
module Replicant.Adapters.CLI
( adapter
, wait
) where
import Replicant.Base
import Replicant.Bot (botDirectives)
import Replicant.Plugins.Base (whitespace)
import qualified Replicant.Logging as Log
import Control.Concurrent.MVar
import Data.Attoparsec.Text
import Data.Maybe (isJust, fromJust)
import qualified Data.List as L
import qualified Data.Text as T
import qualified Data.Text.IO as T
import System.Console.ANSI
import System.IO
import System.IO.Unsafe (unsafePerformIO)
import Replicant.Plugin
adapter :: Replicant e m => Adapter m
adapter = Adapter
{ bootBot = _bootBot
, sendToUserId = _sendUserId
, sendToRoom = _sendRoom
, parseCommand = _parseCommand
, getRoomByName = _getRoomByName
, getRoomMembers = _getRoomMembers
}
done :: MVar ()
{-# NOINLINE done #-}
done = unsafePerformIO newEmptyMVar
wait :: MonadIO m => m ()
{-# NOINLINE wait #-}
wait = liftIO $ takeMVar done
_bootBot :: Replicant e m => BotSpec m -> m ()
_bootBot spec@BotSpec{..} = do
let Bot{..} = botRecord
let
loop = do
_ask botName "here"
input <- liftIO $ do
hFlush stdout
T.getLine
if input == "q"
then liftIO $ putMVar done ()
else dispatch spec input >> loop
loop
dispatch :: Replicant e m => BotSpec m -> Text -> m ()
dispatch spec input = case parseOnly messageParser input of
Left err -> liftIO . putStrLn $ "Could not parse input: " ++ err
Right msg -> botDirectives spec msg
_log :: MonadIO m => [Text] -> m ()
_log = liftIO . T.putStr . T.concat
outPrompt :: Text
outPrompt = Log.colorize' Dull botC " > "
prefixLines :: Text -> Text -> Text
prefixLines pre corpus = T.unlines . map (\l -> pre <> l) $ T.lines corpus
_send :: MonadIO m => Text -> Text -> Text -> m ()
_send bot target text = _log
[ Log.bracket botC bot
, target
, prefixLines outPrompt text
]
_ask :: MonadIO m => Text -> Text -> m ()
_ask botName target = _log
[ Log.bracket botC botName
, Log.bracket roomC target
, " < "
]
_sendUserId :: MonadIO m => Bot -> UserId -> Text -> m ()
_sendUserId Bot{..} _id = _send botName $ Log.bracket userC name
where
name = userName . fromJust $ L.find (\u -> userId u == _id) users
_sendRoom :: MonadIO m => Bot -> Room -> Text -> m ()
_sendRoom Bot{..} Room{..} = _send botName $ Log.bracket roomC roomName
_parseCommand :: Bot -> Message -> Maybe Text
_parseCommand bot Message{..} = case parseOnly (commandParser bot) messageText of
Left _ -> Nothing
Right mt -> mt
_getRoomByName :: Monad m => Bot -> Text -> m (Maybe Room)
_getRoomByName _ = return . roomNamed
_getRoomMembers :: Monad m => Bot -> Room -> m [User]
_getRoomMembers _ room = do
let found = L.find (\(r,_) -> r == room) rooms
return $ case found of
Just (_, users) -> users
Nothing -> []
word :: Parser Text
word = T.pack <$> many' letter
messageParser :: Parser Message
messageParser = do
mRoomName <- optional $ "room:" *> word
let mRoom = case mRoomName of
Just name -> roomNamed name
Nothing -> Just here
room <- maybe mzero return mRoom
mDirect <- optional "dm:"
whitespace
rest <- takeText
return Message
{ messageRoom = room
, messageUser = me
, messageText = rest
, messageDirect = isJust mDirect
}
commandParser :: Bot -> Parser (Maybe Text)
commandParser Bot{..} = do
name <- optional $ string ("@" <> botName)
whitespace
rest <- takeText
return $ if isJust name
then Just rest
else Nothing
me, you :: User
me = User "1" "me"
you = User "2" "you"
users :: [User]
users = [me, you]
here, there :: Room
here = Room "A" "here"
there = Room "B" "there"
rooms :: [(Room, [User])]
rooms =
[ (here, [me, you])
, (there, [you])
]
roomNamed :: Text -> Maybe Room
roomNamed name = fst <$> L.find (\(r,_) -> roomName r == name) rooms
botC, roomC, userC :: Color
botC = Green
roomC = Yellow
userC = Cyan