packages feed

calamity-0.8.0.0: Calamity/Types/Model/User.hs

{-# LANGUAGE TemplateHaskell #-}

-- | A User
module Calamity.Types.Model.User (
  User (..),
  UserBanner (..),
  Partial (PartialUser),
  StatusType (..),
) where

import Calamity.Internal.IntColour
import Calamity.Types.CDNAsset (CDNAsset (..))
import Calamity.Types.Model.Avatar
import {-# SOURCE #-} Calamity.Types.Model.Guild.Member
import Calamity.Types.Partial
import Calamity.Types.Snowflake
import Calamity.Utils.CDNUrl (assetHashFile, cdnURL)
import Data.Aeson ((.:), (.:?))
import Data.Aeson qualified as Aeson
import Data.Colour (Colour)
import Data.Text (Text)
import Data.Text qualified as T
import Data.Text.Read (decimal)
import Data.Word
import Network.HTTP.Req ((/:), (/~))
import Optics.TH
import TextShow qualified
import TextShow.TH

data UserBanner = UserBanner
  { userID :: Snowflake User
  , hash :: T.Text
  }
  deriving (Show, Eq)

instance CDNAsset UserBanner where
  assetURL UserBanner {hash, userID} =
    cdnURL /: "banners" /~ userID /: assetHashFile hash

data User = User
  { id :: Snowflake User
  , username :: Text
  , discriminator :: Text
  , bot :: Maybe Bool
  , avatar :: Avatar
  , mfaEnabled :: Maybe Bool
  , banner :: Maybe UserBanner
  , accentColour :: Maybe (Colour Double)
  , locale :: Maybe Text
  , verified :: Maybe Bool
  , email :: Maybe Text
  , flags :: Maybe Word64
  , premiumType :: Maybe Word64
  }
  deriving (Show, Eq)
  deriving (TextShow.TextShow) via TextShow.FromStringShow User
  deriving (HasID User) via HasIDField "id" User
  deriving (HasID Member) via HasIDFieldCoerce' "id" User

instance Aeson.FromJSON User where
  parseJSON = Aeson.withObject "User" $ \v -> do
    uid <- v .: "id"
    avatarHash <- v .:? "avatar"
    discrim <- v .: "discriminator"
    discrim' <- case decimal discrim of
      Right (n, _) -> pure n
      Left e -> fail e
    let avatar = Avatar avatarHash uid discrim'
    banner <- (UserBanner uid <$>) <$> v .:? "banner"
    User
      <$> pure uid
      <*> v .: "username"
      <*> v .: "discriminator"
      <*> v .:? "bot"
      <*> pure avatar
      <*> v .:? "mfa_enabled"
      <*> pure banner
      <*> (fmap fromIntColour <$> v .:? "accent_color")
      <*> v .:? "locale"
      <*> v .:? "verified"
      <*> v .:? "email"
      <*> v .:? "flags"
      <*> v .:? "premium_type"

newtype instance Partial User = PartialUser
  { id :: Snowflake User
  }
  deriving stock (Show, Eq)
  deriving (HasID User) via HasIDField "id" (Partial User)

instance Aeson.FromJSON (Partial User) where
  parseJSON = Aeson.withObject "Partial User" $ \v ->
    PartialUser <$> v .: "id"

data StatusType
  = Idle
  | DND
  | Online
  | Offline
  | Invisible
  deriving (Eq, Show, Enum)

instance Aeson.FromJSON StatusType where
  parseJSON = Aeson.withText "StatusType" $ \case
    "idle" -> pure Idle
    "dnd" -> pure DND
    "online" -> pure Online
    "offline" -> pure Offline
    "invisible" -> pure Invisible
    _ -> fail "Unknown status type"

instance Aeson.ToJSON StatusType where
  toJSON =
    Aeson.toJSON @Text . \case
      Idle -> "idle"
      DND -> "dnd"
      Online -> "online"
      Offline -> "offline"
      Invisible -> "invisible"

$(deriveTextShow 'PartialUser)
$(deriveTextShow ''StatusType)
$(makeFieldLabelsNoPrefix ''User)
$(makeFieldLabelsNoPrefix 'PartialUser)
$(makeFieldLabelsNoPrefix ''StatusType)