module Lambdabot.Plugin.Social.Tell (tellPlugin) where
import Lambdabot.Compat.AltTime
import Lambdabot.Compat.FreenodeNick
import Lambdabot.Plugin
import Lambdabot.Util
import Control.Monad
import qualified Data.Map as M
import Data.Maybe (fromMaybe)
import Text.Printf (printf)
data NoteType = Tell | Ask deriving (Show, Eq, Read)
data Note = Note { noteSender :: FreenodeNick,
noteContents :: String,
noteTime :: ClockTime,
noteType :: NoteType }
deriving (Eq, Show, Read)
type NoticeEntry = (Maybe ClockTime, [Note], Maybe String)
type NoticeBoard = M.Map FreenodeNick NoticeEntry
type Tell = ModuleT NoticeBoard LB
tellPlugin :: Module NoticeBoard
tellPlugin = newModule
{ moduleCmds = return
[ (command "tell")
{ help = say "tell <nick> <message>. When <nick> shows activity, tell them <message>."
, process = doTell Tell . words
}
, (command "ask")
{ help = say "ask <nick> <message>. When <nick> shows activity, ask them <message>."
, process = doTell Ask . words
}
, (command "messages")
{ help = say "messages. Check your messages, responding in private."
, process = const (doMessages False)
}
, (command "messages-loud")
{ help = say "messages. Check your messages, responding in public."
, process = const (doMessages True)
}
, (command "messages?")
{ help = say "messages?. Tells you whether you have any messages"
, process = const $ do
sender <- getSender
ms <- getMessages sender
case ms of
Just _ -> doRemind sender say
Nothing -> say "Sorry, no messages today."
}
, (command "clear-messages")
{ help = say "clear-messages. Clears your messages."
, process = const $ do
sender <- getSender
clearMessages sender
say "Messages cleared."
}
, (command "auto-reply")
{ help = say "auto-reply. Lets lambdabot auto-reply if someone sends you a message"
, process = doAutoReply
}
, (command "auto-reply?")
{ help = say "auto-reply?. Tells you your auto-reply status"
, process = const $ do
sender <- getSender
a <- getAutoReply sender
case a of
Just s -> say $ "Your auto-reply is \"" ++ s ++ "\"."
Nothing -> say "You do not have an auto-reply message set."
}
, (command "clear-auto-reply")
{ help = say "clear-auto-reply. Clears your auto-reply message."
, process = const $ do
sender <- getSender
clearAutoReply sender
say "Auto-reply message cleared."
}
, (command "print-notices")
{ privileged = True
, help = say "print-notices. Print the current map of notes."
, process = const ((say . show) =<< readMS)
}
, (command "purge-notices")
{ privileged = True
, help = say $
"purge-notices [<nick> [<nick> [<nick> ...]]]]. "
++ "Clear all notes for specified nicks, or all notices if you don't "
++ "specify a nick."
, process = \args -> do
users <- mapM readNick (words args)
if null users
then writeMS M.empty
else mapM_ clearMessages users
say "Messages purged."
}
]
, moduleDefState = return M.empty
, moduleSerialize = Just mapSerial
, contextual = const $ do
sender <- getSender
remp <- needToRemind sender
if remp
then doRemind sender (lb . ircPrivmsg sender)
else return ()
}
showNote :: ClockTime -> Note -> Cmd Tell String
showNote time note = do
sender <- showNick (getFreenodeNick (noteSender note))
let diff = time `diffClockTimes` noteTime note
ago = case timeDiffPretty diff of
[] -> "less than a minute"
pr -> pr
action = case noteType note of Tell -> "said"; Ask -> "asked"
return $ printf "%s %s %s ago: %s" sender action ago (noteContents note)
needToRemind :: Nick -> Cmd Tell Bool
needToRemind n = do
st <- readMS
now <- io getClockTime
return $ case M.lookup (FreenodeNick n) st of
Just (Just lastTime, _, _) ->
let diff = now `diffClockTimes` lastTime
in diff > TimeDiff 86400
Just (Nothing, _, _) -> True
Nothing -> True
writeDown :: Nick -> Nick -> String -> NoteType -> Cmd Tell ()
writeDown to from what ntype = do
time <- io getClockTime
let note = Note { noteSender = FreenodeNick from,
noteContents = what,
noteTime = time,
noteType = ntype }
modEntry to $ \(_, ns, a) -> (Nothing, ns ++ [note], a)
getMessages :: Nick -> Cmd Tell (Maybe [Note])
getMessages sender = do
st <- readMS
return $ case M.lookup (FreenodeNick sender) st of
Nothing -> Nothing
Just (_, [], _) -> Nothing
Just (_, ns, _) -> Just ns
setMessages :: Nick -> [Note] -> Cmd Tell ()
setMessages sender msgs = modEntry sender $ \(t, _, a) -> (t, msgs, a)
clearMessages :: Nick -> Cmd Tell ()
clearMessages sender = modEntry sender $ \(_, _, a) -> (Nothing, [], a)
setAutoReply :: Nick -> String -> Cmd Tell ()
setAutoReply sender msg = modEntry sender $ \(t, ns, _) -> (t, ns, Just msg)
getAutoReply :: Nick -> Cmd Tell (Maybe String)
getAutoReply sender = fmap (join . fmap (\(_,_,a) -> a) . M.lookup (FreenodeNick sender)) readMS
clearAutoReply :: Nick -> Cmd Tell ()
clearAutoReply sender = modEntry sender $ \(t, ns, _) -> (t, ns, Nothing)
modEntry :: Nick -> (NoticeEntry -> NoticeEntry) -> Cmd Tell ()
modEntry sender f = modifyMS $ M.alter (cleanup . f . fromMaybe empty) (FreenodeNick sender)
where empty = (Nothing, [], Nothing)
cleanup (_, [], Nothing) = Nothing
cleanup e = Just e
doMessages :: Bool -> Cmd Tell ()
doMessages loud = do
sender <- getSender
msgs <- getMessages sender
let tellNote = if loud
then say
else lb . ircPrivmsg sender
let loop [] = clearMessages sender
loop (msg : msgs) = do
time <- io getClockTime
showNote time msg >>= tellNote
setMessages sender msgs
loop msgs
case msgs of
Nothing -> say "You don't have any messages"
Just msgs -> loop msgs
verb :: NoteType -> String
verb Ask = "ask"
verb Tell= "tell"
doTell :: NoteType -> [String] -> Cmd Tell ()
doTell ntype [] = say ("Who should I " ++ verb ntype ++ "?")
doTell ntype (who':args) = do
let who = dropFromEnd (== ':') who'
recipient <- readNick who
sender <- getSender
me <- getLambdabotName
let rest = unwords args
(record, res)
| sender == recipient = (False, "You can " ++ verb ntype ++ " yourself!")
| recipient == me = (False, "Nice try ;)")
| null args = (False, "What should I " ++ verb ntype ++ " " ++ who ++ "?")
| otherwise = (True, "Consider it noted.")
when record $ do
autoReply <- getAutoReply recipient
case autoReply of
Nothing -> return ()
Just s -> say $ who ++ " lets you know: " ++ s
writeDown recipient sender rest ntype
say res
doAutoReply :: String -> Cmd Tell ()
doAutoReply "" = say "No auto-reply message given. Did you mean @clear-auto-reply?"
doAutoReply msg = do
sender <- getSender
setAutoReply sender msg
say "Auto-Reply messages noted. You can check the status with auto-reply? and clear it with clear-auto-reply."
doRemind :: Nick -> (String -> Cmd Tell ()) -> Cmd Tell ()
doRemind sender remind = do
ms <- getMessages sender
now <- io getClockTime
modEntry sender $ \(_,ns,a) -> (Just now, ns, a)
case ms of
Just msgs -> do
me <- showNick =<< getLambdabotName
let n = length msgs
(messages, pronoun)
| n > 1 = ("messages", "them")
| otherwise = ("message", "it")
remind $ printf "You have %d new %s. '/msg %s @messages' to read %s."
n messages me pronoun
Nothing -> return ()