api-monobank 0.1.0.1 → 0.1.1.0
raw patch · 3 files changed
+165/−76 lines, 3 filesdep +http-clientdep +http-client-tlsdep +servant
Dependencies added: http-client, http-client-tls, servant, servant-client
Files
- api-monobank.cabal +6/−1
- src/Monobank/Api.hs +99/−17
- src/Monobank/Types.hs +60/−58
api-monobank.cabal view
@@ -1,5 +1,5 @@ name: api-monobank-version: 0.1.0.1+version: 0.1.1.0 -- synopsis: description: Api client for popular Ukrainian bank - Monobank homepage: https://github.com/sigrlami/api-monobank#readme@@ -24,6 +24,10 @@ , time , http-conduit , bytestring+ , servant+ , servant-client+ , http-client+ , http-client-tls default-language: Haskell2010 executable mnb-app@@ -33,6 +37,7 @@ build-depends: base , bytestring , http-conduit+ , http-client , aeson , stm , text
src/Monobank/Api.hs view
@@ -1,33 +1,126 @@-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-} module Monobank.Api ( getCurrencies- , getPersonalInfo- , getPersonalStatement+ , getCurrencyRates'+ , getPersonalInfo'+ , getPersonalStatement' , getPersonalStatementFull ) where +import Control.Applicative+import Control.Monad+import Control.Monad.IO.Class import Data.Aeson import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy.Char8 as BL+import Data.Monoid+import Data.Proxy import qualified Data.Text as T+import GHC.Generics+import Network.HTTP.Client (Manager, newManager)+import qualified Network.HTTP.Client as C+import Network.HTTP.Client.TLS (tlsManagerSettings) import Network.HTTP.Conduit (simpleHttp)+import qualified Network.HTTP.Simple as S+import Servant.API+import Servant.Client import Monobank.Types import Monobank.Utils -------------------------------------------------------------------------------- -endpoint :: T.Text-endpoint = "https://api.monobank.ua"+endpoint = "api.monobank.ua" +type MonobankAPI =+ "bank"+ :> "currency"+ :> Get '[JSON] [CurrencyPair]++ -- GET /personal/client-info+ :<|> "personal"+ :> "client-info"+ :> Header "X-Token" T.Text+ :> Get '[JSON] User++ -- GET /personal/statement/{account}/{from}/{to}+ :<|> "personal"+ :> "statement"+ :> Header "X-Token" T.Text+ :> Capture "account" T.Text+ :> Capture "from" T.Text+ :> Capture "to" T.Text+ :> Get '[JSON] Statement++monobankAPI :: Proxy MonobankAPI+monobankAPI = Proxy++-- Derive call functions for the api+getCurrencyRates :: ClientM [CurrencyPair]+getPersonalInfo :: Maybe T.Text -> ClientM User+getPersonalStatement :: Maybe T.Text -> T.Text -> T.Text -> T.Text -> ClientM Statement+( getCurrencyRates+ :<|> getPersonalInfo+ :<|> getPersonalStatement ) = client monobankAPI++--------------------------------------------------------------------------------++-- | Get a basic list of monobank currency rates+-- |+getCurrencyRates' :: IO (Either ServantError [CurrencyPair])+getCurrencyRates' = do+ mgr <- newManager tlsManagerSettings+ let env = ClientEnv mgr host Nothing+ runClientM getCurrencyRates env+ where+ host = (BaseUrl Https endpoint 443 "")++-- | Ger bank user personal information+-- |+getPersonalInfo' :: Maybe T.Text -- ^ unique user token+ -> IO (Either ServantError User)+getPersonalInfo' tkn = do+ mgr <- newManager tlsManagerSettings+ let env = ClientEnv mgr host Nothing+ runClientM (getPersonalInfo tkn) env+ where+ host = (BaseUrl Https endpoint 443 "")+++-- | Receive an extract for the time from {to} to {to} time in seconds Unix time format.+-- | The maximum time for which it is possible to extract an extract is 31 days+-- | (2678400 seconds) Limit on the use of the function no more than 1 time in 60 seconds.+--+getPersonalStatement' :: Maybe T.Text+ -> T.Text+ -> T.Text+ -> T.Text+ -> IO (Either ServantError Statement)+getPersonalStatement' tkn accM fromM toM = do+ mgr <- newManager tlsManagerSettings+ let env = ClientEnv mgr host Nothing+ runClientM (getPersonalStatement tkn accM fromM toM) env+ where+ host = (BaseUrl Https endpoint 443 "")++--------------------------------------------------------------------------------+ -- Get a basic list of monobank currency rates. -- Information is cached and updated no more than once every 5 minutes. -- -- GET /bank/currency getCurrencies :: IO [CurrencyPair] getCurrencies = do- let path = T.concat [endpoint, "/bank/currency"]+ let path = T.concat [T.pack endpoint, "/bank/currency"] -- results <- fmap decodeEither $ simpleHttp (T.unpack path) resp <- simpleHttp (T.unpack path) --putStrLn $ show $ resp@@ -38,17 +131,6 @@ return $ [] Right val -> do return $ val---- GET /personal/client-info-getPersonalInfo = undefined----- Receive an extract for the time from {to} to {to} time in seconds Unix time format.--- The maximum time for which it is possible to extract an extract is 31 days--- (2678400 seconds) Limit on the use of the function no more than 1 time in 60 seconds.------ GET /personal/statement/{account}/{from}/{to}-getPersonalStatement = undefined -- Get Personal Statement from beginning
src/Monobank/Types.hs view
@@ -18,13 +18,54 @@ import Monobank.Utils ------------------------------------------------------------------+-------------------------------------------------------------------------------- -- | Wrapper for user token from Developer's Dashboard, https://api.monobank.ua/ type Token = T.Text ------------------------------------------------------------------+data Account =+ Account+ { acId :: T.Text -- ^+ , acBalance :: Int -- ^ Current balance+ , acCreditLimit :: Int -- ^ Current credit limit available to user+ , acCurrencyCode :: Int -- ^ Currency of the account+ , acCashbackType :: T.Text -- ^+ } deriving (Eq, Show, Generic) +instance FromJSON Account where+ parseJSON = genericParseJSON opts+ where+ opts = defaultOptions { fieldLabelModifier = uncapFst . drop 2 }++data User =+ User+ { uName :: T.Text -- ^ User's full name+ , uAccounts :: [Account] -- ^ List of user accounts, by currency type+ } deriving (Eq, Show)++instance FromJSON User where+ parseJSON =+ withObject "object" $ \o -> do+ nm <- o .: "name"+ ac <- o .: "accounts"++ return $ User nm ac++data Statement =+ Statement+ { stId :: String -- ^+ , stTime :: String -- ^+ , stDescription :: String -- ^+ , stMCC :: String -- ^+ , stHold :: Bool -- ^+ , stAmount :: Int -- ^+ , stOperationAmount :: Int -- ^+ , stCurrency :: Int -- ^+ , stComissionRate :: Int -- ^+ , stCashbackAmount :: Int -- ^+ , balance :: Int -- ^+ } deriving (Eq, Show, Generic, FromJSON)+ data Currency = Currency { isoCode :: String@@ -34,26 +75,6 @@ , name :: String } deriving (Eq, Show) -convertCurrencyFromCode :: Int -> Currency-convertCurrencyFromCode val =- case val of- 840 -> Currency { isoCode = "USD", isoNumericCode= 840, decimalDigits= 2, symbol= "$", name="Dollar"}- 978 -> Currency { isoCode = "EUR", isoNumericCode= 978, decimalDigits= 2, symbol= "€", name="Euro"}- 980 -> Currency { isoCode = "UAH", isoNumericCode= 980, decimalDigits= 2, symbol= "₴", name="Hrivnya"}- 643 -> Currency { isoCode = "RUB", isoNumericCode= 643, decimalDigits= 2, symbol= "", name="Ruble"}- 826 -> Currency { isoCode = "GBP", isoNumericCode= 826, decimalDigits= 2, symbol= "", name="Pound Sterling"}- 949 -> Currency { isoCode = "TRY", isoNumericCode= 949, decimalDigits= 2, symbol= "", name="Lira"}- 985 -> Currency { isoCode = "PLN", isoNumericCode= 985, decimalDigits= 2, symbol= "", name="Zloty"}- 348 -> Currency { isoCode = "HUF", isoNumericCode= 348, decimalDigits= 2, symbol= "", name="Forint"}- 208 -> Currency { isoCode = "DKK", isoNumericCode= 208, decimalDigits= 2, symbol= "", name="Danish Krone"}- 203 -> Currency { isoCode = "CZK", isoNumericCode= 203, decimalDigits= 2, symbol= "", name="Czech Koruna"}- 124 -> Currency { isoCode = "CAD", isoNumericCode= 124, decimalDigits= 2, symbol= "", name="Canadian Dollar"}- 933 -> Currency { isoCode = "BYN", isoNumericCode= 933, decimalDigits= 2, symbol= "", name="Belarussian Ruble"}- 756 -> Currency { isoCode = "CHF", isoNumericCode= 756, decimalDigits= 2, symbol= "", name="Swiss Franc"}- otherwise -> Currency { isoCode = "NA" , isoNumericCode= 0 , decimalDigits= 2, symbol= "", name="Uknown"}---------------------------------------------------------------------------------------------- -- | Data type that represents currency pair from Monobank -- at spicific time data CurrencyPair =@@ -95,41 +116,22 @@ putStrLn $ " - Sell: " ++ (show $ fromMaybe 0 $ cpRateSell cp) ++ (symbol $ cpCurrencyCodeB cp) putStrLn $ "" --- Simplified version for one-to-one conversion------ instance FromJSON CurrencyPair where--- parseJSON = genericParseJSON opts--- where--- opts = defaultOptions { fieldLabelModifier = uncapFst . drop 2}- -------------------------------------------------------------------------------- -data Account =- Account- { acId :: T.Text -- ^- , acBalance :: Int -- ^ Current balance- , acCreditLimit :: Int -- ^ Current credit limit available to user- , acCurrency :: Currency -- ^ Currency of the account- , acCashbackType :: T.Text -- ^- } deriving (Eq, Show)--data User =- User- { uName :: String -- ^ User's full name- , uAccounts :: [Account] -- ^ List of user accounts, by currency type- } deriving (Eq, Show)--data Statement =- Statement- { stId :: String -- ^- , stTime :: String -- ^- , stDescription :: String -- ^- , stMCC :: String -- ^- , stHold :: Bool -- ^- , stAmount :: Int -- ^- , stOperationAmount :: Int -- ^- , stCurrency :: Currency -- ^- , stComissionRate :: Int -- ^- , stCashbackAmount :: Int -- ^- , balance :: Int -- ^- } deriving (Eq, Show)+convertCurrencyFromCode :: Int -> Currency+convertCurrencyFromCode val =+ case val of+ 840 -> Currency { isoCode = "USD", isoNumericCode= 840, decimalDigits= 2, symbol= "$", name="Dollar"}+ 978 -> Currency { isoCode = "EUR", isoNumericCode= 978, decimalDigits= 2, symbol= "€", name="Euro"}+ 980 -> Currency { isoCode = "UAH", isoNumericCode= 980, decimalDigits= 2, symbol= "₴", name="Hrivnya"}+ 643 -> Currency { isoCode = "RUB", isoNumericCode= 643, decimalDigits= 2, symbol= "", name="Ruble"}+ 826 -> Currency { isoCode = "GBP", isoNumericCode= 826, decimalDigits= 2, symbol= "", name="Pound Sterling"}+ 949 -> Currency { isoCode = "TRY", isoNumericCode= 949, decimalDigits= 2, symbol= "", name="Lira"}+ 985 -> Currency { isoCode = "PLN", isoNumericCode= 985, decimalDigits= 2, symbol= "", name="Zloty"}+ 348 -> Currency { isoCode = "HUF", isoNumericCode= 348, decimalDigits= 2, symbol= "", name="Forint"}+ 208 -> Currency { isoCode = "DKK", isoNumericCode= 208, decimalDigits= 2, symbol= "", name="Danish Krone"}+ 203 -> Currency { isoCode = "CZK", isoNumericCode= 203, decimalDigits= 2, symbol= "", name="Czech Koruna"}+ 124 -> Currency { isoCode = "CAD", isoNumericCode= 124, decimalDigits= 2, symbol= "", name="Canadian Dollar"}+ 933 -> Currency { isoCode = "BYN", isoNumericCode= 933, decimalDigits= 2, symbol= "", name="Belarussian Ruble"}+ 756 -> Currency { isoCode = "CHF", isoNumericCode= 756, decimalDigits= 2, symbol= "", name="Swiss Franc"}+ otherwise -> Currency { isoCode = "NA" , isoNumericCode= 0 , decimalDigits= 2, symbol= "", name="Uknown"}