    {-# LANGUAGE TypeFamilies #-}
    {-# LANGUAGE TypeOperators #-}

    -- Copyright (c) 2005 Don Stewart - http://www.cse.unsw.edu.au/~dons
    -- GPL version 2 or later (see http://www.gnu.org/copyleft/gpl.html)

    -- | Simple wrapper over privmsg to get time information via the CTCP
    module Lambdabot.Plugin.IRC.Localtime (localtimePlugin) where

    import Lambdabot.Plugin
    import Lambdabot.Bot (ircPrivmsg')
    import qualified Data.Map as M

    type TimeMap = M.Map Nick  -- the person who's time we requested
                        [Nick] -- a list of targets waiting on this time

    localtimePlugin :: Module TimeMap
    localtimePlugin :: Module (Map Nick [Nick])
    localtimePlugin = Module (Map Nick [Nick])
    forall st. Module st
    newModule
        { moduleDefState = return M.empty

        , moduleCmds = return
            [ (command "time")
                { aliases = ["localtime"]
                , help = say "time <user>. Print a user's local time. User's client must support ctcp pings."
                , process = doLocalTime
                }
            , (command "localtime-reply")
                { help = say "time <user>. Print a user's local time. User's client must support ctcp pings."
                , process = doReply
                }
            ]
        } :: Module TimeMap

    -- record this person as a callback, for when we (asynchronously) get a result
    doLocalTime :: (MonadLBState m, LBState m ~ M.Map Nick [Nick]) =>
                   [Char] -> Cmd m ()
    doLocalTime :: forall (m :: * -> *).
    (MonadLBState m, LBState m ~ Map Nick [Nick]) =>
    String -> Cmd m ()
    doLocalTime [] = do
        n <- Cmd m Nick
    forall (m :: * -> *). Monad m => Cmd m Nick
    getSender
        doLocalTime (nName n)

    doLocalTime String
    rawWho = do
        whoAsked <- Cmd m Nick
    forall (m :: * -> *). Monad m => Cmd m Nick
    getTarget
        whoToPing <- readNick $ fst $ break (== ' ') rawWho
        me <- getLambdabotName
        if whoToPing /= me
            then do
                modifyMS $ \LBState (Cmd m)
    st -> ([Nick] -> [Nick] -> [Nick])
    -> Nick -> [Nick] -> Map Nick [Nick] -> Map Nick [Nick]
    forall k a. Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
    M.insertWith [Nick] -> [Nick] -> [Nick]
    forall a. [a] -> [a] -> [a]
    (++) Nick
    whoToPing [Nick
    whoAsked] Map Nick [Nick]
    LBState (Cmd m)
    st
                -- this is a CTCP time call, which returns a NOTICE
                lb $ ircPrivmsg' whoToPing ("\^ATIME\^A")     -- has to be raw
            else say "I live on the internet, do you expect me to have a local time?"

    -- the Base module caught the NOTICE TIME, mapped it to a PRIVMGS, and here it is :)
    doReply :: (MonadLBState m, LBState m ~ M.Map Nick [Nick]) =>
               [Char] -> Cmd m ()
    doReply :: forall (m :: * -> *).
    (MonadLBState m, LBState m ~ Map Nick [Nick]) =>
    String -> Cmd m ()
    doReply String
    text = do
        let (String
    whoGotPinged', String
    time') = (Char -> Bool) -> String -> (String, String)
    forall a. (a -> Bool) -> [a] -> ([a], [a])
    break (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    == Char
    ':') String
    text
            time :: String
    time = Int -> String -> String
    forall a. Int -> [a] -> [a]
    drop Int
    1 String
    time'
        whoGotPinged <- String -> Cmd m Nick
    forall (m :: * -> *). Monad m => String -> Cmd m Nick
    readNick String
    whoGotPinged'

        targets <- withMS $ \LBState (Cmd m)
    st LBState (Cmd m) -> Cmd m ()
    set -> do
            case Nick -> Map Nick [Nick] -> Maybe [Nick]
    forall k a. Ord k => k -> Map k a -> Maybe a
    M.lookup Nick
    whoGotPinged Map Nick [Nick]
    LBState (Cmd m)
    st of
                Maybe [Nick]
    Nothing -> [Nick] -> Cmd m [Nick]
    forall a. a -> Cmd m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return []
                Just [Nick]
    xs -> do LBState (Cmd m) -> Cmd m ()
    set (Nick -> [Nick] -> Map Nick [Nick] -> Map Nick [Nick]
    forall k a. Ord k => k -> a -> Map k a -> Map k a
    M.insert Nick
    whoGotPinged [] Map Nick [Nick]
    LBState (Cmd m)
    st) -- clear the callback state
                              [Nick] -> Cmd m [Nick]
    forall a. a -> Cmd m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return [Nick]
    xs
        whoGotPinged'' <- showNick whoGotPinged
        let txt = String
    "Local time for " String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    whoGotPinged'' String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    " is " String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    time
        lb $ flip mapM_ targets $ flip ircPrivmsg' txt