module Network.Discord.Rest.HTTP
( JsonRequest(..)
, R.ReqBodyJson(..)
, R.NoReqBody(..)
, baseUrl
, fetch
, makeRequest
, (//)
, (R./:)
) where
import Data.Semigroup ((<>))
import Control.Monad.State (get, when)
import Data.Aeson
import Data.ByteString.Char8 (pack, ByteString)
import Data.Maybe (fromMaybe)
import qualified Data.Text as T (pack)
import qualified Network.HTTP.Req as R
import Data.Version (showVersion)
import Network.Discord.Rest.Prelude
import Network.Discord.Types (DiscordM, getClient, DiscordState(..), getAuth)
import Paths_discord_hs (version)
baseUrl :: R.Url 'R.Https
baseUrl = R.https "discordapp.com" R./: "api" R./: apiVersion
where apiVersion = "v6"
baseRequestOptions :: DiscordM Option
baseRequestOptions = do
DiscordState {getClient=client} <- get
return $ R.header "Authorization" (pack . show $ getAuth client)
<> R.header "User-Agent" (pack $ "DiscordBot (https://github.com/jano017/Discord.hs,"
++ showVersion version ++ ")")
infixl 5 //
(//) :: Show a => R.Url scheme -> a -> R.Url scheme
url // part = url R./: (T.pack $ show part)
type Option = R.Option 'R.Https
data JsonRequest r where
Delete :: FromJSON r => R.Url 'R.Https -> Option -> JsonRequest r
Get :: FromJSON r => R.Url 'R.Https -> Option -> JsonRequest r
Patch :: (FromJSON r, R.HttpBody a) => R.Url 'R.Https -> a -> Option -> JsonRequest r
Post :: (FromJSON r, R.HttpBody a) => R.Url 'R.Https -> a -> Option -> JsonRequest r
Put :: (FromJSON r, R.HttpBody a) => R.Url 'R.Https -> a -> Option -> JsonRequest r
fetch :: FromJSON r => JsonRequest r -> DiscordM (R.JsonResponse r)
fetch (Delete url opts) = R.req R.DELETE url R.NoReqBody R.jsonResponse =<< (<> opts) <$> baseRequestOptions
fetch (Get url opts) = R.req R.GET url R.NoReqBody R.jsonResponse =<< (<> opts) <$> baseRequestOptions
fetch (Patch url body opts) = R.req R.PATCH url body R.jsonResponse =<< (<> opts) <$> baseRequestOptions
fetch (Post url body opts) = R.req R.POST url body R.jsonResponse =<< (<> opts) <$> baseRequestOptions
fetch (Put url body opts) = R.req R.PUT url body R.jsonResponse =<< (<> opts) <$> baseRequestOptions
makeRequest :: (RateLimit a, FromJSON r) => a -> JsonRequest r -> DiscordM r
makeRequest req action = do
waitRateLimit req
resp <- fetch action
when (parseHeader resp "X-RateLimit-Remaining" 1 < 1) $
setRateLimit req $ parseHeader resp "X-RateLimit-Reset" 0
return $ R.responseBody resp
where
parseHeader :: R.HttpResponse resp => resp -> ByteString -> Int -> Int
parseHeader resp header def = fromMaybe def $ decodeStrict =<< R.responseHeader resp header
instance (FromJSON r) => DoFetch (JsonRequest r) where
doFetch req = SyncFetched . R.responseBody <$> fetch req