packages feed

matsuri-0.0.2: Jabber.hs

-- | Jabber.hs
-- A module for interaction with XMPP.

module Jabber where

import Buffers
import Config
import Utils

import Network
import Network.XMPP
import Network.XMPP.Presence
import Network.XMPP.Roster
import Network.XMPP.MUC
import Control.Concurrent
import Control.Concurrent.MVar
import Data.Maybe
import Data.List


connect :: MVar MEvent -> Config -> Account -> IO Buffer
connect mev config acc =
    case connection acc of
      NoConnection -> do
        forkIO $ do
            c <- openStream (server acc)
            getStreamStart c
            runXMPP c $ do
              err <- startAuth (username acc)
                               (server acc)
                               (password acc)
                               (resource acc)
              if err == 0
                then do
                  tId <- liftIO $ myThreadId
                  put $ insConn (OK c tId)

                  items <- getRoster
                  mapM_ (put . insChat c) items
                  -- create groups; if roster item doesn't have any
                  -- groups then it's element of "other" group
                  mapM_ (put . insGroup items) (groups items)
                  put $ insOtherGroup items

                  sendPresence
                  -- set handlers
                  handleVersion (getCF "client" config)
                                (getCF "version" config)
                                (getCF "OS" config)
                  addHandler isChat (chatCB mev k) True
                  addHandler isPresence (presenceCB mev k) True
                  ---
                else put $ insConn NoConnection
        return $ doBuf Trying
      _ -> return (BufAccount acc)
  where
    put = liftIO . putMVar mev
    k = accName acc
    insConn = InsBuffer k . doBuf
    doBuf conn = BufAccount acc{connection=conn}

    insChat c item = InsBuffer (chatName' item) (item2chat c item)
    item2chat c item = BufChat (Chat item offline (chatName' item) c [])
    offline = Status StatusOffline []
    chatName' item = k++"|"++itemJid item

    insGroup items name = insGroup' (name `elem`) items name
    insOtherGroup items = insGroup' null items "other"
    groups = nub . concat . map itemGroups
    insGroup' p items name = InsBuffer (k++"|"++name) (grp p name items)
    grp p name = BufGroup . Group name False . grpItems' p name
    grpItems' p name
      = map chatName' . filter (p . itemGroups)


-- | Close account connection.
disconnect :: Account -> IO Buffer
disconnect account =
    case connection account of
      OK c tId -> do
        killThread tId
        closeConnection c
        return (BufAccount account{connection=NoConnection})
      _ -> return (BufAccount account)


-- | Status changes.
presenceCB :: MVar MEvent -> String -> StanzaHandler
presenceCB mev acc stanza = do
    let (jid, res) = getJidRes stanza
        presence = doPresence stanza
    case presence of
      Available status' -> putStatus jid status'
      Unavailable status' -> putStatus jid status'
      _ -> return ()
  where
    putStatus jid = liftIO . putMVar mev . NewStatus (acc++"|"++jid)


-- | Send chat message.
sendChatMessage :: Chat -> String -> IO Buffer
sendChatMessage chat msg = do
    runXMPP (chatConn chat) $ sendMessage (itemJid $ item chat) msg
    time <- nowTime
    let msg' = MyMsg $ time++" => "++msg
    return $ BufChat chat{chatContents=msg':(chatContents chat)}


-- | Callback for chat messages.
chatCB :: MVar MEvent -> String -> StanzaHandler
chatCB mev acc stanza = do
    let stamp = getMessageStamp stanza
        isHistory = isJust stamp
    time <- liftIO $ if isHistory
                     then utcToZoned $ maybe "" id stamp
                     else nowTime

    let (jid, res) = getJidRes stanza
        body = maybe "" id (getMessageBody stanza)
        msg = time++" <= "++body
        msg' = if isHistory then HistoryMsg msg else Msg msg

    liftIO $ putMVar mev $ NewMsg (acc++"|"++jid) msg'