packages feed

danibot-0.2.0.0: lib/Network/Danibot/Slack/RTM.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}

module Network.Danibot.Slack.RTM (
          fromWSSURI
        , loopRTM
    ) where

import qualified Data.Monoid.Cancellative as Textual
import qualified Data.Monoid.Textual as Textual
import Data.Text (Text)
import Data.ByteString (ByteString)
import qualified Data.ByteString 
import qualified Data.ByteString.Lazy.Char8 as Bytes.Lazy.Char8
import Data.Aeson (eitherDecode',encode)
import Control.Monad
import Control.Monad.IO.Class
import Control.Exception
import Control.Concurrent.Conceit
import Control.Foldl (impurely,FoldM)
import Streaming (Stream)
import Streaming.Prelude (Of)
import qualified Streaming as Streaming
import qualified Streaming.Prelude as Streaming
import qualified Wuss
import qualified Network.WebSockets as Webs

import Network.Danibot.Slack.Types(Wire(..),Event(..),OutboundMessage(..))

data WSSEndpoint = WSSEndpoint
    {
      host :: Text
    , path :: Text 
    } deriving (Show)

fromWSSURI :: Text -> Either String WSSEndpoint
fromWSSURI uri = 
    case Textual.stripPrefix "wss://" uri of
        Nothing   -> Left "malformed wss uri"
        Just uri' -> case Textual.break_ True (=='/') uri' of
            (host,web) -> Right (WSSEndpoint host web)

{-| Connect to the Slack RTM websocket.		

-}
ws :: FoldM IO Event () -- ^ Fold for processing incoming messages.
   -> Stream (Of OutboundMessage) IO () -- ^ Stream that produces outbound messages.
   -> Webs.ClientApp ()
ws eventFold messageStream connection = 
    let conceited =
            (_Conceit (impurely Streaming.foldM_ eventFold eventStream))
            *> 
            (_Conceit (Streaming.mapM_ sendMessage messageStream))
        eventStream = forever (do
            message <- liftIO (Webs.receiveData connection)
            case eitherDecode' message of
                Left errmsg -> 
                    liftIO (throwIO (userError ("Malformed msg: " ++ errmsg)))
                Right (Wire event) -> do
                    liftIO (Bytes.Lazy.Char8.putStrLn (encode event))
                    Streaming.yield event)
        sendMessage = 
            Webs.sendTextData connection . encode . Wire
    in _runConceit conceited


loopRTM :: FoldM IO Event ()
        -> Stream (Of OutboundMessage) IO ()
        -> WSSEndpoint 
        -> IO ()
loopRTM eventHandler messageEmitter (WSSEndpoint host path) = do  
    Wuss.runSecureClient (Textual.fromText host) 
                         443
                         (Textual.fromText path) 
                         (ws eventHandler messageEmitter)