{- This file is part of irc-fun-client. - - Written in 2015, 2016 by fr33domlover . - - ♡ Copying is an act of love. Please copy, reuse and share. - - The author(s) have dedicated all copyright and related and neighboring - rights to this software to the public domain worldwide. This software is - distributed without any warranty. - - You should have received a copy of the CC0 Public Domain Dedication along - with this software. If not, see - . -} -- | Using the tools provided here, you can track which users are members of -- which channels. This functionality has a variety of uses, e.g. displaying -- user lists in client UI and logging/displaying quit messages in the correct -- channel logs/buffers. module Network.IRC.Fun.Client.NickTracker ( ChannelTracker (..) , NetworkTracker (..) , isMemberOf , isInChannel , presence , newChannel , newNetwork , addMember , addToChannel , changeNick , addChannel , removeMember , removeFromChannel , removeFromNetwork , removeChannel , removeChannels ) where import Network.IRC.Fun.Client.Instances import Network.IRC.Fun.Types import qualified Data.HashMap.Lazy as M import qualified Data.HashSet as S newtype ChannelTracker = ChannelTracker (S.HashSet Nickname) newtype NetworkTracker = NetworkTracker (M.HashMap Channel ChannelTracker) -- | Check whether a nickname is present in a channel. isMemberOf :: Nickname -> ChannelTracker -> Bool nick `isMemberOf` (ChannelTracker nicks) = nick `S.member` nicks -- | Check whether a nickname is present in a channel. isInChannel :: Nickname -> Channel -> NetworkTracker -> Bool isInChannel nick chan (NetworkTracker cts) = case M.lookup chan cts of Nothing -> False Just ct -> nick `isMemberOf` ct applySnd :: (b -> c) -> (a, b) -> (a, c) applySnd f (x, y) = (x, f y) -- | Check in which channels a nickname is present. presence :: Nickname -> NetworkTracker -> [Channel] presence nick (NetworkTracker cts) = [ chan | (chan, True) <- map (applySnd (nick `isMemberOf`)) $ M.toList cts] -- | Record a channel with the given present nicknames. newChannel :: [Nickname] -> ChannelTracker newChannel nicks = ChannelTracker $ S.fromList nicks -- | Create new tracker. newNetwork :: NetworkTracker newNetwork = NetworkTracker $ M.empty -- | Record a nickname being present in a channel. addMember :: Nickname -> ChannelTracker -> ChannelTracker addMember nick (ChannelTracker nicks) = ChannelTracker $ S.insert nick nicks -- | Record a nickname being present in a channel. addToChannel :: Channel -> Nickname -> NetworkTracker -> NetworkTracker addToChannel chan nick (NetworkTracker cts) = NetworkTracker $ f cts where f chans = case M.lookup chan chans of Nothing -> let ct = ChannelTracker $ S.singleton nick in M.insert chan ct chans Just (ChannelTracker nicks) -> let ct = ChannelTracker $ S.insert nick nicks in M.insert chan ct chans -- | Record a nickname change. Remove old nickname from the channels in which -- it's present, and add the new nickname to them. changeNick :: Nickname -> Nickname -> NetworkTracker -> NetworkTracker changeNick old new (NetworkTracker cts) = NetworkTracker $ f cts where f chans = let chansP = M.filter (old `isMemberOf`) chans chansR = M.map (removeMember old) chansP chansN = M.map (addMember new) chansR in chansN `M.union` chans -- | Record a channel with the given present nicknames. addChannel :: Channel -> [Nickname] -> NetworkTracker -> NetworkTracker addChannel chan nicks (NetworkTracker cts) = NetworkTracker $ f cts where f = M.insert chan $ newChannel nicks -- | Record a channel not having a given nickname anymore. removeMember :: Nickname -> ChannelTracker -> ChannelTracker removeMember nick (ChannelTracker nicks) = ChannelTracker $ S.delete nick nicks -- | Record a channel not having a given nickname anymore. removeFromChannel :: Channel -> Nickname -> NetworkTracker -> NetworkTracker removeFromChannel chan nick (NetworkTracker cts) = NetworkTracker $ f cts where f chans = M.adjust (removeMember nick) chan chans -- | Record a nickname not being present in any channel anymore. removeFromNetwork :: Nickname -> NetworkTracker -> NetworkTracker removeFromNetwork nick (NetworkTracker cts) = NetworkTracker $ f cts where f = M.map (removeMember nick) -- | Remove a channel from the tracker. removeChannel :: Channel -> NetworkTracker -> NetworkTracker removeChannel chan (NetworkTracker cts) = NetworkTracker $ f cts where f = M.delete chan -- | Remove channels from the tracker. removeChannels :: [Channel] -> NetworkTracker -> NetworkTracker removeChannels chans (NetworkTracker cts) = NetworkTracker $ f cts where f ts = ts `M.difference` M.fromList (zip chans (repeat ()))