{-# OPTIONS_GHC -fplugin=Polysemy.Plugin #-} -- | Interaction related utilities module Calamity.Interactions.Utils ( respond, respondEphemeral, followUp, followUpEphemeral, edit, defer, deferEphemeral, deferComponent, pushModal, userLocalState, ) where import Calamity.HTTP import Calamity.Interactions.Eff (InteractionEff, getApplicationID, getInteractionID, getInteractionToken, getInteractionUser) import Calamity.Metrics.Eff (MetricEff) import Calamity.Types.LogEff (LogEff) import Calamity.Types.Model.Channel.Component (Component (ActionRow')) import Calamity.Types.Model.User (User) import Calamity.Types.Snowflake (Snowflake) import Calamity.Types.Tellable import Data.HashMap.Strict qualified as H import Data.Text (Text) import Optics import Polysemy qualified as P import Polysemy.State qualified as P import System.Random (getStdRandom, uniform) {- | Provide local state semantics to a running view, the state is keyed to the user invoking the interaction -} userLocalState :: forall r s a. (P.Member InteractionEff r) => -- | Initial state s -> P.Sem (P.State s ': r) a -> P.Sem r a userLocalState s = P.evalState H.empty . P.reinterpret @(P.State s) @(P.State (H.HashMap (Snowflake User) s)) ( \case P.Get -> do uid <- getInteractionUser P.gets (H.lookupDefault s uid) P.Put s -> do uid <- getInteractionUser P.modify' (H.insert uid s) ) -- | Respond to an interaction with a globally visible message respond :: forall t r. (P.Members '[InteractionEff, RatelimitEff, TokenEff, LogEff, MetricEff, P.Embed IO] r, ToMessage t) => t -> P.Sem r (Either RestError ()) respond (runToMessage -> msg) = let opts = InteractionCallbackMessageOptions { tts = msg ^. #tts , content = msg ^. #content , embeds = msg ^. #embeds , allowedMentions = msg ^. #allowedMentions , ephemeral = Just False , suppressEmbeds = Just False , components = msg ^. #components , attachments = msg ^. #attachments } in do interactionID <- getInteractionID interactionToken <- getInteractionToken invoke $ CreateResponseMessage interactionID interactionToken opts -- | Respond to an interaction with an ephemeral message respondEphemeral :: forall t r. (P.Members '[InteractionEff, RatelimitEff, TokenEff, LogEff, MetricEff, P.Embed IO] r, ToMessage t) => t -> P.Sem r (Either RestError ()) respondEphemeral (runToMessage -> msg) = let opts = InteractionCallbackMessageOptions { tts = msg ^. #tts , content = msg ^. #content , embeds = msg ^. #embeds , allowedMentions = msg ^. #allowedMentions , ephemeral = Just True , suppressEmbeds = Just False , components = msg ^. #components , attachments = msg ^. #attachments } in do interactionID <- getInteractionID interactionToken <- getInteractionToken invoke $ CreateResponseMessage interactionID interactionToken opts -- | Respond to an interaction by editing the message that triggered the interaction edit :: forall t r. (P.Members '[InteractionEff, RatelimitEff, TokenEff, LogEff, MetricEff, P.Embed IO] r, ToMessage t) => t -> P.Sem r (Either RestError ()) edit (runToMessage -> msg) = let opts = InteractionCallbackMessageOptions { tts = msg ^. #tts , content = msg ^. #content , embeds = msg ^. #embeds , allowedMentions = msg ^. #allowedMentions , ephemeral = Nothing , suppressEmbeds = Nothing , components = msg ^. #components , attachments = msg ^. #attachments } in do interactionID <- getInteractionID interactionToken <- getInteractionToken invoke $ CreateResponseUpdate interactionID interactionToken opts -- | Create a follow up response to an interaction followUp :: forall t r. (P.Members '[InteractionEff, RatelimitEff, TokenEff, LogEff, MetricEff, P.Embed IO] r, ToMessage t) => t -> P.Sem r (Either RestError ()) followUp (runToMessage -> msg) = let opts = InteractionCallbackMessageOptions { tts = msg ^. #tts , content = msg ^. #content , embeds = msg ^. #embeds , allowedMentions = msg ^. #allowedMentions , ephemeral = Nothing , suppressEmbeds = Nothing , components = msg ^. #components , attachments = msg ^. #attachments } in do applicationID <- getApplicationID interactionToken <- getInteractionToken invoke $ CreateFollowupMessage applicationID interactionToken opts -- | Create an ephemeral follow up response to an interaction followUpEphemeral :: forall t r. (P.Members '[InteractionEff, RatelimitEff, TokenEff, LogEff, MetricEff, P.Embed IO] r, ToMessage t) => t -> P.Sem r (Either RestError ()) followUpEphemeral (runToMessage -> msg) = let opts = InteractionCallbackMessageOptions { tts = msg ^. #tts , content = msg ^. #content , embeds = msg ^. #embeds , allowedMentions = msg ^. #allowedMentions , ephemeral = Just True , suppressEmbeds = Nothing , components = msg ^. #components , attachments = msg ^. #attachments } in do applicationID <- getApplicationID interactionToken <- getInteractionToken invoke $ CreateFollowupMessage applicationID interactionToken opts -- | Defer an interaction and show a loading state, use @followUp@ later on defer :: (P.Members '[InteractionEff, RatelimitEff, TokenEff, LogEff, MetricEff, P.Embed IO] r) => P.Sem r (Either RestError ()) defer = do interactionID <- getInteractionID interactionToken <- getInteractionToken invoke $ CreateResponseDefer interactionID interactionToken False {- | Defer an interaction and show an ephemeral loading state, use @followUp@ later on -} deferEphemeral :: (P.Members '[InteractionEff, RatelimitEff, TokenEff, LogEff, MetricEff, P.Embed IO] r) => P.Sem r (Either RestError ()) deferEphemeral = do interactionID <- getInteractionID interactionToken <- getInteractionToken invoke $ CreateResponseDefer interactionID interactionToken True -- | Defer operation deferComponent :: (P.Members '[InteractionEff, RatelimitEff, TokenEff, LogEff, MetricEff, P.Embed IO] r) => P.Sem r (Either RestError ()) deferComponent = do interactionID <- getInteractionID interactionToken <- getInteractionToken invoke $ CreateResponseDeferComponent interactionID interactionToken fixupActionRow :: Component -> Component fixupActionRow r@(ActionRow' _) = r fixupActionRow x = ActionRow' [x] {- | Push a modal as a response to an interaction You should probably use this with 'Calamity.Interaction.View.runView' -} pushModal :: (P.Members '[InteractionEff, RatelimitEff, TokenEff, LogEff, MetricEff, P.Embed IO] r) => Text -> [Component] -> P.Sem r (Either RestError ()) pushModal title c = do -- we don't actually use the custom id of the modal. the custom ids of the -- sub-components are enough to disambiguate cid <- P.embed $ getStdRandom uniform interactionID <- getInteractionID interactionToken <- getInteractionToken invoke . CreateResponseModal interactionID interactionToken $ InteractionCallbackModal cid title (map fixupActionRow c)