{-# 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)