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 = return (M.empty,M.empty)
, moduleCmds = return
[ (command "users")
{ help = say "users [chan]. Report the maximum number of users seen in a channel, and active users in the last 30 minutes"
, process = doUsers
}
, (command "seen")
{ help = say "seen <user>. Report if a user has been seen by the bot"
, process = doSeen
}
]
, moduleInit = do
sequence_
[ registerCallback signal (withSeenFM signal cb)
| (signal, cb) <- zip
["JOIN", "PART", "QUIT", "NICK", "353", "PRIVMSG"]
[joinCB, partCB, quitCB, nickCB, joinChanCB, msgCB]
]
c <- lb $ findLBFileForReading "seen"
s <- maybe (return (P.pack "")) (io . P.readFile) c
let ls = PackedNick -> ByteString
L.fromStrict PackedNick
s
mbDecoded <- io . try . evaluate $ decode ls
case mbDecoded of
Left exc :: SomeException
exc@SomeException{} -> do
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 = do
chans <- lift $ ircGetChannels
unless (null chans) $ do
ct <- io getClockTime
modifyMS $ \(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)
withMS $ \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
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
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
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"
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
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 ])
(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
"."
prettyMissed StopWatch
_ p
_ p
_ifNotMissed = String
"."
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
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
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
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
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"
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
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))
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'
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"
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
= 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)