2 changed files with 105 additions and 0 deletions
@ -0,0 +1,104 @@
@@ -0,0 +1,104 @@
|
||||
{-# LANGUAGE RankNTypes #-} |
||||
module Network.Xmpp.IM.PresenceTracker where |
||||
|
||||
import Control.Applicative |
||||
import Control.Concurrent.STM |
||||
import Control.Monad (guard) |
||||
import Data.Foldable |
||||
import Data.Map.Strict (Map) |
||||
import qualified Data.Map.Strict as Map |
||||
import Data.Maybe |
||||
import Data.Traversable |
||||
import Lens.Family2 |
||||
import Lens.Family2.Stock |
||||
import Network.Xmpp.Concurrent.Types |
||||
import Network.Xmpp.IM.Presence |
||||
import Network.Xmpp.IM.Roster |
||||
import Network.Xmpp.Lens hiding (Lens, Traversal) |
||||
import Network.Xmpp.Types |
||||
import Prelude hiding (mapM) |
||||
|
||||
-- Map from bare JIDs to a map of full JIDs to show maybe status. |
||||
-- |
||||
-- Invariants: |
||||
-- * The outer map should not have entries for bare JIDs that have no |
||||
-- available resource, i.e. the inner map should never be empty |
||||
-- |
||||
-- * The inner map keys' local and domain part coincide with the outer keys' |
||||
newtype Peers = Peers { unPeers :: Map Jid (Map Jid (Maybe IMPresence))} |
||||
deriving (Show) |
||||
|
||||
_peers :: Iso Peers (Map Jid (Map Jid (Maybe IMPresence))) |
||||
_peers = mkIso unPeers Peers |
||||
|
||||
data PeerStatus = PeerAvailable (Maybe IMPresence) |
||||
| PeerUnavailable |
||||
deriving (Show) |
||||
|
||||
_PeerAvailable :: Prism PeerStatus (Maybe IMPresence) |
||||
_PeerAvailable = prism' PeerAvailable fromPeerAvailable |
||||
where |
||||
fromPeerAvailable (PeerAvailable pa) = Just pa |
||||
fromPeerAvailable _ = Nothing |
||||
|
||||
_PeerUnavailable :: Prism PeerStatus () |
||||
_PeerUnavailable = prism' (const PeerUnavailable) fromPeerUnavailable |
||||
where |
||||
fromPeerUnavailable PeerUnavailable = Just () |
||||
fromPeerUnavailable _ = Nothing |
||||
|
||||
_PeerStatus :: Iso (Maybe (Maybe IMPresence)) PeerStatus |
||||
_PeerStatus = mkIso toPeerStatus fromPeerStatus |
||||
where |
||||
toPeerStatus (Nothing) = PeerUnavailable |
||||
toPeerStatus (Just imp) = PeerAvailable imp |
||||
fromPeerStatus PeerUnavailable = Nothing |
||||
fromPeerStatus (PeerAvailable imp) = Just imp |
||||
|
||||
maybeMap :: Iso (Maybe (Map a b)) (Map a b) |
||||
maybeMap = mkIso maybeToMap mapToMaybe |
||||
where |
||||
maybeToMap Nothing = Map.empty |
||||
maybeToMap (Just m) = m |
||||
mapToMaybe m | Map.null m = Nothing |
||||
| otherwise = Just m |
||||
|
||||
peerStatusL :: Jid -> Lens' Peers PeerStatus |
||||
peerStatusL j = _peers . at (toBare j) . maybeMap . at j . _PeerStatus |
||||
|
||||
peerMapPeerAvailable :: Jid -> Peers -> Bool |
||||
peerMapPeerAvailable j = not . nullOf (peerStatusL j . _PeerAvailable) |
||||
|
||||
statusTracker :: (Stanza -> IO (Either XmppFailure ())) |
||||
-> IO (Plugin', TVar Peers) |
||||
statusTracker out = do |
||||
sessRef <- newTVarIO Nothing |
||||
peerMap <- newTVarIO (Peers Map.empty) |
||||
|
||||
return (Plugin' { inHandler = handleIn sessRef peerMap |
||||
, outHandler = out |
||||
, onSessionUp = atomically . writeTVar sessRef . Just |
||||
}, peerMap) |
||||
where |
||||
handleIn sessRef peers st _ = do |
||||
mbRoster <- atomically (mapM getRoster' =<< readTVar sessRef) |
||||
let mbPr = do |
||||
pr <- st ^? _Presence -- Only act on presence stanzas |
||||
fr <- pr ^? from . _Just . _isFull -- Only act on full JIDs |
||||
roster <- mbRoster |
||||
-- Check that the from address is in our roster. This means that |
||||
-- deactivating the roster effectively turns off this plugin |
||||
guard $ Map.member (toBare fr) (roster ^. itemsL) |
||||
return (pr, fr) |
||||
forM_ mbPr $ \(pr, fr) -> |
||||
case presenceType pr of |
||||
Available -> atomically . modifyTVar peers |
||||
$ set (peerStatusL fr) |
||||
(PeerAvailable (getIMPresence pr)) |
||||
Unavailable -> atomically . modifyTVar peers |
||||
$ set (peerStatusL fr) PeerUnavailable |
||||
_ -> return () |
||||
return [(st, [])] |
||||
|
||||
isPeerAvailable :: Jid -> TVar Peers -> STM Bool |
||||
isPeerAvailable j peerRef = peerMapPeerAvailable j <$> readTVar peerRef |
||||
Loading…
Reference in new issue