From 5c8040b80cbf86d9d31d6962790574cd70877d7d Mon Sep 17 00:00:00 2001 From: Philipp Balzarek Date: Wed, 17 Sep 2014 15:52:36 +0200 Subject: [PATCH] add onPresenceChange method onPresenceChange is called when the presence status of a peer changes, i.e. it goes online or offline or the IM presence is changed --- source/Network/Xmpp/Concurrent.hs | 2 +- source/Network/Xmpp/Concurrent/Types.hs | 2 ++ source/Network/Xmpp/IM/Presence.hs | 4 +-- source/Network/Xmpp/IM/PresenceTracker.hs | 30 +++++++++++++------ .../Network/Xmpp/IM/PresenceTracker/Types.hs | 2 +- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/source/Network/Xmpp/Concurrent.hs b/source/Network/Xmpp/Concurrent.hs index 52855fb..ccfa84a 100644 --- a/source/Network/Xmpp/Concurrent.hs +++ b/source/Network/Xmpp/Concurrent.hs @@ -175,7 +175,7 @@ newSession stream config realm mbSasl = runErrorT $ do then [handleRoster boundJid ros out] else [] let presenceH = if (enablePresenceTracking config) - then [handlePresence peers out] + then [handlePresence (onPresenceChange config) peers out] else [] (sStanza, ps) <- initPlugins out $ plugins config let stanzaHandler = runHandlers $ List.concat diff --git a/source/Network/Xmpp/Concurrent/Types.hs b/source/Network/Xmpp/Concurrent/Types.hs index 91460ff..4085150 100644 --- a/source/Network/Xmpp/Concurrent/Types.hs +++ b/source/Network/Xmpp/Concurrent/Types.hs @@ -87,6 +87,7 @@ data SessionConfiguration = SessionConfiguration , enableRoster :: Bool -- | Track incomming presence stancas. , enablePresenceTracking :: Bool + , onPresenceChange :: Maybe (PeerStatus -> PeerStatus -> IO ()) } instance Default SessionConfiguration where @@ -101,6 +102,7 @@ instance Default SessionConfiguration where , plugins = [] , enableRoster = True , enablePresenceTracking = True + , onPresenceChange = Nothing } -- | Handlers to be run when the Xmpp session ends and when the Xmpp connection is diff --git a/source/Network/Xmpp/IM/Presence.hs b/source/Network/Xmpp/IM/Presence.hs index 42adfeb..6e13135 100644 --- a/source/Network/Xmpp/IM/Presence.hs +++ b/source/Network/Xmpp/IM/Presence.hs @@ -13,12 +13,12 @@ import Network.Xmpp.Types data ShowStatus = StatusAway | StatusChat | StatusDnd - | StatusXa deriving (Read, Show) + | StatusXa deriving (Read, Show, Eq) data IMPresence = IMP { showStatus :: Maybe ShowStatus , status :: Maybe Text , priority :: Maybe Int - } deriving Show + } deriving (Show, Eq) imPresence :: IMPresence imPresence = IMP { showStatus = Nothing diff --git a/source/Network/Xmpp/IM/PresenceTracker.hs b/source/Network/Xmpp/IM/PresenceTracker.hs index 82c331d..c4ff0ad 100644 --- a/source/Network/Xmpp/IM/PresenceTracker.hs +++ b/source/Network/Xmpp/IM/PresenceTracker.hs @@ -2,8 +2,10 @@ module Network.Xmpp.IM.PresenceTracker where import Control.Applicative +import Control.Concurrent import Control.Concurrent.STM -import Data.Foldable +import Control.Monad +import qualified Data.Foldable as Foldable import Data.Map.Strict (Map) import qualified Data.Map.Strict as Map import Data.Maybe @@ -57,21 +59,31 @@ peerMapPeerAvailable :: Jid -> Peers -> Bool peerMapPeerAvailable j | isFull j = not . nullOf (peerStatusL j . _PeerAvailable) | otherwise = not . nullOf (_peers . at j . _Just) -handlePresence :: TVar Peers -> StanzaHandler -handlePresence peers _ st _ = do +handlePresence :: Maybe (PeerStatus -> PeerStatus -> IO ()) + -> TVar Peers + -> StanzaHandler +handlePresence onChange peers _ st _ = do let mbPr = do pr <- st ^? _Presence -- Only act on presence stanzas fr <- pr ^? from . _Just . _isFull -- Only act on full JIDs return (pr, fr) - forM_ mbPr $ \(pr, fr) -> + Foldable.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 + Available -> setStatus fr (PeerAvailable (getIMPresence pr)) + Unavailable -> setStatus fr PeerUnavailable _ -> return () return [(st, [])] + where + setStatus fr newStatus = do + os <- atomically $ do + ps <- readTVar peers + let oldStatus = ps ^. peerStatusL fr + writeTVar peers $ ps & set (peerStatusL fr) newStatus + return oldStatus + unless (os == newStatus) $ case onChange of + Nothing -> return () + Just oc -> void . forkIO $ oc os newStatus + return () -- | Check whether a given jid is available isPeerAvailable :: Jid -> Session -> STM Bool diff --git a/source/Network/Xmpp/IM/PresenceTracker/Types.hs b/source/Network/Xmpp/IM/PresenceTracker/Types.hs index 334c066..5e02800 100644 --- a/source/Network/Xmpp/IM/PresenceTracker/Types.hs +++ b/source/Network/Xmpp/IM/PresenceTracker/Types.hs @@ -17,4 +17,4 @@ newtype Peers = Peers { unPeers :: Map Jid (Map Jid (Maybe IMPresence))} data PeerStatus = PeerAvailable (Maybe IMPresence) | PeerUnavailable - deriving (Show) + deriving (Show, Eq)