packages feed

hxmppc-0.2: EasyXMPP.hs

{-# LANGUAGE OverloadedStrings #-}
module EasyXMPP (
  runPersistentClient,
  mkMsg,
  extractMsg,

  -- more details
  (<>),
  pBody,
  contentText,
  extractBody,
  extractContent,
  p
  ) where

import Network.Protocol.XMPP hiding (Node)
import Data.Text (Text)
import qualified Data.Text as T
import Data.XML.Types (Element(..), Node(..), Content(..), Name(..))
import Network (PortID(..), HostName)
import Data.Maybe (catMaybes)
import Control.Monad
import Control.Monad.IO.Class
import Control.Concurrent
import Data.Functor
import Data.Monoid

(<>) :: Monoid m => m -> m -> m
(<>) = mappend

pBody :: [Node] -> Element
pBody = Element (Name "body" (Just "jabber:client") Nothing) []

contentText :: Text -> Node
contentText = NodeContent . ContentText

extractBody :: [Element] -> [Node]
extractBody [Element (Name "body" _ _) _ ns] = ns
extractBody _ = []

extractContent :: Node -> Maybe Text
extractContent (NodeContent (ContentText txt)) = Just txt
extractContent _ = Nothing

p :: Text -> Element
p = pBody . return . contentText

mkMsg :: JID -> Text -> Message
mkMsg toJID txt =
  Message { messageType     = MessageNormal
          , messageTo       = Just toJID
          , messageFrom     = Nothing -- done by the server
          , messageID       = Nothing
          , messageLang     = Just "en"
          , messagePayloads = [p txt]
          }

extractMsg :: Message -> Text
extractMsg = T.concat . catMaybes . map extractContent . extractBody . messagePayloads

-- Send a "ping" occasionally, to prevent server timeouts from
-- closing the connection.
sendPings :: Integer -> Session -> IO ()
sendPings seconds s = forever send where
  send = do
    -- Ignore errors
    _ <- runXMPP s $ putStanza ping
    threadDelay $ fromInteger $ 1000000 * seconds
  ping = (emptyIQ IQGet)
           { iqPayload = Just (Element pingName [] []) }

pingName :: Name
pingName = Name "ping" (Just "urn:xmpp:ping") Nothing

runPersistentClient :: (JID, Text) -> (HostName, PortID) -> XMPP a -> IO (Either Error a)
runPersistentClient (myJID, pass) (hostname, port) act =
  runClient server myJID username pass $ do
    -- Some servers will close the XMPP connection after some period
    -- of inactivity. For this example, we'll simply send a "ping" every
    -- 60 seconds
    _ <- getSession >>= liftIO . forkIO . sendPings 60

    _ <- bindJID myJID
    act

  where
    server = Server (JID Nothing (jidDomain myJID) Nothing) hostname port
    username = case strNode <$> jidNode myJID of
      Just x -> x
      Nothing -> error $ "JID must include a username"