{-# language OverloadedStrings, DeriveGeneric #-}
module BinancePriceFetcher ( fetchADADetails
, fetchTicker
, receivers
) where
import Data.Aeson
import qualified Data.ByteString.Lazy as B
import GHC.Generics
import Network.HTTP.Conduit ( simpleHttp )
import qualified Data.Text as T ( pack
, unpack )
import Discord ( DiscordHandler )
import Discord.Types ( Message
, messageChannel )
import UnliftIO ( liftIO )
import Command
import Owoifier ( owoify )
receivers :: [Message -> DiscordHandler ()]
receivers :: [Message -> DiscordHandler ()]
receivers = (Command DiscordHandler -> Message -> DiscordHandler ())
-> [Command DiscordHandler] -> [Message -> DiscordHandler ()]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Command DiscordHandler -> Message -> DiscordHandler ()
forall (m :: * -> *).
MonadDiscord m =>
Command m -> Message -> m ()
runCommand
[ Command DiscordHandler
handleTicker
, Command DiscordHandler
handleAda24h
]
data Ticker = Ticker {
Ticker -> String
symbol :: String,
Ticker -> String
priceChange :: String,
Ticker -> String
priceChangePercent :: String,
Ticker -> String
weightedAvgPrice :: String,
Ticker -> String
prevClosePrice :: String,
Ticker -> String
lastPrice :: String,
Ticker -> String
lastQty :: String,
Ticker -> String
bidPrice :: String,
Ticker -> String
bidQty :: String,
Ticker -> String
askPrice :: String,
Ticker -> String
askQty :: String,
Ticker -> String
openPrice :: String,
Ticker -> String
highPrice :: String,
Ticker -> String
lowPrice :: String,
Ticker -> String
volume :: String,
Ticker -> String
quoteVolume :: String,
Ticker -> Integer
openTime :: Integer,
Ticker -> Integer
closeTime :: Integer,
Ticker -> Integer
firstId :: Integer,
Ticker -> Integer
lastId :: Integer,
Ticker -> Integer
count :: Integer
} deriving (Int -> Ticker -> ShowS
[Ticker] -> ShowS
Ticker -> String
(Int -> Ticker -> ShowS)
-> (Ticker -> String) -> ([Ticker] -> ShowS) -> Show Ticker
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Ticker] -> ShowS
$cshowList :: [Ticker] -> ShowS
show :: Ticker -> String
$cshow :: Ticker -> String
showsPrec :: Int -> Ticker -> ShowS
$cshowsPrec :: Int -> Ticker -> ShowS
Show, (forall x. Ticker -> Rep Ticker x)
-> (forall x. Rep Ticker x -> Ticker) -> Generic Ticker
forall x. Rep Ticker x -> Ticker
forall x. Ticker -> Rep Ticker x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep Ticker x -> Ticker
$cfrom :: forall x. Ticker -> Rep Ticker x
Generic)
instance FromJSON Ticker
instance ToJSON Ticker
adaEmoji :: String
adaEmoji :: String
adaEmoji = String
"<:ada:805934431071371305>"
jsonURL :: String -> String -> String
jsonURL :: String -> ShowS
jsonURL String
base String
quote = String
"https://api.binance.com/api/v3/ticker/24hr?symbol=" String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
base String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
quote
sign :: String -> String
sign :: ShowS
sign String
"BUSD" = String
"$"
sign String
"TUSD" = String
"$"
sign String
"USDT" = String
"$"
sign String
"AUD" = String
"$"
sign String
"CAD" = String
"$"
sign String
"EUR" = String
"€"
sign String
"GBP" = String
"£"
sign String
"JPY" = String
"¥"
sign String
"ADA" = String
"₳"
sign String
"BCH" = String
"Ƀ"
sign String
"BSV" = String
"Ɓ"
sign String
"BTC" = String
"₿"
sign String
"DAI" = String
"◈"
sign String
"DOGE" = String
"Ð"
sign String
"EOS" = String
"ε"
sign String
"ETC" = String
"ξ"
sign String
"ETH" = String
"Ξ"
sign String
"LTC" = String
"Ł"
sign String
"MKR" = String
"Μ"
sign String
"REP" = String
"Ɍ"
sign String
"STEEM" = String
"ȿ"
sign String
"XMR" = String
"ɱ"
sign String
"XRP" = String
"✕"
sign String
"XTZ" = String
"ꜩ"
sign String
"ZEC" = String
"ⓩ"
sign String
x = String
x
getJSON :: String -> String -> IO B.ByteString
getJSON :: String -> String -> IO ByteString
getJSON String
a String
b = String -> IO ByteString
forall (m :: * -> *). MonadIO m => String -> m ByteString
simpleHttp (String -> IO ByteString) -> String -> IO ByteString
forall a b. (a -> b) -> a -> b
$ String -> ShowS
jsonURL String
a String
b
fetchADADetails :: IO (Either String String)
fetchADADetails :: IO (Either String String)
fetchADADetails = do
Either String String
ticker <- String -> String -> IO (Either String String)
fetchTicker String
"ADA" String
"BUSD"
Either String String -> IO (Either String String)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either String String -> IO (Either String String))
-> Either String String -> IO (Either String String)
forall a b. (a -> b) -> a -> b
$ case Either String String
ticker of
Left String
err -> String -> Either String String
forall a b. a -> Either a b
Left String
err
Right String
str -> String -> Either String String
forall a b. b -> Either a b
Right (String -> Either String String) -> String -> Either String String
forall a b. (a -> b) -> a -> b
$ String
adaEmoji String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" (philcoin) is " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
str
fetchTicker :: String -> String -> IO (Either String String)
fetchTicker :: String -> String -> IO (Either String String)
fetchTicker String
base String
quote = do
Either String Ticker
detailsM <- (ByteString -> Either String Ticker
forall a. FromJSON a => ByteString -> Either String a
eitherDecode (ByteString -> Either String Ticker)
-> IO ByteString -> IO (Either String Ticker)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> String -> IO ByteString
getJSON String
base String
quote) :: IO (Either String Ticker)
Either String String -> IO (Either String String)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either String String -> IO (Either String String))
-> Either String String -> IO (Either String String)
forall a b. (a -> b) -> a -> b
$ case Either String Ticker
detailsM of
Left String
err -> String -> Either String String
forall a b. a -> Either a b
Left String
err
Right Ticker
details -> do
let percentChangeD :: Double
percentChangeD = String -> Double
forall a. Read a => String -> a
read (Ticker -> String
priceChangePercent Ticker
details) :: Double
curPriceD :: Double
curPriceD = String -> Double
forall a. Read a => String -> a
read (Ticker -> String
lastPrice Ticker
details) :: Double
lowPriceD :: Double
lowPriceD = String -> Double
forall a. Read a => String -> a
read (Ticker -> String
lowPrice Ticker
details) :: Double
highPriceD :: Double
highPriceD = String -> Double
forall a. Read a => String -> a
read (Ticker -> String
highPrice Ticker
details) :: Double
String -> Either String String
forall a b. b -> Either a b
Right (String -> Either String String) -> String -> Either String String
forall a b. (a -> b) -> a -> b
$ String -> String -> Double -> Double -> Double -> Double -> String
tickerAnnounce String
base String
quote Double
percentChangeD Double
curPriceD Double
lowPriceD Double
highPriceD
tickerAnnounce :: String -> String -> Double -> Double -> Double -> Double -> String
tickerAnnounce :: String -> String -> Double -> Double -> Double -> Double -> String
tickerAnnounce String
base String
quote Double
percentChange Double
curPrice Double
lowPrice Double
highPrice = [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [
String
"**", if Double
percentChange Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
0 then String
"down 💢" else String
"up 🚀🚀", String
"** "
, String
"**", Double -> String
forall a. Show a => a -> String
show (Double -> Double
forall a. Num a => a -> a
abs Double
percentChange), String
"%** in the past 24 hours, "
, String
"currently sitting at **", ShowS
sign String
base, String
"1** = **"
, ShowS
sign String
quote, Double -> String
forall a. Show a => a -> String
show Double
curPrice, String
"** per unit.\n"
, String
"Lowest price in the past 24h: **", ShowS
sign String
quote, Double -> String
forall a. Show a => a -> String
show Double
lowPrice, String
"**.\n"
, String
"Highest price in the past 24h: **", ShowS
sign String
quote, Double -> String
forall a. Show a => a -> String
show Double
highPrice, String
"**."
]
handleTicker :: Command DiscordHandler
handleTicker :: Command DiscordHandler
handleTicker = Text
-> (Message -> String -> String -> DiscordHandler ())
-> Command DiscordHandler
forall (m :: * -> *) h.
(CommandHandlerType m h, MonadDiscord m) =>
Text -> h -> Command m
command Text
"binance" ((Message -> String -> String -> DiscordHandler ())
-> Command DiscordHandler)
-> (Message -> String -> String -> DiscordHandler ())
-> Command DiscordHandler
forall a b. (a -> b) -> a -> b
$ \Message
m String
base String
quote -> do
Either String String
announcementM <- IO (Either String String)
-> ReaderT DiscordHandle IO (Either String String)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either String String)
-> ReaderT DiscordHandle IO (Either String String))
-> IO (Either String String)
-> ReaderT DiscordHandle IO (Either String String)
forall a b. (a -> b) -> a -> b
$ String -> String -> IO (Either String String)
fetchTicker String
base String
quote
case Either String String
announcementM of
Left String
err -> do
IO () -> DiscordHandler ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> DiscordHandler ()) -> IO () -> DiscordHandler ()
forall a b. (a -> b) -> a -> b
$ String -> IO ()
putStrLn (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"[DEBUG] Cannot get ticker from Binance: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
err
Message -> Text -> DiscordHandler ()
forall (m :: * -> *). MonadDiscord m => Message -> Text -> m ()
respond Message
m (Text -> DiscordHandler ()) -> Text -> DiscordHandler ()
forall a b. (a -> b) -> a -> b
$ Text -> Text
owoify Text
"Couldn't get the data! Sorry!"
Right String
announcement ->
Message -> Text -> DiscordHandler ()
forall (m :: * -> *). MonadDiscord m => Message -> Text -> m ()
respond Message
m (Text -> DiscordHandler ()) -> Text -> DiscordHandler ()
forall a b. (a -> b) -> a -> b
$ Text -> Text
owoify (Text -> Text) -> (String -> Text) -> String -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ String
base String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
"/" String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
quote String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
" is "
String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
announcement
handleAda24h :: Command DiscordHandler
handleAda24h :: Command DiscordHandler
handleAda24h =
Text -> Command DiscordHandler -> Command DiscordHandler
forall (m :: * -> *). Text -> Command m -> Command m
alias Text
"ada24h"
(Command DiscordHandler -> Command DiscordHandler)
-> ((Message -> DiscordHandler ()) -> Command DiscordHandler)
-> (Message -> DiscordHandler ())
-> Command DiscordHandler
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> (Message -> DiscordHandler ()) -> Command DiscordHandler
forall (m :: * -> *) h.
(CommandHandlerType m h, MonadDiscord m) =>
Text -> h -> Command m
command Text
"ada" ((Message -> DiscordHandler ()) -> Command DiscordHandler)
-> (Message -> DiscordHandler ()) -> Command DiscordHandler
forall a b. (a -> b) -> a -> b
$ \Message
m -> do
Either String String
adaAnnouncementM <- IO (Either String String)
-> ReaderT DiscordHandle IO (Either String String)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO (Either String String)
fetchADADetails
case Either String String
adaAnnouncementM of
Left String
err -> do
IO () -> DiscordHandler ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> DiscordHandler ()) -> IO () -> DiscordHandler ()
forall a b. (a -> b) -> a -> b
$ String -> IO ()
putStrLn (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"[DEBUG] Cannot fetch ADA details from Binance: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
err
Message -> Text -> DiscordHandler ()
forall (m :: * -> *). MonadDiscord m => Message -> Text -> m ()
respond Message
m (Text -> DiscordHandler ()) -> Text -> DiscordHandler ()
forall a b. (a -> b) -> a -> b
$ Text -> Text
owoify Text
"Couldn't get the data! Sorry!"
Right String
announcement ->
Message -> Text -> DiscordHandler ()
forall (m :: * -> *). MonadDiscord m => Message -> Text -> m ()
respond Message
m (Text -> DiscordHandler ()) -> Text -> DiscordHandler ()
forall a b. (a -> b) -> a -> b
$ Text -> Text
owoify (Text -> Text) -> Text -> Text
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack String
announcement