{-# LANGUAGE TemplateHaskell #-}
module Calamity.Commands.Utils (
addCommands,
useConstantPrefix,
CmdInvokeFailReason (..),
CtxCommandError (..),
CommandNotFound (..),
CommandInvoked (..),
) where
import Calamity.Client.Client
import Calamity.Client.Types
import Calamity.Commands.Dsl
import Calamity.Commands.Types
import Calamity.Metrics.Eff
import Calamity.Types.Model.Channel
import Calamity.Types.Model.Guild.Member (Member)
import Calamity.Types.Model.User (User)
import CalamityCommands.CommandUtils
import qualified CalamityCommands.Context as CC
import qualified CalamityCommands.Error as CC
import qualified CalamityCommands.ParsePrefix as CC
import qualified CalamityCommands.Utils as CC
import Control.Monad
import qualified Data.Text as T
import Data.Typeable
import qualified Polysemy as P
import Optics.TH (makeFieldLabelsNoPrefix)
data CmdInvokeFailReason c
= NoContext
| NotFound [T.Text]
| CommandInvokeError c CC.CommandError
data CtxCommandError c = CtxCommandError
{ forall c. CtxCommandError c -> c
ctx :: c
, forall c. CtxCommandError c -> CommandError
err :: CC.CommandError
}
deriving (Int -> CtxCommandError c -> ShowS
forall c. Show c => Int -> CtxCommandError c -> ShowS
forall c. Show c => [CtxCommandError c] -> ShowS
forall c. Show c => CtxCommandError c -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CtxCommandError c] -> ShowS
$cshowList :: forall c. Show c => [CtxCommandError c] -> ShowS
show :: CtxCommandError c -> String
$cshow :: forall c. Show c => CtxCommandError c -> String
showsPrec :: Int -> CtxCommandError c -> ShowS
$cshowsPrec :: forall c. Show c => Int -> CtxCommandError c -> ShowS
Show)
data CommandNotFound = CommandNotFound
{ CommandNotFound -> Message
msg :: Message
, CommandNotFound -> User
user :: User
, CommandNotFound -> Maybe Member
member :: Maybe Member
,
CommandNotFound -> [Text]
path :: [T.Text]
}
deriving (Int -> CommandNotFound -> ShowS
[CommandNotFound] -> ShowS
CommandNotFound -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CommandNotFound] -> ShowS
$cshowList :: [CommandNotFound] -> ShowS
show :: CommandNotFound -> String
$cshow :: CommandNotFound -> String
showsPrec :: Int -> CommandNotFound -> ShowS
$cshowsPrec :: Int -> CommandNotFound -> ShowS
Show)
newtype CommandInvoked c = CommandInvoked
{ forall c. CommandInvoked c -> c
ctx :: c
}
deriving stock (Int -> CommandInvoked c -> ShowS
forall c. Show c => Int -> CommandInvoked c -> ShowS
forall c. Show c => [CommandInvoked c] -> ShowS
forall c. Show c => CommandInvoked c -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CommandInvoked c] -> ShowS
$cshowList :: forall c. Show c => [CommandInvoked c] -> ShowS
show :: CommandInvoked c -> String
$cshow :: forall c. Show c => CommandInvoked c -> String
showsPrec :: Int -> CommandInvoked c -> ShowS
$cshowsPrec :: forall c. Show c => Int -> CommandInvoked c -> ShowS
Show)
useConstantPrefix :: T.Text -> P.Sem (CC.ParsePrefix Message ': r) a -> P.Sem r a
useConstantPrefix :: forall (r :: [(* -> *) -> * -> *]) a.
Text -> Sem (ParsePrefix Message : r) a -> Sem r a
useConstantPrefix Text
pre = forall (e :: (* -> *) -> * -> *) (r :: [(* -> *) -> * -> *]) a.
FirstOrder e "interpret" =>
(forall (rInitial :: [(* -> *) -> * -> *]) x.
e (Sem rInitial) x -> Sem r x)
-> Sem (e : r) a -> Sem r a
P.interpret (\case
CC.ParsePrefix Message { Text
$sel:content:Message :: Message -> Text
content :: Text
content } -> forall (f :: * -> *) a. Applicative f => a -> f a
pure ((Text
pre, ) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Text -> Maybe Text
T.stripPrefix Text
pre Text
content))
addCommands :: (BotC r, Typeable c, CommandContext c, P.Members [CC.ParsePrefix Message, CC.ConstructContext (Message, User, Maybe Member) c IO ()] r)
=> P.Sem (DSLState c r) a -> P.Sem r (P.Sem r (), CommandHandler c, a)
addCommands :: forall (r :: [(* -> *) -> * -> *]) c a.
(BotC r, Typeable c, CommandContext c,
Members
'[ParsePrefix Message,
ConstructContext (Message, User, Maybe Member) c IO ()]
r) =>
Sem (DSLState c r) a -> Sem r (Sem r (), CommandHandler c, a)
addCommands Sem (DSLState c r) a
m = do
(CommandHandler c
handler, a
res) <- forall (r :: [(* -> *) -> * -> *]) c (m :: * -> *) a x.
(Monad m, MonadFix m, Member (Final m) r) =>
Sem (DSLState m c a r) x -> Sem r (CommandHandler m c a, x)
CC.buildCommands Sem (DSLState c r) a
m
Sem r ()
remove <- forall (s :: EventType) (r :: [(* -> *) -> * -> *]).
(BotC r, ReactConstraints s) =>
(EHType s -> Sem r ()) -> Sem r (Sem r ())
react @'MessageCreateEvt forall a b. (a -> b) -> a -> b
$ \case
(Message
msg, Just User
user, Maybe Member
member) -> do
forall msg (r :: [(* -> *) -> * -> *]).
Member (ParsePrefix msg) r =>
msg -> Sem r (Maybe (Text, Text))
CC.parsePrefix Message
msg forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Just (Text
prefix, Text
cmd) -> do
Either (CmdInvokeFailReason c) (c, ())
r <- forall (m :: * -> *) msg c a (r :: [(* -> *) -> * -> *]).
(Monad m, Members '[ConstructContext msg c m a, Embed m] r,
CommandContext m c a) =>
CommandHandler m c a
-> msg
-> Text
-> Text
-> Sem r (Either (CmdInvokeFailReason c) (c, a))
CC.handleCommands CommandHandler c
handler (Message
msg, User
user, Maybe Member
member) Text
prefix Text
cmd
case Either (CmdInvokeFailReason c) (c, ())
r of
Left (CC.CommandInvokeError c
ctx CommandError
e) -> forall (r :: [(* -> *) -> * -> *]).
BotC r =>
CalamityEvent -> Sem r ()
fire forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Typeable a => a -> CalamityEvent
customEvt forall a b. (a -> b) -> a -> b
$ forall c. c -> CommandError -> CtxCommandError c
CtxCommandError c
ctx CommandError
e
Left (CC.NotFound [Text]
path) -> forall (r :: [(* -> *) -> * -> *]).
BotC r =>
CalamityEvent -> Sem r ()
fire forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Typeable a => a -> CalamityEvent
customEvt forall a b. (a -> b) -> a -> b
$ Message -> User -> Maybe Member -> [Text] -> CommandNotFound
CommandNotFound Message
msg User
user Maybe Member
member [Text]
path
Left CmdInvokeFailReason c
CC.NoContext -> forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
Right (c
ctx, ()) -> do
Counter
cmdInvoke <- forall (r :: [(* -> *) -> * -> *]).
Member MetricEff r =>
Text -> [(Text, Text)] -> Sem r Counter
registerCounter Text
"commands_invoked" [(Text
"name", [Text] -> Text
T.unwords forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) c a. Command m c a -> [Text]
commandPath (forall (m :: * -> *) c a.
CommandContext m c a =>
c -> Command m c a
CC.ctxCommand c
ctx))]
forall (f :: * -> *) a. Functor f => f a -> f ()
void forall a b. (a -> b) -> a -> b
$ forall (r :: [(* -> *) -> * -> *]).
Member MetricEff r =>
Int -> Counter -> Sem r Int
addCounter Int
1 Counter
cmdInvoke
forall (r :: [(* -> *) -> * -> *]).
BotC r =>
CalamityEvent -> Sem r ()
fire forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Typeable a => a -> CalamityEvent
customEvt forall a b. (a -> b) -> a -> b
$ forall c. c -> CommandInvoked c
CommandInvoked c
ctx
Maybe (Text, Text)
Nothing -> forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
EHType 'MessageCreateEvt
_ -> forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Sem r ()
remove, CommandHandler c
handler, a
res)
$(makeFieldLabelsNoPrefix ''CmdInvokeFailReason)
$(makeFieldLabelsNoPrefix ''CtxCommandError)
$(makeFieldLabelsNoPrefix ''CommandNotFound)
$(makeFieldLabelsNoPrefix ''CommandInvoked)