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

    -- | Keep track of IRC users.
    module Lambdabot.Plugin.Social.Seen (seenPlugin) where

    import Lambdabot.Bot
    import Lambdabot.Compat.AltTime
    import Lambdabot.Compat.PackedNick
    import Lambdabot.IRC
    import Lambdabot.Logging
    import qualified Lambdabot.Message as G
    import Lambdabot.Monad
    import Lambdabot.Nick
    import Lambdabot.Plugin
    import Lambdabot.Util

    import Lambdabot.Plugin.Social.Seen.StopWatch
    import Lambdabot.Plugin.Social.Seen.UserStatus

    import Control.Exception
    import Control.Monad
    import Control.Monad.Trans
    import Data.Binary
    import qualified Data.ByteString.Char8 as P
    import qualified Data.ByteString.Lazy as L
    import Data.Char
    import Data.List
    import qualified Data.Map.Strict as M
    import Text.Printf

    type SeenState = (MaxMap, SeenMap)
    type SeenMap   = M.Map PackedNick UserStatus
    type MaxMap    = M.Map Channel Int

    type Seen = ModuleT SeenState LB

    ------------------------------------------------------------------------

    seenPlugin :: Module (M.Map Channel Int, M.Map PackedNick UserStatus)
    seenPlugin :: Module (Map PackedNick Int, Map PackedNick UserStatus)
    seenPlugin = Module (Map PackedNick Int, Map PackedNick UserStatus)
    forall st. Module st
    newModule
        { moduleDefState :: LB (Map PackedNick Int, Map PackedNick UserStatus)
    moduleDefState = (Map PackedNick Int, Map PackedNick UserStatus)
    -> LB (Map PackedNick Int, Map PackedNick UserStatus)
    forall a. a -> LB a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Map PackedNick Int
    forall k a. Map k a
    M.empty,Map PackedNick UserStatus
    forall k a. Map k a
    M.empty)

        , moduleCmds :: ModuleT
      (Map PackedNick Int, Map PackedNick UserStatus) LB [Command Seen]
    moduleCmds = [Command Seen]
    -> ModuleT
         (Map PackedNick Int, Map PackedNick UserStatus) LB [Command Seen]
    forall a.
    a -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB a
    forall (m :: * -> *) a. Monad m => a -> m a
    return
            [ (String -> Command Identity
    command String
    "users")
                { help :: Cmd Seen ()
    help = String -> Cmd Seen ()
    forall (m :: * -> *). Monad m => String -> Cmd m ()
    say String
    "users [chan]. Report the maximum number of users seen in a channel, and active users in the last 30 minutes"
                , process :: String -> Cmd Seen ()
    process = String -> Cmd Seen ()
    doUsers
                }
            , (String -> Command Identity
    command String
    "seen")
                { help :: Cmd Seen ()
    help = String -> Cmd Seen ()
    forall (m :: * -> *). Monad m => String -> Cmd m ()
    say String
    "seen <user>. Report if a user has been seen by the bot"
                , process :: String -> Cmd Seen ()
    process = String -> Cmd Seen ()
    doSeen
                }
            ]

        , moduleInit :: ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    moduleInit = do
            [ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()]
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    forall (t :: * -> *) (m :: * -> *) a.
    (Foldable t, Monad m) =>
    t (m a) -> m ()
    sequence_
                [ String
    -> Callback (Map PackedNick Int, Map PackedNick UserStatus)
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    forall st. String -> Callback st -> ModuleT st LB ()
    registerCallback String
    signal (String
    -> (IrcMessage
        -> ClockTime
        -> PackedNick
        -> Map PackedNick UserStatus
        -> Either String (Map PackedNick UserStatus))
    -> Callback (Map PackedNick Int, Map PackedNick UserStatus)
    forall a.
    Message a =>
    String
    -> (a
        -> ClockTime
        -> PackedNick
        -> Map PackedNick UserStatus
        -> Either String (Map PackedNick UserStatus))
    -> a
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    withSeenFM String
    signal IrcMessage
    -> ClockTime
    -> PackedNick
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    cb)
                | (String
    signal, IrcMessage
    -> ClockTime
    -> PackedNick
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    cb) <- [String]
    -> [IrcMessage
        -> ClockTime
        -> PackedNick
        -> Map PackedNick UserStatus
        -> Either String (Map PackedNick UserStatus)]
    -> [(String,
         IrcMessage
         -> ClockTime
         -> PackedNick
         -> Map PackedNick UserStatus
         -> Either String (Map PackedNick UserStatus))]
    forall a b. [a] -> [b] -> [(a, b)]
    zip
                    [String
    "JOIN", String
    "PART", String
    "QUIT", String
    "NICK", String
    "353",      String
    "PRIVMSG"]
                    [IrcMessage
    -> ClockTime
    -> PackedNick
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    joinCB, IrcMessage
    -> ClockTime
    -> PackedNick
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    partCB, IrcMessage
    -> ClockTime
    -> PackedNick
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    quitCB, IrcMessage
    -> ClockTime
    -> PackedNick
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    nickCB, IrcMessage
    -> ClockTime
    -> PackedNick
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    joinChanCB, IrcMessage
    -> ClockTime
    -> PackedNick
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    msgCB]
                ]

            Maybe String
    c <- LB (Maybe String)
    -> ModuleT
         (Map PackedNick Int, Map PackedNick UserStatus) LB (Maybe String)
    forall a.
    LB a
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB a
    forall (m :: * -> *) a. MonadLB m => LB a -> m a
    lb (LB (Maybe String)
     -> ModuleT
          (Map PackedNick Int, Map PackedNick UserStatus) LB (Maybe String))
    -> LB (Maybe String)
    -> ModuleT
         (Map PackedNick Int, Map PackedNick UserStatus) LB (Maybe String)
    forall a b. (a -> b) -> a -> b
    $ String -> LB (Maybe String)
    findLBFileForReading String
    "seen"
            PackedNick
    s <- ModuleT
      (Map PackedNick Int, Map PackedNick UserStatus) LB PackedNick
    -> (String
        -> ModuleT
             (Map PackedNick Int, Map PackedNick UserStatus) LB PackedNick)
    -> Maybe String
    -> ModuleT
         (Map PackedNick Int, Map PackedNick UserStatus) LB PackedNick
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe (PackedNick
    -> ModuleT
         (Map PackedNick Int, Map PackedNick UserStatus) LB PackedNick
    forall a.
    a -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (String -> PackedNick
    P.pack String
    "")) (IO PackedNick
    -> ModuleT
         (Map PackedNick Int, Map PackedNick UserStatus) LB PackedNick
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io (IO PackedNick
     -> ModuleT
          (Map PackedNick Int, Map PackedNick UserStatus) LB PackedNick)
    -> (String -> IO PackedNick)
    -> String
    -> ModuleT
         (Map PackedNick Int, Map PackedNick UserStatus) LB PackedNick
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> IO PackedNick
    P.readFile) Maybe String
    c
            let ls :: ByteString
    ls = PackedNick -> ByteString
    L.fromStrict PackedNick
    s
            Either
      SomeException (Map PackedNick Int, Map PackedNick UserStatus)
    mbDecoded <- IO
      (Either
         SomeException (Map PackedNick Int, Map PackedNick UserStatus))
    -> ModuleT
         (Map PackedNick Int, Map PackedNick UserStatus)
         LB
         (Either
            SomeException (Map PackedNick Int, Map PackedNick UserStatus))
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io (IO
       (Either
          SomeException (Map PackedNick Int, Map PackedNick UserStatus))
     -> ModuleT
          (Map PackedNick Int, Map PackedNick UserStatus)
          LB
          (Either
             SomeException (Map PackedNick Int, Map PackedNick UserStatus)))
    -> ((Map PackedNick Int, Map PackedNick UserStatus)
        -> IO
             (Either
                SomeException (Map PackedNick Int, Map PackedNick UserStatus)))
    -> (Map PackedNick Int, Map PackedNick UserStatus)
    -> ModuleT
         (Map PackedNick Int, Map PackedNick UserStatus)
         LB
         (Either
            SomeException (Map PackedNick Int, Map PackedNick UserStatus))
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . IO (Map PackedNick Int, Map PackedNick UserStatus)
    -> IO
         (Either
            SomeException (Map PackedNick Int, Map PackedNick UserStatus))
    forall e a. Exception e => IO a -> IO (Either e a)
    try (IO (Map PackedNick Int, Map PackedNick UserStatus)
     -> IO
          (Either
             SomeException (Map PackedNick Int, Map PackedNick UserStatus)))
    -> ((Map PackedNick Int, Map PackedNick UserStatus)
        -> IO (Map PackedNick Int, Map PackedNick UserStatus))
    -> (Map PackedNick Int, Map PackedNick UserStatus)
    -> IO
         (Either
            SomeException (Map PackedNick Int, Map PackedNick UserStatus))
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Map PackedNick Int, Map PackedNick UserStatus)
    -> IO (Map PackedNick Int, Map PackedNick UserStatus)
    forall a. a -> IO a
    evaluate ((Map PackedNick Int, Map PackedNick UserStatus)
     -> ModuleT
          (Map PackedNick Int, Map PackedNick UserStatus)
          LB
          (Either
             SomeException (Map PackedNick Int, Map PackedNick UserStatus)))
    -> (Map PackedNick Int, Map PackedNick UserStatus)
    -> ModuleT
         (Map PackedNick Int, Map PackedNick UserStatus)
         LB
         (Either
            SomeException (Map PackedNick Int, Map PackedNick UserStatus))
    forall a b. (a -> b) -> a -> b
    $ ByteString -> (Map PackedNick Int, Map PackedNick UserStatus)
    forall a. Binary a => ByteString -> a
    decode ByteString
    ls
            case Either
      SomeException (Map PackedNick Int, Map PackedNick UserStatus)
    mbDecoded of
                Left exc :: SomeException
    exc@SomeException{} -> do
                    -- try reading the old format (slightly different type... oh, "binary"...)
                    Either SomeException (Map String Int, Map PackedNick UserStatus)
    mbOld <- IO
      (Either SomeException (Map String Int, Map PackedNick UserStatus))
    -> ModuleT
         (Map PackedNick Int, Map PackedNick UserStatus)
         LB
         (Either SomeException (Map String Int, Map PackedNick UserStatus))
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io (IO
       (Either SomeException (Map String Int, Map PackedNick UserStatus))
     -> ModuleT
          (Map PackedNick Int, Map PackedNick UserStatus)
          LB
          (Either SomeException (Map String Int, Map PackedNick UserStatus)))
    -> ((Map String Int, Map PackedNick UserStatus)
        -> IO
             (Either SomeException (Map String Int, Map PackedNick UserStatus)))
    -> (Map String Int, Map PackedNick UserStatus)
    -> ModuleT
         (Map PackedNick Int, Map PackedNick UserStatus)
         LB
         (Either SomeException (Map String Int, Map PackedNick UserStatus))
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . IO (Map String Int, Map PackedNick UserStatus)
    -> IO
         (Either SomeException (Map String Int, Map PackedNick UserStatus))
    forall e a. Exception e => IO a -> IO (Either e a)
    try (IO (Map String Int, Map PackedNick UserStatus)
     -> IO
          (Either SomeException (Map String Int, Map PackedNick UserStatus)))
    -> ((Map String Int, Map PackedNick UserStatus)
        -> IO (Map String Int, Map PackedNick UserStatus))
    -> (Map String Int, Map PackedNick UserStatus)
    -> IO
         (Either SomeException (Map String Int, Map PackedNick UserStatus))
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Map String Int, Map PackedNick UserStatus)
    -> IO (Map String Int, Map PackedNick UserStatus)
    forall a. a -> IO a
    evaluate ((Map String Int, Map PackedNick UserStatus)
     -> ModuleT
          (Map PackedNick Int, Map PackedNick UserStatus)
          LB
          (Either SomeException (Map String Int, Map PackedNick UserStatus)))
    -> (Map String Int, Map PackedNick UserStatus)
    -> ModuleT
         (Map PackedNick Int, Map PackedNick UserStatus)
         LB
         (Either SomeException (Map String Int, Map PackedNick UserStatus))
    forall a b. (a -> b) -> a -> b
    $ ByteString -> (Map String Int, Map PackedNick UserStatus)
    forall a. Binary a => ByteString -> a
    decode ByteString
    ls
                    case Either SomeException (Map String Int, Map PackedNick UserStatus)
    mbOld of
                        Left SomeException{} ->
                            String
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    forall (m :: * -> *). MonadLogging m => String -> m ()
    warningM (String
    "WARNING: failed to read Seen module state: "  String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ SomeException -> String
    forall a. Show a => a -> String
    show SomeException
    exc)
                        Right (Map String Int
    maxMap, Map PackedNick UserStatus
    seenMap) ->
                            LBState Seen
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    forall (m :: * -> *). MonadLBState m => LBState m -> m ()
    writeMS ((String -> PackedNick) -> Map String Int -> Map PackedNick Int
    forall k2 k1 a. Ord k2 => (k1 -> k2) -> Map k1 a -> Map k2 a
    M.mapKeys String -> PackedNick
    P.pack Map String Int
    maxMap, Map PackedNick UserStatus
    seenMap)
                Right (Map PackedNick Int, Map PackedNick UserStatus)
    decoded -> LBState Seen
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    forall (m :: * -> *). MonadLBState m => LBState m -> m ()
    writeMS (Map PackedNick Int, Map PackedNick UserStatus)
    LBState Seen
    decoded

        , moduleExit :: ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    moduleExit = do
            [Nick]
    chans <- LB [Nick]
    -> ModuleT
         (Map PackedNick Int, Map PackedNick UserStatus) LB [Nick]
    forall (m :: * -> *) a.
    Monad m =>
    m a -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift (LB [Nick]
     -> ModuleT
          (Map PackedNick Int, Map PackedNick UserStatus) LB [Nick])
    -> LB [Nick]
    -> ModuleT
         (Map PackedNick Int, Map PackedNick UserStatus) LB [Nick]
    forall a b. (a -> b) -> a -> b
    $ LB [Nick]
    ircGetChannels
            Bool
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
    unless ([Nick] -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [Nick]
    chans) (ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
     -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ())
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    forall a b. (a -> b) -> a -> b
    $ do
                ClockTime
    ct    <- IO ClockTime
    -> ModuleT
         (Map PackedNick Int, Map PackedNick UserStatus) LB ClockTime
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io IO ClockTime
    getClockTime
                (LBState Seen -> LBState Seen)
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    forall (m :: * -> *).
    MonadLBState m =>
    (LBState m -> LBState m) -> m ()
    modifyMS ((LBState Seen -> LBState Seen)
     -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ())
    -> (LBState Seen -> LBState Seen)
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    forall a b. (a -> b) -> a -> b
    $ \(Map PackedNick Int
    n,Map PackedNick UserStatus
    m) -> (Map PackedNick Int
    n, ClockTime
    -> [PackedNick]
    -> Map PackedNick UserStatus
    -> Map PackedNick UserStatus
    botPart ClockTime
    ct ((Nick -> PackedNick) -> [Nick] -> [PackedNick]
    forall a b. (a -> b) -> [a] -> [b]
    map Nick -> PackedNick
    packNick [Nick]
    chans) Map PackedNick UserStatus
    m)

            -- and write out our state:
            (LBState Seen
     -> (LBState Seen
         -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ())
     -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ())
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    forall a.
    (LBState Seen
     -> (LBState Seen
         -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ())
     -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB a)
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB a
    forall (m :: * -> *) a.
    MonadLBState m =>
    (LBState m -> (LBState m -> m ()) -> m a) -> m a
    withMS ((LBState Seen
      -> (LBState Seen
          -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ())
      -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ())
     -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ())
    -> (LBState Seen
        -> (LBState Seen
            -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ())
        -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ())
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    forall a b. (a -> b) -> a -> b
    $ \LBState Seen
    s LBState Seen
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    _ -> LB String
    -> ModuleT
         (Map PackedNick Int, Map PackedNick UserStatus) LB String
    forall a.
    LB a
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB a
    forall (m :: * -> *) a. MonadLB m => LB a -> m a
    lb (String -> LB String
    findLBFileForWriting String
    "seen") ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB String
    -> (String
        -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ())
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    forall a b.
    ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB a
    -> (a
        -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB b)
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \ String
    c -> IO ()
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io (String -> (Map PackedNick Int, Map PackedNick UserStatus) -> IO ()
    forall a. Binary a => String -> a -> IO ()
    encodeFile String
    c (Map PackedNick Int, Map PackedNick UserStatus)
    LBState Seen
    s)
        }

    lcNick :: Nick -> Nick
    lcNick :: Nick -> Nick
    lcNick (Nick String
    svr String
    nck) = String -> String -> Nick
    Nick String
    svr ((Char -> Char) -> String -> String
    forall a b. (a -> b) -> [a] -> [b]
    map Char -> Char
    toLower String
    nck)

    ------------------------------------------------------------------------

    doUsers :: String -> Cmd Seen ()
    doUsers :: String -> Cmd Seen ()
    doUsers String
    rest = (forall a. Message a => a -> Cmd Seen ()) -> Cmd Seen ()
    forall (m :: * -> *) t.
    Monad m =>
    (forall a. Message a => a -> Cmd m t) -> Cmd m t
    withMsg ((forall a. Message a => a -> Cmd Seen ()) -> Cmd Seen ())
    -> (forall a. Message a => a -> Cmd Seen ()) -> Cmd Seen ()
    forall a b. (a -> b) -> a -> b
    $ \a
    msg -> do
        -- first step towards tracking the maximum number of users
        Nick
    chan <- Cmd Seen Nick
    forall (m :: * -> *). Monad m => Cmd m Nick
    getTarget
        (Map PackedNick Int
    m, Map PackedNick UserStatus
    seenFM) <- Cmd Seen (Map PackedNick Int, Map PackedNick UserStatus)
    Cmd Seen (LBState (Cmd Seen))
    forall (m :: * -> *). MonadLBState m => m (LBState m)
    readMS
        ClockTime
    s <- IO ClockTime -> Cmd Seen ClockTime
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io IO ClockTime
    getClockTime
        let who :: PackedNick
    who = Nick -> PackedNick
    packNick (Nick -> PackedNick) -> Nick -> PackedNick
    forall a b. (a -> b) -> a -> b
    $ Nick -> Nick
    lcNick (Nick -> Nick) -> Nick -> Nick
    forall a b. (a -> b) -> a -> b
    $ if String -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null String
    rest then Nick
    chan else String -> String -> Nick
    parseNick (a -> String
    forall a. Message a => a -> String
    G.server a
    msg) String
    rest
            now :: Int
    now = [()] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [ () | (PackedNick
    _,Present LastSpoke
    _ [PackedNick]
    chans) <- Map PackedNick UserStatus -> [(PackedNick, UserStatus)]
    forall k a. Map k a -> [(k, a)]
    M.toList Map PackedNick UserStatus
    seenFM
                              , PackedNick
    who PackedNick -> [PackedNick] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [PackedNick]
    chans ]

            n :: Int
    n = case PackedNick -> Map PackedNick Int -> Maybe Int
    forall k a. Ord k => k -> Map k a -> Maybe a
    M.lookup PackedNick
    who Map PackedNick Int
    m of Maybe Int
    Nothing -> Int
    1; Just Int
    n' -> Int
    n'

            active :: Int
    active = [()] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [() | (PackedNick
    _,st :: UserStatus
    st@(Present LastSpoke
    _ [PackedNick]
    chans)) <- Map PackedNick UserStatus -> [(PackedNick, UserStatus)]
    forall k a. Map k a -> [(k, a)]
    M.toList Map PackedNick UserStatus
    seenFM
                                , PackedNick
    who PackedNick -> [PackedNick] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [PackedNick]
    chans Bool -> Bool -> Bool
    && UserStatus -> Bool
    isActive UserStatus
    st ]

            isActive :: UserStatus -> Bool
    isActive (Present (Just (ClockTime
    ct,TimeDiff
    _td)) [PackedNick]
    _cs) = ClockTime -> Bool
    recent ClockTime
    ct
            isActive UserStatus
    _                             = Bool
    False

            recent :: ClockTime -> Bool
    recent ClockTime
    t = ClockTime -> ClockTime -> TimeDiff
    diffClockTimes ClockTime
    s ClockTime
    t TimeDiff -> TimeDiff -> Bool
    forall a. Ord a => a -> a -> Bool
    < TimeDiff
    gap_minutes
            gap_minutes :: TimeDiff
    gap_minutes = NominalDiffTime -> TimeDiff
    TimeDiff NominalDiffTime
    1800 -- 30 minutes

            percent :: a -> a -> Double
    percent a
    p a
    q = Double
    100 Double -> Double -> Double
    forall a. Num a => a -> a -> a
    * (a -> Double
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral a
    p Double -> Double -> Double
    forall a. Fractional a => a -> a -> a
    / a -> Double
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral a
    q) :: Double

            total :: a -> a -> String
    total   a
    0 a
    0 = String
    "0"
            total   a
    p a
    q = String -> a -> Double -> String
    forall r. PrintfType r => String -> r
    printf String
    "%d (%0.1f%%)" a
    p (a -> a -> Double
    forall {a} {a}. (Integral a, Integral a) => a -> a -> Double
    percent a
    p a
    q)

        String -> Cmd Seen ()
    forall (m :: * -> *). Monad m => String -> Cmd m ()
    say (String -> Cmd Seen ()) -> String -> Cmd Seen ()
    forall a b. (a -> b) -> a -> b
    $! String -> String -> Int -> String -> String -> String
    forall r. PrintfType r => String -> r
    printf String
    "Maximum users seen in %s: %d, currently: %s, active: %s"
            (String -> Nick -> String
    fmtNick (a -> String
    forall a. Message a => a -> String
    G.server a
    msg) (Nick -> String) -> Nick -> String
    forall a b. (a -> b) -> a -> b
    $ PackedNick -> Nick
    unpackNick PackedNick
    who) Int
    n (Int -> Int -> String
    forall {a} {a}.
    (PrintfArg a, Integral a, Integral a) =>
    a -> a -> String
    total Int
    now Int
    n) (Int -> Int -> String
    forall {a} {a}.
    (PrintfArg a, Integral a, Integral a) =>
    a -> a -> String
    total Int
    active Int
    now)

    doSeen :: String -> Cmd Seen ()
    doSeen :: String -> Cmd Seen ()
    doSeen String
    rest = (forall a. Message a => a -> Cmd Seen ()) -> Cmd Seen ()
    forall (m :: * -> *) t.
    Monad m =>
    (forall a. Message a => a -> Cmd m t) -> Cmd m t
    withMsg ((forall a. Message a => a -> Cmd Seen ()) -> Cmd Seen ())
    -> (forall a. Message a => a -> Cmd Seen ()) -> Cmd Seen ()
    forall a b. (a -> b) -> a -> b
    $ \a
    msg -> do
        Nick
    target <- Cmd Seen Nick
    forall (m :: * -> *). Monad m => Cmd m Nick
    getTarget
        (Map PackedNick Int
    _,Map PackedNick UserStatus
    seenFM) <- Cmd Seen (Map PackedNick Int, Map PackedNick UserStatus)
    Cmd Seen (LBState (Cmd Seen))
    forall (m :: * -> *). MonadLBState m => m (LBState m)
    readMS
        ClockTime
    now        <- IO ClockTime -> Cmd Seen ClockTime
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io IO ClockTime
    getClockTime
        let ([String]
    txt,Bool
    safe) = (a
    -> String
    -> Map PackedNick UserStatus
    -> ClockTime
    -> ([String], Bool)
    forall a.
    Message a =>
    a
    -> String
    -> Map PackedNick UserStatus
    -> ClockTime
    -> ([String], Bool)
    getAnswer a
    msg String
    rest Map PackedNick UserStatus
    seenFM ClockTime
    now)
        if Bool
    safe Bool -> Bool -> Bool
    || Bool -> Bool
    not (String
    "#" String -> String -> Bool
    forall a. Eq a => [a] -> [a] -> Bool
    `isPrefixOf` Nick -> String
    nName Nick
    target)
            then (String -> Cmd Seen ()) -> [String] -> Cmd Seen ()
    forall (t :: * -> *) (m :: * -> *) a b.
    (Foldable t, Monad m) =>
    (a -> m b) -> t a -> m ()
    mapM_ String -> Cmd Seen ()
    forall (m :: * -> *). Monad m => String -> Cmd m ()
    say [String]
    txt
            else LB () -> Cmd Seen ()
    forall a. LB a -> Cmd Seen a
    forall (m :: * -> *) a. MonadLB m => LB a -> m a
    lb (Nick -> String -> LB ()
    ircPrivmsg (a -> Nick
    forall a. Message a => a -> Nick
    G.nick a
    msg) ([String] -> String
    unlines [String]
    txt))

    getAnswer :: G.Message a => a -> String -> SeenMap -> ClockTime -> ([String], Bool)
    getAnswer :: forall a.
    Message a =>
    a
    -> String
    -> Map PackedNick UserStatus
    -> ClockTime
    -> ([String], Bool)
    getAnswer a
    msg String
    rest Map PackedNick UserStatus
    seenFM ClockTime
    now
        | String -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null String
    nick' =
            let people :: [PackedNick]
    people  = ((PackedNick, UserStatus) -> PackedNick)
    -> [(PackedNick, UserStatus)] -> [PackedNick]
    forall a b. (a -> b) -> [a] -> [b]
    map (PackedNick, UserStatus) -> PackedNick
    forall a b. (a, b) -> a
    fst ([(PackedNick, UserStatus)] -> [PackedNick])
    -> [(PackedNick, UserStatus)] -> [PackedNick]
    forall a b. (a -> b) -> a -> b
    $ ((PackedNick, UserStatus) -> Bool)
    -> [(PackedNick, UserStatus)] -> [(PackedNick, UserStatus)]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (PackedNick, UserStatus) -> Bool
    forall {a}. (a, UserStatus) -> Bool
    isActive ([(PackedNick, UserStatus)] -> [(PackedNick, UserStatus)])
    -> [(PackedNick, UserStatus)] -> [(PackedNick, UserStatus)]
    forall a b. (a -> b) -> a -> b
    $ Map PackedNick UserStatus -> [(PackedNick, UserStatus)]
    forall k a. Map k a -> [(k, a)]
    M.toList Map PackedNick UserStatus
    seenFM
                isActive :: (a, UserStatus) -> Bool
    isActive (a
    _nick,UserStatus
    state) = case UserStatus
    state of
                    (Present (Just (ClockTime
    ct,TimeDiff
    _td)) [PackedNick]
    _cs) -> ClockTime -> Bool
    recent ClockTime
    ct
                    UserStatus
    _ -> Bool
    False
                recent :: ClockTime -> Bool
    recent ClockTime
    t = ClockTime -> ClockTime -> TimeDiff
    diffClockTimes ClockTime
    now ClockTime
    t TimeDiff -> TimeDiff -> Bool
    forall a. Ord a => a -> a -> Bool
    < TimeDiff
    gap_minutes
                gap_minutes :: TimeDiff
    gap_minutes = NominalDiffTime -> TimeDiff
    TimeDiff NominalDiffTime
    900 -- 15 minutes
             in ([String
    "Lately, I have seen " String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ (if [PackedNick] -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [PackedNick]
    people then String
    "nobody"
                     else String -> [String] -> String
    listToStr String
    "and" ((PackedNick -> String) -> [PackedNick] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map PackedNick -> String
    upAndShow [PackedNick]
    people)) String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    "."], Bool
    False)

        | Nick
    pnick Nick -> Nick -> Bool
    forall a. Eq a => a -> a -> Bool
    == a -> Nick
    forall a. Message a => a -> Nick
    G.lambdabotName a
    msg =
            case PackedNick -> Map PackedNick UserStatus -> Maybe UserStatus
    forall k a. Ord k => k -> Map k a -> Maybe a
    M.lookup (Nick -> PackedNick
    packNick Nick
    pnick) Map PackedNick UserStatus
    seenFM of
                Just (Present LastSpoke
    _ [PackedNick]
    cs) ->
                    ([String
    "Yes, I'm here. I'm in " String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String -> [String] -> String
    listToStr String
    "and" ((PackedNick -> String) -> [PackedNick] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map PackedNick -> String
    upAndShow [PackedNick]
    cs)], Bool
    True)
                Maybe UserStatus
    _ -> String -> ([String], Bool)
    forall a. HasCallStack => String -> a
    error String
    "I'm here, but not here. And very confused!"

        | String -> Char
    forall a. HasCallStack => [a] -> a
    head (Nick -> String
    nName Nick
    pnick) Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    == Char
    '#' =
            let people :: [PackedNick]
    people  = ((PackedNick, UserStatus) -> PackedNick)
    -> [(PackedNick, UserStatus)] -> [PackedNick]
    forall a b. (a -> b) -> [a] -> [b]
    map (PackedNick, UserStatus) -> PackedNick
    forall a b. (a, b) -> a
    fst ([(PackedNick, UserStatus)] -> [PackedNick])
    -> [(PackedNick, UserStatus)] -> [PackedNick]
    forall a b. (a -> b) -> a -> b
    $ ((PackedNick, UserStatus) -> Bool)
    -> [(PackedNick, UserStatus)] -> [(PackedNick, UserStatus)]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (PackedNick, UserStatus) -> Bool
    forall {a}. (a, UserStatus) -> Bool
    inChan ([(PackedNick, UserStatus)] -> [(PackedNick, UserStatus)])
    -> [(PackedNick, UserStatus)] -> [(PackedNick, UserStatus)]
    forall a b. (a -> b) -> a -> b
    $ Map PackedNick UserStatus -> [(PackedNick, UserStatus)]
    forall k a. Map k a -> [(k, a)]
    M.toList Map PackedNick UserStatus
    seenFM
                inChan :: (a, UserStatus) -> Bool
    inChan (a
    _nick,UserStatus
    state) = case UserStatus
    state of
                    (Present (Just (ClockTime, TimeDiff)
    _) [PackedNick]
    cs)
                        -> Nick -> PackedNick
    packNick Nick
    pnick PackedNick -> [PackedNick] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [PackedNick]
    cs
                    UserStatus
    _   -> Bool
    False
             in ([String
    "In "String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++String
    nick'String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++String
    " I can see "
                String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ (if [PackedNick] -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [PackedNick]
    people then String
    "nobody"    -- todo, how far back does this go?
                   else String -> [String] -> String
    listToStr String
    "and" ((PackedNick -> String) -> [PackedNick] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map PackedNick -> String
    upAndShow [PackedNick]
    people)) String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    "."], Bool
    False)

        | Bool
    otherwise        = (String -> [String]
    forall a. a -> [a]
    forall (m :: * -> *) a. Monad m => a -> m a
    return (String -> [String]) -> String -> [String]
    forall a b. (a -> b) -> a -> b
    $ [String] -> String
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat (case PackedNick -> Map PackedNick UserStatus -> Maybe UserStatus
    forall k a. Ord k => k -> Map k a -> Maybe a
    M.lookup (Nick -> PackedNick
    packNick Nick
    pnick) Map PackedNick UserStatus
    seenFM of
            Just (Present LastSpoke
    mct [PackedNick]
    cs)            -> LastSpoke -> [String] -> [String]
    nickPresent LastSpoke
    mct ((PackedNick -> String) -> [PackedNick] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map PackedNick -> String
    upAndShow [PackedNick]
    cs)
            Just (NotPresent ClockTime
    ct StopWatch
    td [PackedNick]
    chans)    -> ClockTime -> StopWatch -> [String] -> [String]
    nickNotPresent ClockTime
    ct StopWatch
    td ((PackedNick -> String) -> [PackedNick] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map PackedNick -> String
    upAndShow [PackedNick]
    chans)
            Just (WasPresent ClockTime
    ct StopWatch
    sw LastSpoke
    _ [PackedNick]
    chans)  -> ClockTime -> StopWatch -> [String] -> [String]
    nickWasPresent ClockTime
    ct StopWatch
    sw ((PackedNick -> String) -> [PackedNick] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map PackedNick -> String
    upAndShow [PackedNick]
    chans)
            Just (NewNick PackedNick
    newnick)           -> PackedNick -> [String]
    nickIsNew PackedNick
    newnick
            Maybe UserStatus
    _ -> [String
    "I haven't seen ", String
    nick, String
    "."]), Bool
    True)
        where
            -- I guess the only way out of this spagetty hell are printf-style responses.
            upAndShow :: PackedNick -> String
    upAndShow = String -> Nick -> String
    fmtNick (a -> String
    forall a. Message a => a -> String
    G.server a
    msg) (Nick -> String) -> (PackedNick -> Nick) -> PackedNick -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . PackedNick -> Nick
    unpackNick
            nickPresent :: LastSpoke -> [String] -> [String]
    nickPresent LastSpoke
    mct [String]
    cs =
                [ if Bool
    you then String
    "You are" else String
    nick String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    " is"
                , String
    " in ", String -> [String] -> String
    listToStr String
    "and" [String]
    cs, String
    "."
                , case LastSpoke
    mct of
                    LastSpoke
    Nothing          -> [String] -> String
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat [String
    " I don't know when ", String
    nick, String
    " last spoke."]
                    Just (ClockTime
    ct,TimeDiff
    missed) -> StopWatch -> String -> String -> String
    forall {p} {p}. StopWatch -> p -> p -> String
    prettyMissed (TimeDiff -> StopWatch
    Stopped TimeDiff
    missed)
                           ([String] -> String
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat [String
    " I last heard ", String
    nick, String
    " speak ",
                                    String
    lastSpoke {-, ", but "-}])
                           (String
    " Last spoke " String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    lastSpoke)
                        where lastSpoke :: String
    lastSpoke = ClockTime -> String
    clockDifference ClockTime
    ct
                ]
            nickNotPresent :: ClockTime -> StopWatch -> [String] -> [String]
    nickNotPresent ClockTime
    ct StopWatch
    missed [String]
    chans =
                [ String
    "I saw ", String
    nick, String
    " leaving ", String -> [String] -> String
    listToStr String
    "and" [String]
    chans, String
    " "
                , ClockTime -> String
    clockDifference ClockTime
    ct, StopWatch -> String -> String -> String
    forall {p} {p}. StopWatch -> p -> p -> String
    prettyMissed StopWatch
    missed String
    ", and " String
    ""
                ]
            nickWasPresent :: ClockTime -> StopWatch -> [String] -> [String]
    nickWasPresent ClockTime
    ct StopWatch
    sw [String]
    chans =
                [ String
    "Last time I saw ", String
    nick, String
    " was when I left "
                , String -> [String] -> String
    listToStr String
    "and" [String]
    chans , String
    " ", ClockTime -> String
    clockDifference ClockTime
    ct
                , StopWatch -> String -> String -> String
    forall {p} {p}. StopWatch -> p -> p -> String
    prettyMissed StopWatch
    sw String
    ", and " String
    ""
                ]
            nickIsNew :: PackedNick -> [String]
    nickIsNew PackedNick
    newnick =
                [ if Bool
    you then String
    "You have" else String
    nickString -> String -> String
    forall a. [a] -> [a] -> [a]
    ++String
    " has"
                , String
    " changed nick to ", String
    us, String
    "."
                ] [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++ ([String], Bool) -> [String]
    forall a b. (a, b) -> a
    fst (a
    -> String
    -> Map PackedNick UserStatus
    -> ClockTime
    -> ([String], Bool)
    forall a.
    Message a =>
    a
    -> String
    -> Map PackedNick UserStatus
    -> ClockTime
    -> ([String], Bool)
    getAnswer a
    msg String
    us Map PackedNick UserStatus
    seenFM ClockTime
    now)
                where
                    us :: String
    us = PackedNick -> String
    upAndShow (PackedNick -> String) -> PackedNick -> String
    forall a b. (a -> b) -> a -> b
    $ PackedNick -> PackedNick
    findFunc PackedNick
    newnick
                    findFunc :: PackedNick -> PackedNick
    findFunc PackedNick
    pstr = case PackedNick -> Map PackedNick UserStatus -> Maybe UserStatus
    forall k a. Ord k => k -> Map k a -> Maybe a
    M.lookup PackedNick
    pstr Map PackedNick UserStatus
    seenFM of
                        Just (NewNick PackedNick
    pstr') -> PackedNick -> PackedNick
    findFunc PackedNick
    pstr'
                        Just UserStatus
    _               -> PackedNick
    pstr
                        Maybe UserStatus
    Nothing              -> String -> PackedNick
    forall a. HasCallStack => String -> a
    error String
    "SeenModule.nickIsNew: Nothing"

            nick' :: String
    nick' = (Char -> Bool) -> String -> String
    forall a. (a -> Bool) -> [a] -> [a]
    takeWhile (Bool -> Bool
    not (Bool -> Bool) -> (Char -> Bool) -> Char -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Char -> Bool
    isSpace) String
    rest
            you :: Bool
    you   = Nick
    pnick Nick -> Nick -> Bool
    forall a. Eq a => a -> a -> Bool
    == Nick -> Nick
    lcNick (a -> Nick
    forall a. Message a => a -> Nick
    G.nick a
    msg)
            nick :: String
    nick  = if Bool
    you then String
    "you" else String
    nick'
            pnick :: Nick
    pnick = Nick -> Nick
    lcNick (Nick -> Nick) -> Nick -> Nick
    forall a b. (a -> b) -> a -> b
    $ String -> String -> Nick
    parseNick (a -> String
    forall a. Message a => a -> String
    G.server a
    msg) String
    nick'
            clockDifference :: ClockTime -> String
    clockDifference ClockTime
    past
                | (Char -> Bool) -> String -> Bool
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
    all (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    ==Char
    ' ') String
    diff = String
    "just now"
                | Bool
    otherwise        = String
    diff String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    " ago"
                where diff :: String
    diff = TimeDiff -> String
    timeDiffPretty (TimeDiff -> String)
    -> (ClockTime -> TimeDiff) -> ClockTime -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ClockTime -> ClockTime -> TimeDiff
    diffClockTimes ClockTime
    now (ClockTime -> String) -> ClockTime -> String
    forall a b. (a -> b) -> a -> b
    $ ClockTime
    past

            prettyMissed :: StopWatch -> p -> p -> String
    prettyMissed (Stopped TimeDiff
    _) p
    _ifMissed p
    _     = String
    "." -- ifMissed ++ "."
            prettyMissed StopWatch
    _           p
    _ p
    _ifNotMissed  = String
    "." -- ifNotMissed ++ "."

    {-
            prettyMissed (Stopped missed) ifMissed _
                | missedPretty <- timeDiffPretty missed
                , any (/=' ') missedPretty
                = concat [ifMissed, "I have missed ", missedPretty, " since then."]

            prettyMissed _ _ ifNotMissed = ifNotMissed ++ "."
    -}

    -- | extract channels from message as packed, lower cased, strings.
    msgChans :: G.Message a => a -> [Channel]
    msgChans :: forall a. Message a => a -> [PackedNick]
    msgChans = (Nick -> PackedNick) -> [Nick] -> [PackedNick]
    forall a b. (a -> b) -> [a] -> [b]
    map (Nick -> PackedNick
    packNick (Nick -> PackedNick) -> (Nick -> Nick) -> Nick -> PackedNick
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Nick -> Nick
    lcNick) ([Nick] -> [PackedNick]) -> (a -> [Nick]) -> a -> [PackedNick]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . a -> [Nick]
    forall a. Message a => a -> [Nick]
    G.channels

    -- | Callback for when somebody joins. If it is not the bot that joins, record
    --   that we have a new user in our state tree and that we have never seen the
    --   user speaking.
    joinCB :: IrcMessage -> ClockTime -> PackedNick -> SeenMap -> Either String SeenMap
    joinCB :: IrcMessage
    -> ClockTime
    -> PackedNick
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    joinCB IrcMessage
    msg ClockTime
    _ct PackedNick
    nick Map PackedNick UserStatus
    fm
        | PackedNick
    nick PackedNick -> PackedNick -> Bool
    forall a. Eq a => a -> a -> Bool
    == PackedNick
    lbNick = Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    forall a b. b -> Either a b
    Right Map PackedNick UserStatus
    fm
        | Bool
    otherwise      = Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    forall a b. b -> Either a b
    Right (Map PackedNick UserStatus
     -> Either String (Map PackedNick UserStatus))
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    forall a b. (a -> b) -> a -> b
    $! (UserStatus -> UserStatus)
    -> PackedNick
    -> UserStatus
    -> Map PackedNick UserStatus
    -> Map PackedNick UserStatus
    forall {k} {a}. Ord k => (a -> a) -> k -> a -> Map k a -> Map k a
    insertUpd (Maybe ClockTime -> [PackedNick] -> UserStatus -> UserStatus
    updateJ Maybe ClockTime
    forall a. Maybe a
    Nothing [PackedNick]
    chans) PackedNick
    nick UserStatus
    newInfo Map PackedNick UserStatus
    fm
        where
            insertUpd :: (a -> a) -> k -> a -> Map k a -> Map k a
    insertUpd a -> a
    f = (a -> a -> a) -> k -> a -> Map k a -> Map k a
    forall k a. Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
    M.insertWith (\a
    _ -> a -> a
    f)
            lbNick :: PackedNick
    lbNick  = Nick -> PackedNick
    packNick (Nick -> PackedNick) -> Nick -> PackedNick
    forall a b. (a -> b) -> a -> b
    $ IrcMessage -> Nick
    forall a. Message a => a -> Nick
    G.lambdabotName IrcMessage
    msg
            newInfo :: UserStatus
    newInfo = LastSpoke -> [PackedNick] -> UserStatus
    Present LastSpoke
    forall a. Maybe a
    Nothing [PackedNick]
    chans
            chans :: [PackedNick]
    chans   = IrcMessage -> [PackedNick]
    forall a. Message a => a -> [PackedNick]
    msgChans IrcMessage
    msg

    -- | Update the state to reflect the bot leaving channel(s)
    botPart :: ClockTime -> [Channel] -> SeenMap -> SeenMap
    botPart :: ClockTime
    -> [PackedNick]
    -> Map PackedNick UserStatus
    -> Map PackedNick UserStatus
    botPart ClockTime
    ct [PackedNick]
    cs = (UserStatus -> UserStatus)
    -> Map PackedNick UserStatus -> Map PackedNick UserStatus
    forall a b. (a -> b) -> Map PackedNick a -> Map PackedNick b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap UserStatus -> UserStatus
    botPart'
        where
            botPart' :: UserStatus -> UserStatus
    botPart' (Present LastSpoke
    mct [PackedNick]
    xs) = case [PackedNick]
    xs [PackedNick] -> [PackedNick] -> [PackedNick]
    forall a. Eq a => [a] -> [a] -> [a]
    \\ [PackedNick]
    cs of
                [] -> ClockTime -> StopWatch -> LastSpoke -> [PackedNick] -> UserStatus
    WasPresent ClockTime
    ct (ClockTime -> StopWatch -> StopWatch
    startWatch ClockTime
    ct StopWatch
    zeroWatch) LastSpoke
    mct [PackedNick]
    cs
                [PackedNick]
    ys -> LastSpoke -> [PackedNick] -> UserStatus
    Present LastSpoke
    mct [PackedNick]
    ys
            botPart' (NotPresent ClockTime
    ct' StopWatch
    missed [PackedNick]
    c)
                | [PackedNick] -> PackedNick
    forall a. HasCallStack => [a] -> a
    head [PackedNick]
    c PackedNick -> [PackedNick] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [PackedNick]
    cs = ClockTime -> StopWatch -> [PackedNick] -> UserStatus
    NotPresent ClockTime
    ct' (ClockTime -> StopWatch -> StopWatch
    startWatch ClockTime
    ct StopWatch
    missed) [PackedNick]
    c
            botPart' (WasPresent ClockTime
    ct' StopWatch
    missed LastSpoke
    mct [PackedNick]
    c)
                | [PackedNick] -> PackedNick
    forall a. HasCallStack => [a] -> a
    head [PackedNick]
    c PackedNick -> [PackedNick] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [PackedNick]
    cs = ClockTime -> StopWatch -> LastSpoke -> [PackedNick] -> UserStatus
    WasPresent ClockTime
    ct' (ClockTime -> StopWatch -> StopWatch
    startWatch ClockTime
    ct StopWatch
    missed) LastSpoke
    mct [PackedNick]
    c
            botPart' UserStatus
    us = UserStatus
    us

    -- | when somebody parts
    partCB :: IrcMessage -> ClockTime -> PackedNick -> SeenMap -> Either String SeenMap
    partCB :: IrcMessage
    -> ClockTime
    -> PackedNick
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    partCB IrcMessage
    msg ClockTime
    ct PackedNick
    nick Map PackedNick UserStatus
    fm
        | PackedNick
    nick PackedNick -> PackedNick -> Bool
    forall a. Eq a => a -> a -> Bool
    == PackedNick
    lbNick = Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    forall a b. b -> Either a b
    Right (Map PackedNick UserStatus
     -> Either String (Map PackedNick UserStatus))
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    forall a b. (a -> b) -> a -> b
    $ ClockTime
    -> [PackedNick]
    -> Map PackedNick UserStatus
    -> Map PackedNick UserStatus
    botPart ClockTime
    ct (IrcMessage -> [PackedNick]
    forall a. Message a => a -> [PackedNick]
    msgChans IrcMessage
    msg) Map PackedNick UserStatus
    fm
        | Bool
    otherwise      = case PackedNick -> Map PackedNick UserStatus -> Maybe UserStatus
    forall k a. Ord k => k -> Map k a -> Maybe a
    M.lookup PackedNick
    nick Map PackedNick UserStatus
    fm of
            Just (Present LastSpoke
    mct [PackedNick]
    xs) ->
                case [PackedNick]
    xs [PackedNick] -> [PackedNick] -> [PackedNick]
    forall a. Eq a => [a] -> [a] -> [a]
    \\ (IrcMessage -> [PackedNick]
    forall a. Message a => a -> [PackedNick]
    msgChans IrcMessage
    msg) of
                    [] -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    forall a b. b -> Either a b
    Right (Map PackedNick UserStatus
     -> Either String (Map PackedNick UserStatus))
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    forall a b. (a -> b) -> a -> b
    $! PackedNick
    -> UserStatus
    -> Map PackedNick UserStatus
    -> Map PackedNick UserStatus
    forall k a. Ord k => k -> a -> Map k a -> Map k a
    M.insert PackedNick
    nick (ClockTime -> StopWatch -> [PackedNick] -> UserStatus
    NotPresent ClockTime
    ct StopWatch
    zeroWatch [PackedNick]
    xs) Map PackedNick UserStatus
    fm
                    [PackedNick]
    ys -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    forall a b. b -> Either a b
    Right (Map PackedNick UserStatus
     -> Either String (Map PackedNick UserStatus))
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    forall a b. (a -> b) -> a -> b
    $! PackedNick
    -> UserStatus
    -> Map PackedNick UserStatus
    -> Map PackedNick UserStatus
    forall k a. Ord k => k -> a -> Map k a -> Map k a
    M.insert PackedNick
    nick (LastSpoke -> [PackedNick] -> UserStatus
    Present LastSpoke
    mct [PackedNick]
    ys)             Map PackedNick UserStatus
    fm
            Maybe UserStatus
    _ -> String -> Either String (Map PackedNick UserStatus)
    forall a b. a -> Either a b
    Left String
    "someone who isn't known parted"
        where lbNick :: PackedNick
    lbNick = Nick -> PackedNick
    packNick (Nick -> PackedNick) -> Nick -> PackedNick
    forall a b. (a -> b) -> a -> b
    $ IrcMessage -> Nick
    forall a. Message a => a -> Nick
    G.lambdabotName IrcMessage
    msg

    -- | when somebody quits
    quitCB :: IrcMessage -> ClockTime -> PackedNick -> SeenMap -> Either String SeenMap
    quitCB :: IrcMessage
    -> ClockTime
    -> PackedNick
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    quitCB IrcMessage
    _ ClockTime
    ct PackedNick
    nick Map PackedNick UserStatus
    fm = case PackedNick -> Map PackedNick UserStatus -> Maybe UserStatus
    forall k a. Ord k => k -> Map k a -> Maybe a
    M.lookup PackedNick
    nick Map PackedNick UserStatus
    fm of
        Just (Present LastSpoke
    _ct [PackedNick]
    xs) -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    forall a b. b -> Either a b
    Right (Map PackedNick UserStatus
     -> Either String (Map PackedNick UserStatus))
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    forall a b. (a -> b) -> a -> b
    $! PackedNick
    -> UserStatus
    -> Map PackedNick UserStatus
    -> Map PackedNick UserStatus
    forall k a. Ord k => k -> a -> Map k a -> Map k a
    M.insert PackedNick
    nick (ClockTime -> StopWatch -> [PackedNick] -> UserStatus
    NotPresent ClockTime
    ct StopWatch
    zeroWatch [PackedNick]
    xs) Map PackedNick UserStatus
    fm
        Maybe UserStatus
    _                     -> String -> Either String (Map PackedNick UserStatus)
    forall a b. a -> Either a b
    Left String
    "someone who isn't known has quit"

    -- | when somebody changes his\/her name
    nickCB :: IrcMessage -> ClockTime -> PackedNick -> SeenMap -> Either String SeenMap
    nickCB :: IrcMessage
    -> ClockTime
    -> PackedNick
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    nickCB IrcMessage
    msg ClockTime
    _ PackedNick
    nick Map PackedNick UserStatus
    fm = case PackedNick -> Map PackedNick UserStatus -> Maybe UserStatus
    forall k a. Ord k => k -> Map k a -> Maybe a
    M.lookup PackedNick
    nick Map PackedNick UserStatus
    fm of
        Just UserStatus
    status -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    forall a b. b -> Either a b
    Right (Map PackedNick UserStatus
     -> Either String (Map PackedNick UserStatus))
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    forall a b. (a -> b) -> a -> b
    $! PackedNick
    -> UserStatus
    -> Map PackedNick UserStatus
    -> Map PackedNick UserStatus
    forall k a. Ord k => k -> a -> Map k a -> Map k a
    M.insert PackedNick
    lcnewnick UserStatus
    status
                              (Map PackedNick UserStatus -> Map PackedNick UserStatus)
    -> Map PackedNick UserStatus -> Map PackedNick UserStatus
    forall a b. (a -> b) -> a -> b
    $ PackedNick
    -> UserStatus
    -> Map PackedNick UserStatus
    -> Map PackedNick UserStatus
    forall k a. Ord k => k -> a -> Map k a -> Map k a
    M.insert PackedNick
    nick (PackedNick -> UserStatus
    NewNick PackedNick
    lcnewnick) Map PackedNick UserStatus
    fm
        Maybe UserStatus
    _           -> String -> Either String (Map PackedNick UserStatus)
    forall a b. a -> Either a b
    Left String
    "someone who isn't here changed nick"
        where
            newnick :: String
    newnick = Int -> String -> String
    forall a. Int -> [a] -> [a]
    drop Int
    1 (String -> String) -> String -> String
    forall a b. (a -> b) -> a -> b
    $ [String] -> String
    forall a. HasCallStack => [a] -> a
    head (IrcMessage -> [String]
    ircMsgParams IrcMessage
    msg)
            lcnewnick :: PackedNick
    lcnewnick = Nick -> PackedNick
    packNick (Nick -> PackedNick) -> Nick -> PackedNick
    forall a b. (a -> b) -> a -> b
    $ Nick -> Nick
    lcNick (Nick -> Nick) -> Nick -> Nick
    forall a b. (a -> b) -> a -> b
    $ String -> String -> Nick
    parseNick (IrcMessage -> String
    forall a. Message a => a -> String
    G.server IrcMessage
    msg) String
    newnick

    -- | when the bot joins a channel
    joinChanCB :: IrcMessage -> ClockTime -> PackedNick -> SeenMap -> Either String SeenMap
    joinChanCB :: IrcMessage
    -> ClockTime
    -> PackedNick
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    joinChanCB IrcMessage
    msg ClockTime
    now PackedNick
    _nick Map PackedNick UserStatus
    fm
        = Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    forall a b. b -> Either a b
    Right (Map PackedNick UserStatus
     -> Either String (Map PackedNick UserStatus))
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    forall a b. (a -> b) -> a -> b
    $! (UserStatus -> UserStatus)
    -> Map PackedNick UserStatus -> Map PackedNick UserStatus
    forall a b. (a -> b) -> Map PackedNick a -> Map PackedNick b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (ClockTime -> PackedNick -> UserStatus -> UserStatus
    updateNP ClockTime
    now PackedNick
    chan) ((Map PackedNick UserStatus
     -> PackedNick -> Map PackedNick UserStatus)
    -> Map PackedNick UserStatus
    -> [PackedNick]
    -> Map PackedNick UserStatus
    forall b a. (b -> a -> b) -> b -> [a] -> b
    forall (t :: * -> *) b a.
    Foldable t =>
    (b -> a -> b) -> b -> t a -> b
    foldl Map PackedNick UserStatus
    -> PackedNick -> Map PackedNick UserStatus
    insertNick Map PackedNick UserStatus
    fm [PackedNick]
    chanUsers)
        where
            l :: [String]
    l = IrcMessage -> [String]
    ircMsgParams IrcMessage
    msg
            chan :: PackedNick
    chan = Nick -> PackedNick
    packNick (Nick -> PackedNick) -> Nick -> PackedNick
    forall a b. (a -> b) -> a -> b
    $ Nick -> Nick
    lcNick (Nick -> Nick) -> Nick -> Nick
    forall a b. (a -> b) -> a -> b
    $ String -> String -> Nick
    parseNick (IrcMessage -> String
    forall a. Message a => a -> String
    G.server IrcMessage
    msg) (String -> Nick) -> String -> Nick
    forall a b. (a -> b) -> a -> b
    $ [String]
    l [String] -> Int -> String
    forall a. HasCallStack => [a] -> Int -> a
    !! Int
    2
            chanUsers :: [PackedNick]
    chanUsers = (String -> PackedNick) -> [String] -> [PackedNick]
    forall a b. (a -> b) -> [a] -> [b]
    map (Nick -> PackedNick
    packNick (Nick -> PackedNick) -> (String -> Nick) -> String -> PackedNick
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Nick -> Nick
    lcNick (Nick -> Nick) -> (String -> Nick) -> String -> Nick
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> String -> Nick
    parseNick (IrcMessage -> String
    forall a. Message a => a -> String
    G.server IrcMessage
    msg)) ([String] -> [PackedNick]) -> [String] -> [PackedNick]
    forall a b. (a -> b) -> a -> b
    $ String -> [String]
    words (Int -> String -> String
    forall a. Int -> [a] -> [a]
    drop Int
    1 ([String]
    l [String] -> Int -> String
    forall a. HasCallStack => [a] -> Int -> a
    !! Int
    3)) -- remove ':'
            unUserMode :: Nick -> Nick
    unUserMode Nick
    nick = String -> String -> Nick
    Nick (Nick -> String
    nTag Nick
    nick) ((Char -> Bool) -> String -> String
    forall a. (a -> Bool) -> [a] -> [a]
    dropWhile (Char -> String -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` String
    "@+") (String -> String) -> String -> String
    forall a b. (a -> b) -> a -> b
    $ Nick -> String
    nName Nick
    nick)
            insertUpd :: (a -> a) -> k -> a -> Map k a -> Map k a
    insertUpd a -> a
    f = (a -> a -> a) -> k -> a -> Map k a -> Map k a
    forall k a. Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
    M.insertWith (\a
    _ -> a -> a
    f)
            insertNick :: Map PackedNick UserStatus
    -> PackedNick -> Map PackedNick UserStatus
    insertNick Map PackedNick UserStatus
    fm' PackedNick
    u = (UserStatus -> UserStatus)
    -> PackedNick
    -> UserStatus
    -> Map PackedNick UserStatus
    -> Map PackedNick UserStatus
    forall {k} {a}. Ord k => (a -> a) -> k -> a -> Map k a -> Map k a
    insertUpd (Maybe ClockTime -> [PackedNick] -> UserStatus -> UserStatus
    updateJ (ClockTime -> Maybe ClockTime
    forall a. a -> Maybe a
    Just ClockTime
    now) [PackedNick
    chan])
                (Nick -> PackedNick
    packNick (Nick -> PackedNick)
    -> (PackedNick -> Nick) -> PackedNick -> PackedNick
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Nick -> Nick
    unUserMode (Nick -> Nick) -> (PackedNick -> Nick) -> PackedNick -> Nick
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Nick -> Nick
    lcNick (Nick -> Nick) -> (PackedNick -> Nick) -> PackedNick -> Nick
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . PackedNick -> Nick
    unpackNick (PackedNick -> PackedNick) -> PackedNick -> PackedNick
    forall a b. (a -> b) -> a -> b
    $ PackedNick
    u)
                (LastSpoke -> [PackedNick] -> UserStatus
    Present LastSpoke
    forall a. Maybe a
    Nothing [PackedNick
    chan]) Map PackedNick UserStatus
    fm'

    -- | when somebody speaks, update their clocktime
    msgCB :: IrcMessage -> ClockTime -> PackedNick -> SeenMap -> Either String SeenMap
    msgCB :: IrcMessage
    -> ClockTime
    -> PackedNick
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    msgCB IrcMessage
    _ ClockTime
    ct PackedNick
    nick Map PackedNick UserStatus
    fm =
        case PackedNick -> Map PackedNick UserStatus -> Maybe UserStatus
    forall k a. Ord k => k -> Map k a -> Maybe a
    M.lookup PackedNick
    nick Map PackedNick UserStatus
    fm of
            Just (Present LastSpoke
    _ [PackedNick]
    xs) -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    forall a b. b -> Either a b
    Right (Map PackedNick UserStatus
     -> Either String (Map PackedNick UserStatus))
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    forall a b. (a -> b) -> a -> b
    $!
                PackedNick
    -> UserStatus
    -> Map PackedNick UserStatus
    -> Map PackedNick UserStatus
    forall k a. Ord k => k -> a -> Map k a -> Map k a
    M.insert PackedNick
    nick (LastSpoke -> [PackedNick] -> UserStatus
    Present ((ClockTime, TimeDiff) -> LastSpoke
    forall a. a -> Maybe a
    Just (ClockTime
    ct, TimeDiff
    noTimeDiff)) [PackedNick]
    xs) Map PackedNick UserStatus
    fm
            Maybe UserStatus
    _ -> String -> Either String (Map PackedNick UserStatus)
    forall a b. a -> Either a b
    Left String
    "someone who isn't here msg us"

    -- | Callbacks are only allowed to use a limited knowledge of the world.
    -- 'withSeenFM' is (up to trivial isomorphism) a monad morphism from the
    -- restricted
    --   'ReaderT (IRC.Message, ClockTime, Nick) (StateT SeenState (Error String))'
    -- to the
    --   'ReaderT IRC.Message (Seen IRC)'
    -- monad.
    withSeenFM :: G.Message a
               => String
               -> (a -> ClockTime -> PackedNick -> SeenMap -> Either String SeenMap)
               -> (a -> Seen ())

    withSeenFM :: forall a.
    Message a =>
    String
    -> (a
        -> ClockTime
        -> PackedNick
        -> Map PackedNick UserStatus
        -> Either String (Map PackedNick UserStatus))
    -> a
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    withSeenFM String
    signal a
    -> ClockTime
    -> PackedNick
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    f a
    msg = do
        let chan :: PackedNick
    chan = Nick -> PackedNick
    packNick (Nick -> PackedNick) -> (a -> Nick) -> a -> PackedNick
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Nick -> Nick
    lcNick (Nick -> Nick) -> (a -> Nick) -> a -> Nick
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Nick] -> Nick
    forall a. HasCallStack => [a] -> a
    head ([Nick] -> Nick) -> (a -> [Nick]) -> a -> Nick
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . a -> [Nick]
    forall a. Message a => a -> [Nick]
    G.channels (a -> PackedNick) -> a -> PackedNick
    forall a b. (a -> b) -> a -> b
    $! a
    msg
            nick :: PackedNick
    nick = Nick -> PackedNick
    packNick (Nick -> PackedNick) -> (a -> Nick) -> a -> PackedNick
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Nick -> Nick
    lcNick (Nick -> Nick) -> (a -> Nick) -> a -> Nick
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . a -> Nick
    forall a. Message a => a -> Nick
    G.nick (a -> PackedNick) -> a -> PackedNick
    forall a b. (a -> b) -> a -> b
    $ a
    msg

        (LBState Seen
     -> (LBState Seen
         -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ())
     -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ())
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    forall a.
    (LBState Seen
     -> (LBState Seen
         -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ())
     -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB a)
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB a
    forall (m :: * -> *) a.
    MonadLBState m =>
    (LBState m -> (LBState m -> m ()) -> m a) -> m a
    withMS ((LBState Seen
      -> (LBState Seen
          -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ())
      -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ())
     -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ())
    -> (LBState Seen
        -> (LBState Seen
            -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ())
        -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ())
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    forall a b. (a -> b) -> a -> b
    $ \(Map PackedNick Int
    maxUsers,Map PackedNick UserStatus
    state) LBState Seen
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    writer -> do
            ClockTime
    ct <- IO ClockTime
    -> ModuleT
         (Map PackedNick Int, Map PackedNick UserStatus) LB ClockTime
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io IO ClockTime
    getClockTime
            case a
    -> ClockTime
    -> PackedNick
    -> Map PackedNick UserStatus
    -> Either String (Map PackedNick UserStatus)
    f a
    msg ClockTime
    ct PackedNick
    nick Map PackedNick UserStatus
    state of
                Left String
    _         -> () -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    forall a.
    a -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()
                Right Map PackedNick UserStatus
    newstate -> do
                    let curUsers :: Int
    curUsers = [()] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length ([()] -> Int) -> [()] -> Int
    forall a b. (a -> b) -> a -> b
    $!
                            [ () | (PackedNick
    _,Present LastSpoke
    _ [PackedNick]
    chans) <- Map PackedNick UserStatus -> [(PackedNick, UserStatus)]
    forall k a. Map k a -> [(k, a)]
    M.toList Map PackedNick UserStatus
    state
                            , PackedNick
    chan PackedNick -> [PackedNick] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [PackedNick]
    chans ]

                        newMax :: Map PackedNick Int
    newMax
                            | String
    signal String -> [String] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [String
    "JOIN", String
    "353"]
                            = case PackedNick -> Map PackedNick Int -> Maybe Int
    forall k a. Ord k => k -> Map k a -> Maybe a
    M.lookup PackedNick
    chan Map PackedNick Int
    maxUsers of
                                Maybe Int
    Nothing -> PackedNick -> Int -> Map PackedNick Int -> Map PackedNick Int
    forall k a. Ord k => k -> a -> Map k a -> Map k a
    M.insert PackedNick
    chan Int
    curUsers Map PackedNick Int
    maxUsers
                                Just  Int
    n -> if Int
    n Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    < Int
    curUsers
                                    then PackedNick -> Int -> Map PackedNick Int -> Map PackedNick Int
    forall k a. Ord k => k -> a -> Map k a -> Map k a
    M.insert PackedNick
    chan Int
    curUsers Map PackedNick Int
    maxUsers
                                    else Map PackedNick Int
    maxUsers
                            | Bool
    otherwise -- ["PART", "QUIT", "NICK", "PRIVMSG"]
                            = Map PackedNick Int
    maxUsers

                    Map PackedNick Int
    newMax Map PackedNick Int
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    forall a b. a -> b -> b
    `seq` Map PackedNick UserStatus
    newstate Map PackedNick UserStatus
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    forall a b. a -> b -> b
    `seq` LBState Seen
    -> ModuleT (Map PackedNick Int, Map PackedNick UserStatus) LB ()
    writer (Map PackedNick Int
    newMax, Map PackedNick UserStatus
    newstate)