hask-irc/hask-irc-handlers/Network/IRC/Handlers/Tell.hs

137 lines
5.4 KiB
Haskell
Raw Normal View History

2014-06-01 06:48:24 +05:30
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
2014-06-07 00:50:27 +05:30
module Network.IRC.Handlers.Tell (tellMsgHandlerMaker) where
2014-06-01 06:48:24 +05:30
import qualified Data.IxSet as IS
import ClassyPrelude hiding (swap)
import Control.Monad.State.Strict (get, put)
import Data.Acid (AcidState, Query, Update, makeAcidic, query, update,
openLocalState, createArchive)
import Data.Acid.Local (createCheckpointAndClose)
import Data.IxSet ((@=))
import Data.Text (split, strip)
import Network.IRC
2014-06-01 06:48:24 +05:30
import Network.IRC.Handlers.NickTracker.Types
2014-06-07 00:50:27 +05:30
import Network.IRC.Handlers.Tell.Internal.Types
2014-06-01 06:48:24 +05:30
import Network.IRC.Util
2014-06-01 23:14:19 +05:30
-- database
2014-06-01 06:48:24 +05:30
getUndeliveredTellsQ :: CanonicalNick -> Query Tells [Tell]
getUndeliveredTellsQ nick = do
Tells { .. } <- ask
return . sortBy (comparing tellCreatedOn) . IS.toList $ tells @= nick @= NewTell
saveTellQ :: Tell -> Update Tells ()
saveTellQ tell@Tell { .. } = do
Tells { .. } <- get
put $ if tellId == -1
then Tells (nextTellId + 1) (IS.updateIx nextTellId tell{ tellId = nextTellId } tells)
else Tells nextTellId (IS.updateIx tellId tell tells)
2014-06-01 06:48:24 +05:30
$(makeAcidic ''Tells ['getUndeliveredTellsQ, 'saveTellQ])
getUndeliveredTells :: AcidState Tells -> CanonicalNick -> IO [Tell]
getUndeliveredTells acid = query acid . GetUndeliveredTellsQ
saveTell :: AcidState Tells -> Tell -> IO ()
saveTell acid = update acid . SaveTellQ
2014-06-01 23:14:19 +05:30
-- handler
2014-06-01 06:48:24 +05:30
newtype TellState = TellState { acid :: AcidState Tells }
tellMsg :: MonadMsgHandler m => MessageChannel Message -> IORef TellState -> Message -> m [Message]
tellMsg messageChannel state Message { .. }
| Just (ChannelMsg (User { .. }) msg) <- fromMessage message
, command msg == "!tell"
2014-06-01 06:48:24 +05:30
, args <- drop 1 . words $ msg
, length args >= 2 = io $ do
TellState { .. } <- readIORef state
reps <- if "<" `isPrefixOf` headEx args
2014-06-01 23:14:19 +05:30
then do -- multi tell
let (nicks, tell) =
2014-06-01 06:48:24 +05:30
(parseNicks *** (strip . drop 1)) . break (== '>') . drop 1 . unwords $ args
if null tell
2014-06-01 06:48:24 +05:30
then return []
else do
res <- forM nicks $ \nick -> handleTell acid userNick nick tell
2014-06-01 06:48:24 +05:30
let (fails, passes) = partitionEithers res
let reps = (if null fails then [] else ["Unknown nicks: " ++ intercalate ", " fails]) ++
(if null passes then [] else
["Message noted and will be passed on to " ++ intercalate ", " passes])
return reps
2014-06-01 23:14:19 +05:30
else do -- single tell
2014-06-01 06:48:24 +05:30
let nick = Nick . headEx $ args
let tell = strip . unwords . drop 1 $ args
if null tell
2014-06-01 06:48:24 +05:30
then return []
else do
res <- handleTell acid userNick nick tell
2014-06-01 06:48:24 +05:30
let rep = case res of
Left _ -> "Unknown nick: " ++ nickToText nick
Right _ -> "Message noted and will be passed on to " ++ nickToText nick
return [rep]
tells <- getTellsToDeliver userNick
mapM (textToReply userNick) (reps ++ tells)
| Just (ChannelMsg (User { .. }) _) <- fromMessage message = io $ do
tells <- getTellsToDeliver userNick
mapM (textToReply userNick) tells
| Just (TellRequest user msg) <- fromMessage message = do
tellMsg messageChannel state . Message msgTime "" . toMessage $ ChannelMsg user msg
return []
| otherwise = return []
2014-06-01 06:48:24 +05:30
where
command msg = clean . fromMaybe "" . headMay . words $ msg
2014-06-01 06:48:24 +05:30
parseNicks = ordNub . map Nick . filter (not . null) . split (\x -> x == ' ' || x == ',')
textToReply nick t = newMessage . ChannelMsgReply $ nickToText nick ++ ": " ++ t
2014-06-01 06:48:24 +05:30
tellToMsg Tell { .. } =
relativeTime tellCreatedOn msgTime ++ " " ++ nickToText tellFromNick ++ " said: " ++ tellContent
newTell nick canonicalNick = Tell (-1) nick canonicalNick Nothing NewTell msgTime Nothing
2014-06-01 06:48:24 +05:30
getTellsToDeliver nick = io $ do
2014-06-01 06:48:24 +05:30
TellState { .. } <- readIORef state
mcn <- getCanonicalNick messageChannel nick
2014-06-01 06:48:24 +05:30
case mcn of
Nothing -> return []
Just canonicalNick -> do
tells <- getUndeliveredTells acid canonicalNick
forM tells $ \tell -> do
saveTell acid tell{ tellStatus = DeliveredTell, tellDeliveredOn = Just msgTime }
return . tellToMsg $ tell
handleTell acid userNick nick tell = do
mcn <- getCanonicalNick messageChannel nick
2014-06-01 06:48:24 +05:30
case mcn of
Nothing -> return . Left . nickToText $ nick
Just canonicalNick ->
saveTell acid (newTell userNick canonicalNick tell) >> (return . Right . nickToText $ nick)
2014-06-01 23:14:19 +05:30
2014-06-01 06:48:24 +05:30
stopTell :: MonadMsgHandler m => IORef TellState -> m ()
stopTell state = io $ do
TellState { .. } <- readIORef state
createArchive acid
createCheckpointAndClose acid
2014-06-07 00:50:27 +05:30
tellMsgHandlerMaker :: MsgHandlerMaker
tellMsgHandlerMaker = MsgHandlerMaker "tell" go
2014-06-01 06:48:24 +05:30
where
go BotConfig { .. } messageChannel = do
acid <- openLocalState emptyTells
state <- newIORef (TellState acid)
return $ newMsgHandler { onMessage = tellMsg messageChannel state
, onStop = stopTell state
, handlerHelp = return helpMsgs }
helpMsgs = singletonMap "!tell" $
"Publically pass a message to a user or a bunch of users. " ++
"!tell <nick> <message> or !tell <<nick1> <nick2> ...> <message>."