pusher-haskell-0.1.0.0: src/Network/Pusher/Event.hs
-----------------------------------------------------------------------------
-- |
-- Module : Network.Pusher.Event
-- Copyright : See LICENSE file
-- License : BSD
--
-- Maintainer : Sid Raval <sidsraval@gmail.com>
-- Stability : experimental
-- Portability : non-portable (not tested)
--
-- The 'Network.Pusher.Event' module provides an simple interface for interacting
-- with Pusher.com's @event@ endpoints. This is used for trigger an event on one
-- or more channels with arbitrary data.
-----------------------------------------------------------------------------
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleInstances #-}
module Network.Pusher.Event (triggerEvent, triggerMultiChannelEvent) where
import Network.HTTP
import Control.Applicative
import Control.Monad
import Control.Monad.Reader
import Data.Aeson.Encode (encode)
import Data.Digest.Pure.SHA
import qualified Data.ByteString.Lazy.Char8 as B
import Data.Hash.MD5
import Network.Pusher.Base
type Environment = (Pusher, String, Event)
-- | @triggerEvent (pusher, channel, event)@ sends an event to one
-- channel for the given 'Pusher' instance. The result is the response body
-- from the Pusher server.
triggerEvent :: (Pusher, Channel, Event) -> IO String
triggerEvent (p, c, e) = runReaderT event (p, requestBody c e, e)
-- | @triggerMultiChannelEvent (pusher, channels, event)@ sends an event to multiple
-- channels for the given 'Pusher' instance. The result is the response body
-- from the Pusher server.
triggerMultiChannelEvent :: (Pusher, String, Event) -> IO String
triggerMultiChannelEvent (p, cs, e) = runReaderT event (p, requestMultiChannelBody cs e, e)
event :: ReaderT Environment IO String
event = do
(p, b, e) <- ask
url <- generateUrl
response <- liftIO . simpleHTTP $ postRequestWithBody url contentType b
liftIO $ getResponseBody response
generateUrl :: ReaderT Environment IO String
generateUrl = do
(p, b, e) <- ask
let md5body = md5s . Str $ b
let timestamp = authTimestamp
withoutSignature <- urlWithoutSignature md5body timestamp
(++) (withoutSignature ++ "&auth_signature=")
<$> signedAuthString timestamp md5body
urlWithoutSignature :: Md5Body -> Timestamp -> ReaderT Environment IO String
urlWithoutSignature b t = do
(p@(Pusher _ k _), _, _) <- ask
liftIO $ ((++) (baseUrl p
++ "/events?body_md5="
++ b
++ "&auth_version=1.0&auth_key="
++ k
++ "&auth_timestamp=")) <$> t
signedAuthString :: Timestamp -> Md5Body -> ReaderT Environment IO String
signedAuthString t b = do
(p@(Pusher _ _ appSecret), _, _) <- ask
signatureString <- unsignedAuthString t b >>= return . B.pack
return . showDigest $ hmacSha256 (B.pack appSecret) signatureString
unsignedAuthString :: Timestamp -> Md5Body -> ReaderT Environment IO String
unsignedAuthString t b = do
(p@(Pusher appId appKey _), _, _) <- ask
liftIO $ idKeyAndTimestamp appId appKey
<$> t
>>= (\u -> return $ u ++ "&auth_version=1.0&body_md5=" ++ b)
requestBody c e = "{\"name\": \""
++ (eventName e)
++ "\", \"channel\": \""
++ c
++ "\", \"data\":"
++ (B.unpack . encode . eventData $ e)
++ "}"
requestMultiChannelBody cs e = "{\"name\": \""
++ (eventName e)
++ "\", \"channels\": "
++ show cs
++ ", \"data\":"
++ (B.unpack . encode . eventData $ e)
++ "}"
-- Helper for unsignedAuthString
idKeyAndTimestamp :: String -> String -> String -> String
idKeyAndTimestamp i k t = "POST\n/apps/"
++ i
++ "/events\nauth_key="
++ k
++ "&auth_timestamp="
++ t