calamity-0.12.1.0: Calamity/HTTP/Internal/Types.hs
{-# LANGUAGE TemplateHaskell #-}
-- | Types for the http lib
module Calamity.HTTP.Internal.Types (
RestError (..),
RateLimitState (..),
DiscordResponseType (..),
Bucket (..),
BucketState (..),
GatewayResponse,
BotGatewayResponse,
) where
import Calamity.HTTP.Internal.Route
import Control.Concurrent.Event (Event)
import Control.Concurrent.STM.TVar (TVar)
import Data.Aeson
import Data.Aeson qualified as Aeson
import Data.ByteString qualified as B
import Data.ByteString.Lazy qualified as LB
import Data.Text as T
import Data.Time
import Optics.TH
import StmContainers.Map qualified as SC
data RestError
= -- | An error response from discord
HTTPError
{ status :: Int
, response :: Maybe Value
}
| -- | Something failed while making the request (after retrying a few times)
InternalClientError T.Text
deriving (Show)
data BucketState = BucketState
{ resetTime :: Maybe UTCTime
-- ^ The time when the bucket resets, used to heuristically wait out ratelimits
, resetKey :: Int
-- ^ The X-Ratelimit-Reset value discord gave us
, remaining :: Int
-- ^ The number of uses left in the bucket, used to heuristically wait out ratelimits
, limit :: Int
-- ^ The total number of uses for this bucket
, ongoing :: Int
-- ^ How many ongoing requests
}
deriving (Show)
newtype Bucket = Bucket
{ state :: TVar BucketState
}
data RateLimitState = RateLimitState
{ bucketKeys :: SC.Map RouteKey B.ByteString
, buckets :: SC.Map B.ByteString Bucket
, globalLock :: Event
}
data DiscordResponseType
= -- | A good response
Good
LB.ByteString
-- | The ratelimit headers if we got them
(Maybe (BucketState, B.ByteString))
| -- | We hit a 429, no response and ratelimited
Ratelimited
-- | Retry after
UTCTime
-- | Global ratelimit
Bool
(Maybe (BucketState, B.ByteString))
| -- | Discord's error, we should retry (HTTP 5XX)
ServerError Int
| -- | Our error, we should fail
ClientError Int LB.ByteString
| -- | Something went wrong with the http client
InternalResponseError T.Text
newtype GatewayResponse = GatewayResponse
{ url :: T.Text
}
deriving stock (Show)
instance Aeson.FromJSON GatewayResponse where
parseJSON = Aeson.withObject "GatewayResponse" $ \v ->
GatewayResponse <$> v .: "url"
data BotGatewayResponse = BotGatewayResponse
{ url :: T.Text
, shards :: Int
}
deriving (Show)
instance Aeson.FromJSON BotGatewayResponse where
parseJSON = Aeson.withObject "BotGatewayResponse" $ \v ->
BotGatewayResponse
<$> v .: "url"
<*> v .: "shards"
$(makeFieldLabelsNoPrefix ''Bucket)
$(makeFieldLabelsNoPrefix ''BucketState)
$(makeFieldLabelsNoPrefix ''RateLimitState)
$(makeFieldLabelsNoPrefix ''GatewayResponse)
$(makeFieldLabelsNoPrefix ''BotGatewayResponse)