packages feed

calamity-0.8.0.0: Calamity/Types/Model/Channel/Embed.hs

{-# LANGUAGE TemplateHaskell #-}

-- | Message embeds
module Calamity.Types.Model.Channel.Embed (
  Embed (..),
  embedFooter,
  embedImage,
  embedThumbnail,
  embedAuthor,
  embedAuthor',
  embedField,
  EmbedFooter (..),
  EmbedImage (..),
  EmbedThumbnail (..),
  EmbedVideo (..),
  EmbedProvider (..),
  EmbedAuthor (..),
  EmbedField (..),
) where

import Calamity.Internal.IntColour (IntColour (..))
import Calamity.Internal.Utils (CalamityToJSON (..), CalamityToJSON' (..), (.=), (.?=))
import Data.Aeson ((.!=), (.:), (.:?))
import Data.Aeson qualified as Aeson
import Data.Colour (Colour)
import Data.Default.Class
import Data.Semigroup
import Data.Text (Text)
import Data.Time
import Data.Word
import Optics ((%~), (&), (^.))
import Optics.TH
import TextShow qualified
import TextShow.TH

data Embed = Embed
  { title :: Maybe Text
  , type_ :: Maybe Text
  , description :: Maybe Text
  , url :: Maybe Text
  , timestamp :: Maybe UTCTime
  , color :: Maybe (Colour Double)
  , footer :: Maybe EmbedFooter
  , image :: Maybe EmbedImage
  , thumbnail :: Maybe EmbedThumbnail
  , video :: Maybe EmbedVideo
  , provider :: Maybe EmbedProvider
  , author :: Maybe EmbedAuthor
  , fields :: [EmbedField]
  }
  deriving (Eq, Show)
  deriving (TextShow.TextShow) via TextShow.FromStringShow Embed
  deriving (Aeson.ToJSON) via CalamityToJSON Embed

instance Default Embed where
  def =
    Embed
      Nothing
      Nothing
      Nothing
      Nothing
      Nothing
      Nothing
      Nothing
      Nothing
      Nothing
      Nothing
      Nothing
      Nothing
      []

instance CalamityToJSON' Embed where
  toPairs Embed {..} =
    [ "title" .?= title
    , "type" .?= type_
    , "description" .?= description
    , "url" .?= url
    , "timestamp" .?= timestamp
    , "color" .?= (IntColour <$> color)
    , "footer" .?= footer
    , "image" .?= image
    , "thumbnail" .?= thumbnail
    , "video" .?= video
    , "provider" .?= provider
    , "author" .?= author
    , "fields" .= fields
    ]

instance Aeson.FromJSON Embed where
  parseJSON = Aeson.withObject "Embed" $ \v ->
    Embed
      <$> v .:? "title"
      <*> v .:? "type"
      <*> v .:? "description"
      <*> v .:? "url"
      <*> v .:? "timestamp"
      <*> (fmap fromIntColour <$> v .:? "color")
      <*> v .:? "footer"
      <*> v .:? "image"
      <*> v .:? "thumbnail"
      <*> v .:? "video"
      <*> v .:? "provider"
      <*> v .:? "author"
      <*> v .:? "fields" .!= []

data EmbedFooter = EmbedFooter
  { text :: Text
  , iconUrl :: Maybe Text
  , proxyIconUrl :: Maybe Text
  }
  deriving (Eq, Show)
  deriving (Aeson.ToJSON) via CalamityToJSON EmbedFooter

instance CalamityToJSON' EmbedFooter where
  toPairs EmbedFooter {..} =
    [ "text" .= text
    , "icon_url" .?= iconUrl
    , "proxy_icon_url" .?= proxyIconUrl
    ]

instance Aeson.FromJSON EmbedFooter where
  parseJSON = Aeson.withObject "EmbedFooter" $ \v ->
    EmbedFooter
      <$> v .: "text"
      <*> v .:? "icon_url"
      <*> v .:? "proxy_icon_url"

{- | Create an embed footer with a provided content

 The remaining fields are set to Nothing
-}
embedFooter :: Text -> EmbedFooter
embedFooter text = EmbedFooter text Nothing Nothing

data EmbedImage = EmbedImage
  { url :: Text
  , proxyUrl :: Maybe Text
  , width :: Maybe Word64
  , height :: Maybe Word64
  }
  deriving (Eq, Show)
  deriving (Aeson.ToJSON) via CalamityToJSON EmbedImage

instance CalamityToJSON' EmbedImage where
  toPairs EmbedImage {..} =
    [ "url" .= url
    , "proxy_url" .= proxyUrl
    , "width" .= width
    , "height" .= height
    ]

instance Aeson.FromJSON EmbedImage where
  parseJSON = Aeson.withObject "EmbedImage" $ \v ->
    EmbedImage
      <$> v .: "url"
      <*> v .:? "proxy_url"
      <*> v .:? "width"
      <*> v .:? "height"

{- | Create an embed image with a provided url

 The remaining fields are set to Nothing
-}
embedImage :: Text -> EmbedImage
embedImage url = EmbedImage url Nothing Nothing Nothing

data EmbedThumbnail = EmbedThumbnail
  { url :: Text
  , proxyUrl :: Maybe Text
  , width :: Maybe Word64
  , height :: Maybe Word64
  }
  deriving (Eq, Show)
  deriving (Aeson.ToJSON) via CalamityToJSON EmbedThumbnail

instance CalamityToJSON' EmbedThumbnail where
  toPairs EmbedThumbnail {..} =
    [ "url" .= url
    , "proxy_url" .?= proxyUrl
    , "width" .?= width
    , "height" .?= height
    ]

instance Aeson.FromJSON EmbedThumbnail where
  parseJSON = Aeson.withObject "EmbedThumbnail" $ \v ->
    EmbedThumbnail
      <$> v .: "url"
      <*> v .:? "proxy_url"
      <*> v .:? "width"
      <*> v .:? "height"

{- | Create an embed thumbnail with a provided url

 The remaining fields are set to Nothing
-}
embedThumbnail :: Text -> EmbedThumbnail
embedThumbnail url = EmbedThumbnail url Nothing Nothing Nothing

data EmbedVideo = EmbedVideo
  { url :: Maybe Text
  , proxyUrl :: Maybe Text
  , width :: Maybe Word64
  , height :: Maybe Word64
  }
  deriving (Eq, Show)
  deriving (Aeson.ToJSON) via CalamityToJSON EmbedVideo

instance CalamityToJSON' EmbedVideo where
  toPairs EmbedVideo {..} =
    [ "url" .= url
    , "proxy_url" .= proxyUrl
    , "width" .= width
    , "height" .= height
    ]

instance Aeson.FromJSON EmbedVideo where
  parseJSON = Aeson.withObject "EmbedVideo" $ \v ->
    EmbedVideo
      <$> v .: "url"
      <*> v .:? "proxy_url"
      <*> v .:? "width"
      <*> v .:? "height"

data EmbedProvider = EmbedProvider
  { name :: Maybe Text
  , url :: Maybe Text
  }
  deriving (Eq, Show)
  deriving (Aeson.ToJSON) via CalamityToJSON EmbedProvider

instance CalamityToJSON' EmbedProvider where
  toPairs EmbedProvider {..} =
    [ "name" .= name
    , "url" .= url
    ]

instance Aeson.FromJSON EmbedProvider where
  parseJSON = Aeson.withObject "EmbedProvider" $ \v ->
    EmbedProvider
      <$> v .:? "name"
      <*> v .:? "url"

data EmbedAuthor = EmbedAuthor
  { name :: Maybe Text
  , url :: Maybe Text
  , iconUrl :: Maybe Text
  , proxyIconUrl :: Maybe Text
  }
  deriving (Eq, Show)
  deriving (Aeson.ToJSON) via CalamityToJSON EmbedAuthor

instance Default EmbedAuthor where
  def = EmbedAuthor Nothing Nothing Nothing Nothing

instance CalamityToJSON' EmbedAuthor where
  toPairs EmbedAuthor {..} =
    [ "name" .?= name
    , "url" .?= url
    , "icon_url" .?= iconUrl
    , "proxy_icon_url" .?= proxyIconUrl
    ]

instance Aeson.FromJSON EmbedAuthor where
  parseJSON = Aeson.withObject "EmbedAuthor" $ \v ->
    EmbedAuthor
      <$> v .:? "name"
      <*> v .:? "url"
      <*> v .:? "icon_url"
      <*> v .:? "proxy_icon_url"

{- | Create an embed author with the given name

 The remaining fields are set to Nothing
-}
embedAuthor :: Text -> EmbedAuthor
embedAuthor name = EmbedAuthor (Just name) Nothing Nothing Nothing

{- | Create an embed author with the given name, url, and icon url

 The remaining fields are set to Nothing
-}
embedAuthor' ::
  -- | Name
  Text ->
  -- | Url
  Text ->
  -- | Icon url
  Text ->
  EmbedAuthor
embedAuthor' name url iconUrl = EmbedAuthor (Just name) (Just url) (Just iconUrl) Nothing

data EmbedField = EmbedField
  { name :: Text
  , value :: Text
  , inline :: Bool
  }
  deriving (Eq, Show)
  deriving (Aeson.ToJSON) via CalamityToJSON EmbedField

instance CalamityToJSON' EmbedField where
  toPairs EmbedField {..} =
    [ "name" .= name
    , "value" .= value
    , "inline" .= inline
    ]

instance Aeson.FromJSON EmbedField where
  parseJSON = Aeson.withObject "EmbedField" $ \v ->
    EmbedField
      <$> v .: "name"
      <*> v .: "value"
      <*> v .:? "inline" .!= False

-- | Create a non-inline embed field
embedField ::
  -- | Name
  Text ->
  -- | Value
  Text ->
  EmbedField
embedField name value = EmbedField name value False

$(makeFieldLabelsNoPrefix ''Embed)

$(deriveTextShow ''EmbedFooter)
$(makeFieldLabelsNoPrefix ''EmbedFooter)

$(deriveTextShow ''EmbedImage)
$(makeFieldLabelsNoPrefix ''EmbedImage)

$(deriveTextShow ''EmbedThumbnail)
$(makeFieldLabelsNoPrefix ''EmbedThumbnail)

$(deriveTextShow ''EmbedVideo)
$(makeFieldLabelsNoPrefix ''EmbedVideo)

$(deriveTextShow ''EmbedProvider)
$(makeFieldLabelsNoPrefix ''EmbedProvider)

$(deriveTextShow ''EmbedAuthor)
$(makeFieldLabelsNoPrefix ''EmbedAuthor)

$(deriveTextShow ''EmbedField)
$(makeFieldLabelsNoPrefix ''EmbedField)

instance Semigroup EmbedFooter where
  l <> r =
    l
      & #text %~ (<> (r ^. #text))
      & #iconUrl %~ getLast . (<> Last (r ^. #iconUrl)) . Last
      & #proxyIconUrl %~ getLast . (<> Last (r ^. #proxyIconUrl)) . Last

instance Semigroup Embed where
  l <> r =
    l
      & #title %~ (<> (r ^. #title))
      & #type_ %~ getLast . (<> Last (r ^. #type_)) . Last
      & #description %~ (<> (r ^. #description))
      & #url %~ getLast . (<> Last (r ^. #url)) . Last
      & #timestamp %~ getLast . (<> Last (r ^. #timestamp)) . Last
      & #color %~ getLast . (<> Last (r ^. #color)) . Last
      & #footer %~ (<> (r ^. #footer))
      & #image %~ getLast . (<> Last (r ^. #image)) . Last
      & #thumbnail %~ getLast . (<> Last (r ^. #thumbnail)) . Last
      & #video %~ getLast . (<> Last (r ^. #video)) . Last
      & #provider %~ getLast . (<> Last (r ^. #provider)) . Last
      & #author %~ getLast . (<> Last (r ^. #author)) . Last
      & #fields %~ (<> (r ^. #fields))

instance Monoid Embed where
  mempty = def