{-# LANGUAGE OverloadedStrings #-} module CoinbasePro.WebSocketFeed ( subscribeToFeed ) where import Control.Concurrent (forkIO) import Control.Exception (Exception, throwIO) import Control.Monad (forever) import Data.Aeson (decode', encode) import qualified Network.WebSockets as WS import qualified System.IO.Streams as Streams import System.IO.Streams.Concurrent.Unagi (makeChanPipe) import qualified Wuss as WU import CoinbasePro.Authenticated.Request (CoinbaseProCredentials (..)) import CoinbasePro.Environment (Environment, WSConnection (..), wsEndpoint) import CoinbasePro.Types (ProductId) import CoinbasePro.WebSocketFeed.Channel (ChannelMessage (..)) import CoinbasePro.WebSocketFeed.Request (ChannelName (..), RequestMessageType (..), WebSocketFeedRequest (..), authenticatedWebSocketFeedRequest) data ParseException = ParseException deriving Int -> ParseException -> ShowS [ParseException] -> ShowS ParseException -> String forall a. (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a showList :: [ParseException] -> ShowS $cshowList :: [ParseException] -> ShowS show :: ParseException -> String $cshow :: ParseException -> String showsPrec :: Int -> ParseException -> ShowS $cshowsPrec :: Int -> ParseException -> ShowS Show instance Exception ParseException subscribeToFeed :: [ProductId] -> [ChannelName] -> Environment -> Maybe CoinbaseProCredentials -> IO (Streams.InputStream ChannelMessage) subscribeToFeed :: [ProductId] -> [ChannelName] -> Environment -> Maybe CoinbaseProCredentials -> IO (InputStream ChannelMessage) subscribeToFeed [ProductId] prds [ChannelName] channels Environment env = WSConnection -> [ProductId] -> [ChannelName] -> Maybe CoinbaseProCredentials -> IO (InputStream ChannelMessage) subscribe (Environment -> WSConnection wsEndpoint Environment env) [ProductId] prds [ChannelName] channels subscribe :: WSConnection -> [ProductId] -> [ChannelName] -> Maybe CoinbaseProCredentials -> IO (Streams.InputStream ChannelMessage) subscribe :: WSConnection -> [ProductId] -> [ChannelName] -> Maybe CoinbaseProCredentials -> IO (InputStream ChannelMessage) subscribe WSConnection wsConn [ProductId] prids [ChannelName] channels Maybe CoinbaseProCredentials cpc = do (InputStream ChannelMessage is, OutputStream ChannelMessage os) <- forall a. IO (InputStream a, OutputStream a) makeChanPipe ByteString req <- Maybe CoinbaseProCredentials -> IO ByteString mkWsRequest Maybe CoinbaseProCredentials cpc ThreadId _ <- IO () -> IO ThreadId forkIO forall b c a. (b -> c) -> (a -> b) -> a -> c . forall a. String -> PortNumber -> String -> ClientApp a -> IO a WU.runSecureClient String wsHost PortNumber wsPort String "/" forall a b. (a -> b) -> a -> b $ \Connection conn -> do forall a. WebSocketsData a => Connection -> a -> IO () WS.sendTextData Connection conn ByteString req forall (f :: * -> *) a b. Applicative f => f a -> f b forever forall a b. (a -> b) -> a -> b $ Connection -> IO ChannelMessage parseFeed Connection conn forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b >>= forall a. OutputStream a -> Maybe a -> IO () Streams.writeTo OutputStream ChannelMessage os forall b c a. (b -> c) -> (a -> b) -> a -> c . forall a. a -> Maybe a Just forall (m :: * -> *) a. Monad m => a -> m a return InputStream ChannelMessage is where wsHost :: String wsHost = WSConnection -> String host WSConnection wsConn wsPort :: PortNumber wsPort = WSConnection -> PortNumber port WSConnection wsConn mkWsRequest :: Maybe CoinbaseProCredentials -> IO ByteString mkWsRequest = forall b a. b -> (a -> b) -> Maybe a -> b maybe (forall (m :: * -> *) a. Monad m => a -> m a return forall a b. (a -> b) -> a -> b $ forall a. ToJSON a => a -> ByteString encode WebSocketFeedRequest wsRequest) (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b fmap forall a. ToJSON a => a -> ByteString encode forall b c a. (b -> c) -> (a -> b) -> a -> c . CoinbaseProCredentials -> IO AuthenticatedWebSocketFeedRequest authWsRequest) wsRequest :: WebSocketFeedRequest wsRequest = RequestMessageType -> [ProductId] -> [ChannelName] -> WebSocketFeedRequest WebSocketFeedRequest RequestMessageType Subscribe [ProductId] prids [ChannelName] channels authWsRequest :: CoinbaseProCredentials -> IO AuthenticatedWebSocketFeedRequest authWsRequest = WebSocketFeedRequest -> CoinbaseProCredentials -> IO AuthenticatedWebSocketFeedRequest authenticatedWebSocketFeedRequest WebSocketFeedRequest wsRequest parseFeed :: WS.Connection -> IO ChannelMessage parseFeed :: Connection -> IO ChannelMessage parseFeed Connection conn = forall a. WebSocketsData a => Connection -> IO a WS.receiveData Connection conn forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b >>= forall b a. b -> (a -> b) -> Maybe a -> b maybe forall {a}. IO a err forall (m :: * -> *) a. Monad m => a -> m a return forall b c a. (b -> c) -> (a -> b) -> a -> c . forall a. FromJSON a => ByteString -> Maybe a decode' where err :: IO a err = forall e a. Exception e => e -> IO a throwIO ParseException ParseException