packages feed

calamity 0.1.8.1 → 0.1.9.0

raw patch · 22 files changed

+976/−67 lines, 22 filesdep +megaparsecPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: megaparsec

API changes (from Hackage documentation)

+ Calamity.Client.Client: waitUntilM :: forall (s :: EventType) r t eh ehB. (BotC r, WaitUntilMConstraints r s eh ehB t) => ehB -> Sem r t
+ Calamity.Client.Types: type WaitUntilMConstraints r s eh ehB t = (InsertEventHandler s, RemoveEventHandler s, Uncurry eh, eh ~ EHType s (Sem r) (), eh ~ Curried (t -> Sem r ()), Uncurry ehB, Uncurried ehB ~ (t -> Sem r Bool), Curry (t -> IO ()), Curried (t -> IO ()) ~ EHType s IO ())
+ Calamity.Commands.Check: MkCheck :: Text -> (Context -> IO (Maybe Text)) -> Check
+ Calamity.Commands.Check: [$sel:callback:MkCheck] :: Check -> Context -> IO (Maybe Text)
+ Calamity.Commands.Check: [$sel:name:MkCheck] :: Check -> Text
+ Calamity.Commands.Check: buildCheck :: Member (Final IO) r => Text -> (Context -> Sem r (Maybe Text)) -> Sem r Check
+ Calamity.Commands.Check: buildCheckPure :: Text -> (Context -> Maybe Text) -> Check
+ Calamity.Commands.Check: data Check
+ Calamity.Commands.Check: instance GHC.Generics.Generic Calamity.Commands.Check.Check
+ Calamity.Commands.Check: runCheck :: Member (Embed IO) r => Context -> Check -> Sem r (Either CommandError ())
+ Calamity.Commands.Command: Command :: Text -> Maybe Group -> [Check] -> (Context -> Text) -> (Context -> IO (Either CommandError a)) -> ((Context, a) -> IO (Maybe Text)) -> Command
+ Calamity.Commands.Command: [$sel:callback:Command] :: Command -> (Context, a) -> IO (Maybe Text)
+ Calamity.Commands.Command: [$sel:checks:Command] :: Command -> [Check]
+ Calamity.Commands.Command: [$sel:help:Command] :: Command -> Context -> Text
+ Calamity.Commands.Command: [$sel:name:Command] :: Command -> Text
+ Calamity.Commands.Command: [$sel:parent:Command] :: Command -> Maybe Group
+ Calamity.Commands.Command: [$sel:parser:Command] :: Command -> Context -> IO (Either CommandError a)
+ Calamity.Commands.Command: data Command
+ Calamity.Commands.Command: instance GHC.Generics.Generic Calamity.Commands.Command.CommandS
+ Calamity.Commands.Command: instance GHC.Show.Show Calamity.Commands.Command.Command
+ Calamity.Commands.Command: instance GHC.Show.Show Calamity.Commands.Command.CommandS
+ Calamity.Commands.Command: instance TextShow.Classes.TextShow Calamity.Commands.Command.Command
+ Calamity.Commands.Command: instance TextShow.Classes.TextShow Calamity.Commands.Command.CommandS
+ Calamity.Commands.CommandUtils: buildCallback :: Member (Final IO) r => ((Context, a) -> Sem (Fail : r) ()) -> Sem r ((Context, a) -> IO (Maybe Text))
+ Calamity.Commands.CommandUtils: buildCommand :: forall ps a r. (Member (Final IO) r, TypedCommandC ps a r) => Text -> Maybe Group -> [Check] -> (Context -> Text) -> (Context -> CommandForParsers ps r) -> Sem r Command
+ Calamity.Commands.CommandUtils: buildCommand' :: Member (Final IO) r => Text -> Maybe Group -> [Check] -> (Context -> Text) -> (Context -> Sem r (Either CommandError a)) -> ((Context, a) -> Sem (Fail : r) ()) -> Sem r Command
+ Calamity.Commands.CommandUtils: buildParser :: Member (Final IO) r => Text -> (Context -> Sem r (Either CommandError a)) -> Sem r (Context -> IO (Either CommandError a))
+ Calamity.Commands.CommandUtils: instance (Data.Typeable.Internal.Typeable x, Calamity.Commands.Parser.Parser x r, Calamity.Commands.CommandUtils.BuildTypedCommandParser xs r) => Calamity.Commands.CommandUtils.BuildTypedCommandParser (x : xs) r
+ Calamity.Commands.CommandUtils: instance Calamity.Commands.CommandUtils.ApplyTup () b
+ Calamity.Commands.CommandUtils: instance Calamity.Commands.CommandUtils.ApplyTup as b => Calamity.Commands.CommandUtils.ApplyTup (a, as) b
+ Calamity.Commands.CommandUtils: instance Calamity.Commands.CommandUtils.BuildTypedCommandParser '[] r
+ Calamity.Commands.CommandUtils: invokeCommand :: Member (Embed IO) r => Context -> Command -> Sem r (Either CommandError ())
+ Calamity.Commands.CommandUtils: runCommand :: Member (Embed IO) r => Context -> Command -> Sem r (Either CommandError ())
+ Calamity.Commands.CommandUtils: type TypedCommandC ps a r = (ApplyTupRes a (CommandSemType r) ~ CommandForParsers ps r, a ~ ParamsFromParsers ps, BuildTypedCommandParser ps r, ApplyTup a (CommandSemType r))
+ Calamity.Commands.CommandUtils: type family CommandForParsers (ps :: [Type]) r
+ Calamity.Commands.Context: Context :: Message -> Maybe Guild -> Maybe Member -> Channel -> User -> Command -> Text -> Text -> Context
+ Calamity.Commands.Context: [$sel:channel:Context] :: Context -> Channel
+ Calamity.Commands.Context: [$sel:command:Context] :: Context -> Command
+ Calamity.Commands.Context: [$sel:guild:Context] :: Context -> Maybe Guild
+ Calamity.Commands.Context: [$sel:member:Context] :: Context -> Maybe Member
+ Calamity.Commands.Context: [$sel:message:Context] :: Context -> Message
+ Calamity.Commands.Context: [$sel:prefix:Context] :: Context -> Text
+ Calamity.Commands.Context: [$sel:unparsedParams:Context] :: Context -> Text
+ Calamity.Commands.Context: [$sel:user:Context] :: Context -> User
+ Calamity.Commands.Context: data Context
+ Calamity.Commands.Context: instance Calamity.Types.Tellable.Tellable Calamity.Commands.Context.Context
+ Calamity.Commands.Context: instance GHC.Generics.Generic Calamity.Commands.Context.Context
+ Calamity.Commands.Context: instance GHC.Show.Show Calamity.Commands.Context.Context
+ Calamity.Commands.Context: instance TextShow.Classes.TextShow Calamity.Commands.Context.Context
+ Calamity.Commands.Dsl: command :: forall ps a r. (Member (Final IO) r, TypedCommandC ps a r) => Text -> (Context -> CommandForParsers ps r) -> Sem (DSLState r) Command
+ Calamity.Commands.Dsl: command' :: Member (Final IO) r => Text -> (Context -> Sem r (Either CommandError a)) -> ((Context, a) -> Sem (Fail : r) ()) -> Sem (DSLState r) Command
+ Calamity.Commands.Dsl: group :: Member (Final IO) r => Text -> Sem (DSLState r) a -> Sem (DSLState r) a
+ Calamity.Commands.Dsl: help :: Member (Reader (Context -> Text)) r => (Context -> Text) -> Sem r a -> Sem r a
+ Calamity.Commands.Dsl: raiseDSL :: Sem r a -> Sem (DSLState r) a
+ Calamity.Commands.Dsl: requires :: [Check] -> Sem (DSLState r) a -> Sem (DSLState r) a
+ Calamity.Commands.Dsl: requires' :: Member (Final IO) r => Text -> (Context -> Sem r (Maybe Text)) -> Sem (DSLState r) a -> Sem (DSLState r) a
+ Calamity.Commands.Dsl: requiresPure :: [(Text, Context -> Maybe Text)] -> Sem (DSLState r) a -> Sem (DSLState r) a
+ Calamity.Commands.Dsl: type DSLState r = (LocalWriter (HashMap Text Command) : LocalWriter (HashMap Text Group) : Reader (Maybe Group) : Reader (Context -> Text) : Reader [Check] : Fixpoint : r)
+ Calamity.Commands.Error: CheckError :: Text -> Text -> CommandError
+ Calamity.Commands.Error: InvokeError :: Text -> Text -> CommandError
+ Calamity.Commands.Error: ParseError :: Text -> Text -> CommandError
+ Calamity.Commands.Error: data CommandError
+ Calamity.Commands.Error: instance GHC.Generics.Generic Calamity.Commands.Error.CommandError
+ Calamity.Commands.Error: instance GHC.Show.Show Calamity.Commands.Error.CommandError
+ Calamity.Commands.Error: instance TextShow.Classes.TextShow Calamity.Commands.Error.CommandError
+ Calamity.Commands.Group: Group :: Text -> Maybe Group -> HashMap Text Command -> HashMap Text Group -> (Context -> Text) -> [Check] -> Group
+ Calamity.Commands.Group: [$sel:checks:Group] :: Group -> [Check]
+ Calamity.Commands.Group: [$sel:children:Group] :: Group -> HashMap Text Group
+ Calamity.Commands.Group: [$sel:commands:Group] :: Group -> HashMap Text Command
+ Calamity.Commands.Group: [$sel:help:Group] :: Group -> Context -> Text
+ Calamity.Commands.Group: [$sel:name:Group] :: Group -> Text
+ Calamity.Commands.Group: [$sel:parent:Group] :: Group -> Maybe Group
+ Calamity.Commands.Group: data Group
+ Calamity.Commands.Group: instance GHC.Generics.Generic Calamity.Commands.Group.Group
+ Calamity.Commands.Handler: CommandHandler :: HashMap Text Group -> HashMap Text Command -> CommandHandler
+ Calamity.Commands.Handler: [$sel:commands:CommandHandler] :: CommandHandler -> HashMap Text Command
+ Calamity.Commands.Handler: [$sel:groups:CommandHandler] :: CommandHandler -> HashMap Text Group
+ Calamity.Commands.Handler: addCommands :: (BotC r, Member ParsePrefix r) => Sem (DSLState r) a -> Sem r (Sem r (), CommandHandler, a)
+ Calamity.Commands.Handler: buildCommands :: Member (Final IO) r => Sem (DSLState r) a -> Sem r (CommandHandler, a)
+ Calamity.Commands.Handler: buildContext :: BotC r => Message -> Text -> Command -> Text -> Sem r (Maybe Context)
+ Calamity.Commands.Handler: data CommandHandler
+ Calamity.Commands.Handler: instance GHC.Generics.Generic Calamity.Commands.Handler.CommandHandler
+ Calamity.Commands.LocalWriter: [Llisten] :: m a -> LocalWriter o m (o, a)
+ Calamity.Commands.LocalWriter: [Ltell] :: o -> LocalWriter o m ()
+ Calamity.Commands.LocalWriter: data LocalWriter o m a
+ Calamity.Commands.LocalWriter: llisten :: forall o_amon r_amRO a_Xmon. MemberWithError (LocalWriter o_amon) r_amRO => Sem r_amRO a_Xmon -> Sem r_amRO (o_amon, a_Xmon)
+ Calamity.Commands.LocalWriter: ltell :: forall o_amoj r_amRM. MemberWithError (LocalWriter o_amoj) r_amRM => o_amoj -> Sem r_amRM ()
+ Calamity.Commands.LocalWriter: runLocalWriter :: Monoid o => Sem (LocalWriter o : r) a -> Sem r (o, a)
+ Calamity.Commands.ParsePrefix: [ParsePrefix] :: Message -> ParsePrefix m (Maybe (Text, Text))
+ Calamity.Commands.ParsePrefix: data ParsePrefix m a
+ Calamity.Commands.ParsePrefix: parsePrefix :: forall r_a6EvC. MemberWithError ParsePrefix r_a6EvC => Message -> Sem r_a6EvC (Maybe (Text, Text))
+ Calamity.Commands.ParsePrefix: useConstantPrefix :: Text -> Sem (ParsePrefix : r) a -> Sem r a
+ Calamity.Commands.Parser: class Parser (a :: Type) r where {
+ Calamity.Commands.Parser: data KleeneConcat a
+ Calamity.Commands.Parser: instance (Calamity.Commands.Parser.Parser a r, Data.Typeable.Internal.Typeable a) => Calamity.Commands.Parser.Parser (GHC.Base.NonEmpty a) r
+ Calamity.Commands.Parser: instance (GHC.Base.Monoid (Calamity.Commands.Parser.ParserResult a), Calamity.Commands.Parser.Parser a r) => Calamity.Commands.Parser.Parser (Calamity.Commands.Parser.KleeneConcat a) r
+ Calamity.Commands.Parser: instance Calamity.Commands.Parser.Parser (Calamity.Commands.Parser.KleeneConcat Data.Text.Internal.Lazy.Text) r
+ Calamity.Commands.Parser: instance Calamity.Commands.Parser.Parser (Calamity.Types.Snowflake.Snowflake Calamity.Types.Model.Channel.Channel) r
+ Calamity.Commands.Parser: instance Calamity.Commands.Parser.Parser (Calamity.Types.Snowflake.Snowflake Calamity.Types.Model.Guild.Emoji.Emoji) r
+ Calamity.Commands.Parser: instance Calamity.Commands.Parser.Parser (Calamity.Types.Snowflake.Snowflake Calamity.Types.Model.Guild.Member.Member) r
+ Calamity.Commands.Parser: instance Calamity.Commands.Parser.Parser (Calamity.Types.Snowflake.Snowflake Calamity.Types.Model.Guild.Role.Role) r
+ Calamity.Commands.Parser: instance Calamity.Commands.Parser.Parser (Calamity.Types.Snowflake.Snowflake Calamity.Types.Model.User.User) r
+ Calamity.Commands.Parser: instance Calamity.Commands.Parser.Parser Calamity.Types.Model.Guild.Member.Member r
+ Calamity.Commands.Parser: instance Calamity.Commands.Parser.Parser Data.Text.Internal.Lazy.Text r
+ Calamity.Commands.Parser: instance Calamity.Commands.Parser.Parser a r => Calamity.Commands.Parser.Parser [a] r
+ Calamity.Commands.Parser: instance Polysemy.Internal.Union.Member Calamity.Cache.Eff.CacheEff r => Calamity.Commands.Parser.Parser Calamity.Types.Model.User.User r
+ Calamity.Commands.Parser: instance Text.Megaparsec.Error.ShowErrorComponent Data.Text.Internal.Lazy.Text
+ Calamity.Commands.Parser: instance forall k (a :: k) (r :: Polysemy.Internal.Kind.EffectRow). Calamity.Commands.Parser.Parser (Calamity.Types.Snowflake.Snowflake a) r
+ Calamity.Commands.Parser: parse :: Parser a r => (Context, Text) -> Sem r (Either Text (ParserResult a, Text))
+ Calamity.Commands.Parser: type ParserResult a = a;
+ Calamity.Commands.Parser: type family ParserResult a;
+ Calamity.Commands.Parser: }
+ Calamity.Internal.RunIntoIO: bindSemToIO :: forall r p a. Member (Final IO) r => (p -> Sem r a) -> Sem r (p -> IO (Maybe a))
+ Calamity.Internal.Utils: (<.>) :: Functor f => (a -> b) -> (c -> f a) -> c -> f b
+ Calamity.Internal.Utils: (<<*>>) :: (Applicative f, Applicative g) => f (g (a -> b)) -> f (g a) -> f (g b)
+ Calamity.Internal.Utils: justToEither :: Maybe e -> Either e ()
+ Calamity.Internal.Utils: leftToMaybe :: Either e a -> Maybe e
+ Calamity.Internal.Utils: rightToMaybe :: Either e a -> Maybe a
+ Calamity.Types.Tellable: instance Calamity.Types.Tellable.Tellable (Calamity.Types.Snowflake.Snowflake Calamity.Types.Model.Channel.Channel)
+ Calamity.Types.Tellable: instance Calamity.Types.Tellable.Tellable Calamity.Types.Model.Channel.Channel
+ Calamity.Types.Tellable: instance Calamity.Types.Tellable.ToMessage Data.Text.Internal.Lazy.Text
+ Calamity.Types.Tellable: instance Calamity.Types.Tellable.ToMessage GHC.Base.String
- Calamity.Cache.Eff: delDM :: forall r_a9HL6. MemberWithError CacheEff r_a9HL6 => Snowflake DMChannel -> Sem r_a9HL6 ()
+ Calamity.Cache.Eff: delDM :: forall r_a9Joq. MemberWithError CacheEff r_a9Joq => Snowflake DMChannel -> Sem r_a9Joq ()
- Calamity.Cache.Eff: delGuild :: forall r_a9HKZ. MemberWithError CacheEff r_a9HKZ => Snowflake Guild -> Sem r_a9HKZ ()
+ Calamity.Cache.Eff: delGuild :: forall r_a9Joj. MemberWithError CacheEff r_a9Joj => Snowflake Guild -> Sem r_a9Joj ()
- Calamity.Cache.Eff: delMessage :: forall r_a9HLr. MemberWithError CacheEff r_a9HLr => Snowflake Message -> Sem r_a9HLr ()
+ Calamity.Cache.Eff: delMessage :: forall r_a9JoL. MemberWithError CacheEff r_a9JoL => Snowflake Message -> Sem r_a9JoL ()
- Calamity.Cache.Eff: delUnavailableGuild :: forall r_a9HLk. MemberWithError CacheEff r_a9HLk => Snowflake Guild -> Sem r_a9HLk ()
+ Calamity.Cache.Eff: delUnavailableGuild :: forall r_a9JoE. MemberWithError CacheEff r_a9JoE => Snowflake Guild -> Sem r_a9JoE ()
- Calamity.Cache.Eff: delUser :: forall r_a9HLd. MemberWithError CacheEff r_a9HLd => Snowflake User -> Sem r_a9HLd ()
+ Calamity.Cache.Eff: delUser :: forall r_a9Jox. MemberWithError CacheEff r_a9Jox => Snowflake User -> Sem r_a9Jox ()
- Calamity.Cache.Eff: getBotUser :: forall r_a9HKT. MemberWithError CacheEff r_a9HKT => Sem r_a9HKT (Maybe User)
+ Calamity.Cache.Eff: getBotUser :: forall r_a9Jod. MemberWithError CacheEff r_a9Jod => Sem r_a9Jod (Maybe User)
- Calamity.Cache.Eff: getDM :: forall r_a9HL3. MemberWithError CacheEff r_a9HL3 => Snowflake DMChannel -> Sem r_a9HL3 (Maybe DMChannel)
+ Calamity.Cache.Eff: getDM :: forall r_a9Jon. MemberWithError CacheEff r_a9Jon => Snowflake DMChannel -> Sem r_a9Jon (Maybe DMChannel)
- Calamity.Cache.Eff: getDMs :: forall r_a9HL5. MemberWithError CacheEff r_a9HL5 => Sem r_a9HL5 [DMChannel]
+ Calamity.Cache.Eff: getDMs :: forall r_a9Jop. MemberWithError CacheEff r_a9Jop => Sem r_a9Jop [DMChannel]
- Calamity.Cache.Eff: getGuild :: forall r_a9HKW. MemberWithError CacheEff r_a9HKW => Snowflake Guild -> Sem r_a9HKW (Maybe Guild)
+ Calamity.Cache.Eff: getGuild :: forall r_a9Jog. MemberWithError CacheEff r_a9Jog => Snowflake Guild -> Sem r_a9Jog (Maybe Guild)
- Calamity.Cache.Eff: getGuilds :: forall r_a9HKY. MemberWithError CacheEff r_a9HKY => Sem r_a9HKY [Guild]
+ Calamity.Cache.Eff: getGuilds :: forall r_a9Joi. MemberWithError CacheEff r_a9Joi => Sem r_a9Joi [Guild]
- Calamity.Cache.Eff: getMessage :: forall r_a9HLo. MemberWithError CacheEff r_a9HLo => Snowflake Message -> Sem r_a9HLo (Maybe Message)
+ Calamity.Cache.Eff: getMessage :: forall r_a9JoI. MemberWithError CacheEff r_a9JoI => Snowflake Message -> Sem r_a9JoI (Maybe Message)
- Calamity.Cache.Eff: getMessages :: forall r_a9HLq. MemberWithError CacheEff r_a9HLq => Sem r_a9HLq [Message]
+ Calamity.Cache.Eff: getMessages :: forall r_a9JoK. MemberWithError CacheEff r_a9JoK => Sem r_a9JoK [Message]
- Calamity.Cache.Eff: getUnavailableGuilds :: forall r_a9HLj. MemberWithError CacheEff r_a9HLj => Sem r_a9HLj [Snowflake Guild]
+ Calamity.Cache.Eff: getUnavailableGuilds :: forall r_a9JoD. MemberWithError CacheEff r_a9JoD => Sem r_a9JoD [Snowflake Guild]
- Calamity.Cache.Eff: getUser :: forall r_a9HLa. MemberWithError CacheEff r_a9HLa => Snowflake User -> Sem r_a9HLa (Maybe User)
+ Calamity.Cache.Eff: getUser :: forall r_a9Jou. MemberWithError CacheEff r_a9Jou => Snowflake User -> Sem r_a9Jou (Maybe User)
- Calamity.Cache.Eff: getUsers :: forall r_a9HLc. MemberWithError CacheEff r_a9HLc => Sem r_a9HLc [User]
+ Calamity.Cache.Eff: getUsers :: forall r_a9Jow. MemberWithError CacheEff r_a9Jow => Sem r_a9Jow [User]
- Calamity.Cache.Eff: isUnavailableGuild :: forall r_a9HLh. MemberWithError CacheEff r_a9HLh => Snowflake Guild -> Sem r_a9HLh Bool
+ Calamity.Cache.Eff: isUnavailableGuild :: forall r_a9JoB. MemberWithError CacheEff r_a9JoB => Snowflake Guild -> Sem r_a9JoB Bool
- Calamity.Cache.Eff: setBotUser :: forall r_a9HKR. MemberWithError CacheEff r_a9HKR => User -> Sem r_a9HKR ()
+ Calamity.Cache.Eff: setBotUser :: forall r_a9Job. MemberWithError CacheEff r_a9Job => User -> Sem r_a9Job ()
- Calamity.Cache.Eff: setDM :: forall r_a9HL1. MemberWithError CacheEff r_a9HL1 => DMChannel -> Sem r_a9HL1 ()
+ Calamity.Cache.Eff: setDM :: forall r_a9Jol. MemberWithError CacheEff r_a9Jol => DMChannel -> Sem r_a9Jol ()
- Calamity.Cache.Eff: setGuild :: forall r_a9HKU. MemberWithError CacheEff r_a9HKU => Guild -> Sem r_a9HKU ()
+ Calamity.Cache.Eff: setGuild :: forall r_a9Joe. MemberWithError CacheEff r_a9Joe => Guild -> Sem r_a9Joe ()
- Calamity.Cache.Eff: setMessage :: forall r_a9HLm. MemberWithError CacheEff r_a9HLm => Message -> Sem r_a9HLm ()
+ Calamity.Cache.Eff: setMessage :: forall r_a9JoG. MemberWithError CacheEff r_a9JoG => Message -> Sem r_a9JoG ()
- Calamity.Cache.Eff: setUnavailableGuild :: forall r_a9HLf. MemberWithError CacheEff r_a9HLf => Snowflake Guild -> Sem r_a9HLf ()
+ Calamity.Cache.Eff: setUnavailableGuild :: forall r_a9Joz. MemberWithError CacheEff r_a9Joz => Snowflake Guild -> Sem r_a9Joz ()
- Calamity.Cache.Eff: setUser :: forall r_a9HL8. MemberWithError CacheEff r_a9HL8 => User -> Sem r_a9HL8 ()
+ Calamity.Cache.Eff: setUser :: forall r_a9Jos. MemberWithError CacheEff r_a9Jos => User -> Sem r_a9Jos ()
- Calamity.Client.Client: waitUntil :: forall (s :: EventType) r t eh ehB. (BotC r, WaitUntilConstraints r s eh ehB t) => ehB -> Sem r t
+ Calamity.Client.Client: waitUntil :: forall (s :: EventType) r t eh check. (BotC r, WaitUntilConstraints r s eh check t) => check -> Sem r t
- Calamity.Client.Types: type WaitUntilConstraints r s eh ehB t = (InsertEventHandler s, RemoveEventHandler s, Uncurry eh, eh ~ EHType s (Sem r) (), eh ~ Curried (t -> Sem r ()), Uncurry ehB, Uncurried ehB ~ (t -> Sem r Bool), Curry (t -> IO ()), Curried (t -> IO ()) ~ EHType s IO ())
+ Calamity.Client.Types: type WaitUntilConstraints r s eh check t = (InsertEventHandler s, RemoveEventHandler s, Uncurry eh, eh ~ EHType s (Sem r) (), eh ~ Curried (t -> Sem r ()), Uncurry check, Uncurried check ~ (t -> Bool), Curry (t -> IO ()), Curried (t -> IO ()) ~ EHType s IO ())
- Calamity.Internal.RunIntoIO: [IntoIO] :: (p -> m ()) -> IntoIO p m (p -> IO ())
+ Calamity.Internal.RunIntoIO: [IntoIO] :: (p -> m r) -> IntoIO p r m (p -> IO (Maybe r))
- Calamity.Internal.RunIntoIO: data IntoIO p m a
+ Calamity.Internal.RunIntoIO: data IntoIO p r m a
- Calamity.Internal.RunIntoIO: intoIO :: forall p_axeY r_axwF. MemberWithError (IntoIO p_axeY) r_axwF => (p_axeY -> Sem r_axwF ()) -> Sem r_axwF (p_axeY -> IO ())
+ Calamity.Internal.RunIntoIO: intoIO :: forall p_aDGc r_aDGe r_aDLo. MemberWithError (IntoIO p_aDGc r_aDGe) r_aDLo => (p_aDGc -> Sem r_aDLo r_aDGe) -> Sem r_aDLo (p_aDGc -> IO (Maybe r_aDGe))
- Calamity.Internal.RunIntoIO: runIntoIOFinal :: forall r p a. Member (Final IO) r => Sem (IntoIO p : r) a -> Sem r a
+ Calamity.Internal.RunIntoIO: runIntoIOFinal :: forall r p b a. Member (Final IO) r => Sem (IntoIO p b : r) a -> Sem r a
- Calamity.Internal.Utils: infixl 4 <<$>>
+ Calamity.Internal.Utils: infixl 4 <.>
- Calamity.Metrics.Eff: addCounter :: forall r_aF2T. MemberWithError MetricEff r_aF2T => Int -> Counter -> Sem r_aF2T Int
+ Calamity.Metrics.Eff: addCounter :: forall r_aGid. MemberWithError MetricEff r_aGid => Int -> Counter -> Sem r_aGid Int
- Calamity.Metrics.Eff: modifyGauge :: forall r_aF2W. MemberWithError MetricEff r_aF2W => (Double -> Double) -> Gauge -> Sem r_aF2W Double
+ Calamity.Metrics.Eff: modifyGauge :: forall r_aGig. MemberWithError MetricEff r_aGig => (Double -> Double) -> Gauge -> Sem r_aGig Double
- Calamity.Metrics.Eff: observeHistogram :: forall r_aF2Z. MemberWithError MetricEff r_aF2Z => Double -> Histogram -> Sem r_aF2Z HistogramSample
+ Calamity.Metrics.Eff: observeHistogram :: forall r_aGij. MemberWithError MetricEff r_aGij => Double -> Histogram -> Sem r_aGij HistogramSample
- Calamity.Metrics.Eff: registerCounter :: forall r_aF2J. MemberWithError MetricEff r_aF2J => Text -> [(Text, Text)] -> Sem r_aF2J Counter
+ Calamity.Metrics.Eff: registerCounter :: forall r_aGi3. MemberWithError MetricEff r_aGi3 => Text -> [(Text, Text)] -> Sem r_aGi3 Counter
- Calamity.Metrics.Eff: registerGauge :: forall r_aF2M. MemberWithError MetricEff r_aF2M => Text -> [(Text, Text)] -> Sem r_aF2M Gauge
+ Calamity.Metrics.Eff: registerGauge :: forall r_aGi6. MemberWithError MetricEff r_aGi6 => Text -> [(Text, Text)] -> Sem r_aGi6 Gauge
- Calamity.Metrics.Eff: registerHistogram :: forall r_aF2P. MemberWithError MetricEff r_aF2P => Text -> [(Text, Text)] -> [Double] -> Sem r_aF2P Histogram
+ Calamity.Metrics.Eff: registerHistogram :: forall r_aGi9. MemberWithError MetricEff r_aGi9 => Text -> [(Text, Text)] -> [Double] -> Sem r_aGi9 Histogram

Files

ChangeLog.md view
@@ -1,5 +1,15 @@ # Changelog for Calamity +## 0.1.9.0++*2020-05-22*++* Added commands, located in `Calamity.Commands`, along with a DSL for declaring+  commands nicely.+  +* Renamed `waitUntil` to `waitUntilM`, and introduced a variant with a pure+  check function that takes the original name of `waitUntil`.+ ## 0.1.8.0  *2020-05-15*
README.md view
@@ -56,6 +56,8 @@  import           Calamity import           Calamity.Cache.InMemory+import           Calamity.Commands+import qualified Calamity.Commands.Context                  as CommandContext import           Calamity.Metrics.Noop  import           Control.Concurrent@@ -95,22 +97,45 @@     Left e -> DiP.error (e ^. packed)     _      -> pure () -info = DiP.info @Text-debug = DiP.info @Text+info, debug :: BotC r => Text -> P.Sem r ()+info = DiP.info+debug = DiP.info  tellt :: (BotC r, Tellable t) => t -> Text -> P.Sem r (Either RestError Message)-tellt = tell @Text+tellt t m = tell t $ L.toStrict m  main :: IO () main = do   token <- view packed <$> getEnv "BOT_TOKEN"-  void . P.runFinal . P.embedToFinal . runCounterAtomic . runCacheInMemory . runMetricsPrometheusIO+  void . P.runFinal . P.embedToFinal . runCounterAtomic . runCacheInMemory . runMetricsPrometheusIO . useConstantPrefix "!"     $ runBotIO (BotToken token) $ do+    addCommands $ do+      command @'[Text, Snowflake User] "test" $ \ctx something aUser -> do+        info $ "something = " <> showt something <> ", aUser = " <> showt aUser+      command @'[] "hello" $ \ctx -> do+        void $ tellt ctx "heya"+      group "testgroup" $ do+        command @'[[Text]] "test" $ \ctx l -> do+          void $ tellt ctx ("you sent: " <> showtl l)+        command @'[] "count" $ \ctx -> do+          val <- getCounter+          void $ tellt ctx ("The value is: " <> showtl val)+        group "say" $ do+          command @'[KleeneConcat Text] "this" $ \ctx msg -> do+            void $ tellt ctx msg+      command @'[] "explode" $ \ctx -> do+        Just x <- pure Nothing+        debug "unreachable!"+      command @'[] "bye" $ \ctx -> do+        void $ tellt ctx "bye!"+        stopBot+      command @'[] "fire-evt" $ \ctx -> do+        fire $ customEvt @"my-event" ("aha" :: Text, ctx ^. #message)+      command @'[Text] "wait-for" $ \ctx s -> do+        void $ tellt ctx ("waiting for !" <> s)+        waitUntil @'MessageCreateEvt (\msg -> msg ^. #content == ("!" <> s))+        void $ tellt ctx ("got !" <> s)     react @'MessageCreateEvt $ \msg -> handleFailByLogging $ case msg ^. #content of-      "!count" -> replicateM_ 3 $ do-        val <- getCounter-        info $ "the counter is: " <> showt val-        void $ tellt msg ("The value is: " <> showt val)       "!say hi" -> replicateM_ 3 . P.async $ do         info "saying heya"         Right msg' <- tellt msg "heya"@@ -119,18 +144,11 @@         info "slept"         void . invoke $ EditMessage (msg ^. #channelID) msg' (Just "lol") Nothing         info "edited"-      "!explode" -> do-        Just x <- pure Nothing-        debug "unreachable!"-      "!bye" -> do-        void $ tellt msg "bye!"-        stopBot-      "!fire-evt" -> fire $ customEvt @"my-event" ("aha" :: Text, msg)-      "!wait" -> do-        void $ tellt msg "waiting for !continue"-        waitUntil @'MessageCreateEvt (\msg -> (debug $ "got message: " <> showt msg) >> (pure $ msg ^. #content == "!continue"))-        void $ tellt msg "got !continue"       _ -> pure ()+    react @('CustomEvt "command-error" (CommandContext.Context, CommandError)) $ \(ctx, e) -> do+      info $ "Command failed with reason: " <> showt e+      case e of+        ParseError t r -> void . tellt ctx $ "Failed to parse parameter: " <> L.fromStrict t <> ", with reason: ```\n" <> r <> "```"     react @('CustomEvt "my-event" (Text, Message)) $ \(s, m) ->       void $ tellt m ("Somebody told me to tell you about: " <> s) ```
calamity.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 2763f995fb4b7a5e41a214cd6a7d841b9c99817d27924c59d216ff0271005b8b+-- hash: bcd0c50d2410522101e3729e6c1c6f0d6a5830f977929848fc4a7507d8af872b  name:           calamity-version:        0.1.8.1+version:        0.1.9.0 synopsis:       A library for writing discord bots description:    Please see the README on GitHub at <https://github.com/nitros12/calamity#readme> category:       Network, Web@@ -40,6 +40,18 @@       Calamity.Client.Client       Calamity.Client.ShardManager       Calamity.Client.Types+      Calamity.Commands+      Calamity.Commands.Check+      Calamity.Commands.Command+      Calamity.Commands.CommandUtils+      Calamity.Commands.Context+      Calamity.Commands.Dsl+      Calamity.Commands.Error+      Calamity.Commands.Group+      Calamity.Commands.Handler+      Calamity.Commands.LocalWriter+      Calamity.Commands.ParsePrefix+      Calamity.Commands.Parser       Calamity.Gateway       Calamity.Gateway.DispatchEvents       Calamity.Gateway.Shard@@ -111,7 +123,7 @@   hs-source-dirs:       src   default-extensions: CPP AllowAmbiguousTypes BlockArguments NoMonomorphismRestriction BangPatterns BinaryLiterals UndecidableInstances ConstraintKinds DataKinds DefaultSignatures DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable DoAndIfThenElse EmptyDataDecls ExistentialQuantification FlexibleContexts FlexibleInstances FunctionalDependencies GADTs DerivingVia DerivingStrategies GeneralizedNewtypeDeriving StandaloneDeriving DeriveAnyClass InstanceSigs KindSignatures LambdaCase MultiParamTypeClasses MultiWayIf NamedFieldPuns OverloadedStrings OverloadedLabels PartialTypeSignatures PatternGuards PolyKinds RankNTypes RecordWildCards ScopedTypeVariables TupleSections TypeFamilies TypeSynonymInstances ViewPatterns DuplicateRecordFields TypeOperators TypeApplications RoleAnnotations-  ghc-options: -fplugin=Polysemy.Plugin -funbox-strict-fields -Wall+  ghc-options: -fplugin=Polysemy.Plugin -funbox-strict-fields -Wall -fno-warn-name-shadowing   build-depends:       aeson >=1.4 && <2     , async >=2.2 && <3@@ -135,6 +147,7 @@     , http-types >=0.12 && <0.13     , lens >=4.18 && <5     , lens-aeson >=1.1 && <2+    , megaparsec >=8 && <9     , mtl >=2.2 && <3     , polysemy >=1.3 && <2     , polysemy-plugin >=0.2 && <0.3
src/Calamity/Client/Client.hs view
@@ -9,6 +9,7 @@     , events     , fire     , waitUntil+    , waitUntilM     , CalamityEvent(Dispatch, ShutDown)     , customEvt ) where @@ -19,9 +20,9 @@ import           Calamity.Gateway.Types import           Calamity.HTTP.Internal.Ratelimit import           Calamity.Internal.GenericCurry+import           Calamity.Internal.RunIntoIO import qualified Calamity.Internal.SnowflakeMap   as SM import           Calamity.Internal.Updateable-import           Calamity.Internal.RunIntoIO import           Calamity.Internal.Utils import           Calamity.Metrics.Eff import           Calamity.Types.Model.Channel@@ -34,7 +35,7 @@ import           Control.Concurrent.MVar import           Control.Concurrent.STM import           Control.Exception                ( SomeException )-import           Control.Lens+import           Control.Lens                     hiding ( (<.>) ) import           Control.Monad  import           Data.Default.Class@@ -98,11 +99,6 @@         Di.push "calamity-stop" finishUp         pure Nothing -ehToIO :: forall r p. P.Member (P.Final IO) r => (p -> P.Sem r ()) -> P.Sem r (p -> IO ())-ehToIO f = runIntoIOFinal go-  where go :: P.Sem (IntoIO p ': r) (p -> IO ())-        go = intoIO (P.raise . f)- -- | Register an event handler, returning an action that removes the event handler from the bot. -- -- Refer to 'EventType' for what events you can register, and 'EHType' for the@@ -136,10 +132,10 @@       => EHType s (P.Sem r) ()       -> P.Sem r (P.Sem r ()) react handler = do-  handler' <- ehToIO $ uncurryG handler+  handler' <- bindSemToIO $ uncurryG handler   ehidC <- P.asks (^. #ehidCounter)   id' <- P.embed $ atomicModifyIORef ehidC (\i -> (succ i, i))-  let handlers = makeEventHandlers (Proxy @s) id' (curryG handler')+  let handlers = makeEventHandlers (Proxy @s) id' (const () <.> curryG handler')   P.atomicModify (handlers <>)   pure $ removeHandler @s id' @@ -184,18 +180,20 @@   P.embed $ dupChan inc  -- | Wait until an event satisfying a condition happens, then returns it's--- parameters+-- parameters. --+-- The check function for this command is pure unlike 'waitUntilM'+-- -- This is what it would look like with @s ~ \''MessageCreateEvt'@: -- -- @--- 'waitUntil' :: ('Message' -> 'P.Sem' r 'Bool') -> 'P.Sem' r 'Message'+-- 'waitUntil' :: ('Message' -> 'Bool') -> 'P.Sem' r 'Message' -- @ -- -- And for @s ~ \''MessageUpdateEvt'@: -- -- @--- 'waitUntil' :: ('Message' -> 'Message' -> 'P.Sem' r 'Bool') -> 'P.Sem' r ('Message', 'Message')+-- 'waitUntil' :: ('Message' -> 'Message' -> 'Bool') -> 'P.Sem' r ('Message', 'Message') -- @ -- -- ==== Examples@@ -203,15 +201,55 @@ -- Waiting for a message containing the text \"hi\": -- -- @--- f = do msg \<\- 'waitUntil' @\''MessageCreateEvt' (\m -> 'pure' $ 'Data.Text.Lazy.isInfixOf' "hi" $ m ^. #content)+-- f = do msg \<\- 'waitUntil' @\''MessageCreateEvt' (\m -> 'Data.Text.Lazy.isInfixOf' "hi" $ m ^. #content) --        print $ msg ^. #content -- @ waitUntil+  :: forall (s :: EventType) r t eh check.+  ( BotC r, WaitUntilConstraints r s eh check t)+  => check+  -> P.Sem r t+waitUntil f = do+  result <- P.embed newEmptyMVar+  remove <- react @s (curryG $ checker result)+  res <- P.embed $ takeMVar result+  remove+  pure res+  where+    checker :: MVar t -> t -> P.Sem r ()+    checker result args = do+      when (uncurryG f args) $ do+        P.embed $ putMVar result args++-- | Wait until an event satisfying a condition happens, then returns it's+-- parameters+--+-- This is what it would look like with @s ~ \''MessageCreateEvt'@:+--+-- @+-- 'waitUntilM' :: ('Message' -> 'P.Sem' r 'Bool') -> 'P.Sem' r 'Message'+-- @+--+-- And for @s ~ \''MessageUpdateEvt'@:+--+-- @+-- 'waitUntilM' :: ('Message' -> 'Message' -> 'P.Sem' r 'Bool') -> 'P.Sem' r ('Message', 'Message')+-- @+--+-- ==== Examples+--+-- Waiting for a message containing the text \"hi\":+--+-- @+-- f = do msg \<\- 'waitUntilM' @\''MessageCreateEvt' (\m -> ('debug' $ "got message: " <> 'showt' msg) >> ('pure' $ 'Data.Text.Lazy.isInfixOf' "hi" $ m ^. #content))+--        print $ msg ^. #content+-- @+waitUntilM   :: forall (s :: EventType) r t eh ehB.-  ( BotC r, WaitUntilConstraints r s eh ehB t)+  ( BotC r, WaitUntilMConstraints r s eh ehB t)   => ehB   -> P.Sem r t-waitUntil f = do+waitUntilM f = do   result <- P.embed newEmptyMVar   remove <- react @s (curryG $ checker result)   res <- P.embed $ takeMVar result@@ -221,7 +259,6 @@     checker :: MVar t -> t -> P.Sem r ()     checker result args = do       res <- uncurryG f args-      debug $ "checker result: " +|| res ||+ ""       when res $ do         P.embed $ putMVar result args @@ -284,7 +321,7 @@   eventHandlers <- P.atomicGet   actions <- P.runFail $ do     cacheUpdateHisto <- registerHistogram "cache_update" mempty [10, 20..100]-    (time, res) <- timeA $ handleEvent' eventHandlers data'+    (time, res) <- timeA $ Di.reset $ handleEvent' eventHandlers data'     void $ observeHistogram time cacheUpdateHisto     pure res @@ -295,15 +332,6 @@       (time, _) <- timeA . catchAllLogging $ P.embed action       void $ observeHistogram time eventHandleHisto     Left err      -> debug $ "Failed handling actions for event: " +| err |+ ""---- NOTE: We have to be careful with how we run event handlers---       They're registered through `react` which ensures the value of `r` in the event handler---       is the same as the final value of `r`, but:---       because they're held inside a 'Dynamic' to prevent the value of `r` being recursive,---       we have to make sure that we don't accidentally try to execute the event handler inside a---       nested effect, ie: `P.runError $ {- Handle events here -}` since that will result the value of---       `r` where we handle events be: `(P.Error a ': r)`, which will make stuff explode when we unwrap the---       event handlers  handleEvent' :: BotC r               => EventHandlers
src/Calamity/Client/Types.hs view
@@ -8,6 +8,7 @@     , SetupEff     , ReactConstraints     , WaitUntilConstraints+    , WaitUntilMConstraints     , EventHandlers(..)     , InsertEventHandler(..)     , RemoveEventHandler(..)@@ -84,7 +85,21 @@  -- | Some constraints that 'Calamity.Client.Client.waitUntil' needs to work. Don't -- worry about these since they are satisfied for any type @s@ can be-type WaitUntilConstraints r s eh ehB t =+type WaitUntilConstraints r s eh check t =+  ( InsertEventHandler s+  , RemoveEventHandler s+  , Uncurry eh+  , eh ~ EHType s (P.Sem r) ()+  , eh ~ Curried (t -> P.Sem r ())+  , Uncurry check+  , Uncurried check ~ (t -> Bool)+  , Curry (t -> IO ())+  , Curried (t -> IO ()) ~ EHType s IO ()+  )++-- | Some constraints that 'Calamity.Client.Client.waitUntilM' needs to work. Don't+-- worry about these since they are satisfied for any type @s@ can be+type WaitUntilMConstraints r s eh ehB t =   ( InsertEventHandler s   , RemoveEventHandler s   , Uncurry eh
+ src/Calamity/Commands.hs view
@@ -0,0 +1,14 @@+-- | Calamity commands+-- This module only exports the DSL and core types for using commands+module Calamity.Commands+    ( module Calamity.Commands.Dsl+    , module Calamity.Commands.Error+    , module Calamity.Commands.Handler+    , module Calamity.Commands.ParsePrefix+    , module Calamity.Commands.Parser ) where++import           Calamity.Commands.Dsl+import           Calamity.Commands.Error+import           Calamity.Commands.Handler+import           Calamity.Commands.ParsePrefix+import           Calamity.Commands.Parser
+ src/Calamity/Commands/Check.hs view
@@ -0,0 +1,40 @@+-- | Command invokation preconditions+module Calamity.Commands.Check+    ( Check(..)+    , buildCheck+    , buildCheckPure+    , runCheck ) where++import {-# SOURCE #-} Calamity.Commands.Context+import           Calamity.Commands.Error+import           Calamity.Internal.RunIntoIO+import           Calamity.Internal.Utils++import           Control.Lens                hiding ( (<.>), Context )++import           Data.Generics.Labels        ()+import           Data.Maybe+import qualified Data.Text                   as S+import qualified Data.Text.Lazy              as L++import           GHC.Generics++import qualified Polysemy                    as P++data Check = MkCheck+  { name     :: S.Text+  , callback :: Context -> IO (Maybe L.Text)+  }+  deriving ( Generic )++buildCheck :: P.Member (P.Final IO) r => S.Text -> (Context -> P.Sem r (Maybe L.Text)) -> P.Sem r Check+buildCheck name cb = do+  cb' <- bindSemToIO cb+  let cb'' = fromMaybe (Just "failed internally") <.> cb'+  pure $ MkCheck name cb''++buildCheckPure :: S.Text -> (Context -> Maybe L.Text) -> Check+buildCheckPure name cb = MkCheck name (pure . cb)++runCheck :: P.Member (P.Embed IO) r => Context -> Check -> P.Sem r (Either CommandError ())+runCheck ctx chk = P.embed (callback chk ctx) <&> justToEither . (CheckError (chk ^. #name) <$>)
+ src/Calamity/Commands/Command.hs view
@@ -0,0 +1,43 @@+-- | Commands and stuff+module Calamity.Commands.Command+    ( Command(..) ) where++import           Calamity.Commands.Check+import           Calamity.Commands.Context+import           Calamity.Commands.Error+import           Calamity.Commands.Group++import           Control.Lens              hiding ( (<.>), Context )++import           Data.Text                 as S+import           Data.Text.Lazy            as L++import           GHC.Generics++import           TextShow+import qualified TextShow.Generic          as TSG++data Command = forall a. Command+  { name     :: S.Text+  , parent   :: Maybe Group+  , checks   :: [Check]+  , help     :: Context -> L.Text+  , parser   :: Context -> IO (Either CommandError a)+  , callback :: (Context, a) -> IO (Maybe L.Text)+  }++data CommandS = CommandS+  { name   :: S.Text+  , parent :: Maybe S.Text+  , checks :: [S.Text]+  }+  deriving ( Generic, Show )+  deriving ( TextShow ) via TSG.FromGeneric CommandS++instance Show Command where+  showsPrec d Command { name, parent, checks } = showsPrec d $ CommandS name (parent ^? _Just . #name)+    (checks ^.. traverse . #name)++instance TextShow Command where+  showbPrec d Command { name, parent, checks } = showbPrec d $ CommandS name (parent ^? _Just . #name)+    (checks ^.. traverse . #name)
+ src/Calamity/Commands/Command.hs-boot view
@@ -0,0 +1,11 @@+-- | Commands and stuff+module Calamity.Commands.Command+    ( Command+    ) where++import TextShow++data Command++instance Show Command+instance TextShow Command
+ src/Calamity/Commands/CommandUtils.hs view
@@ -0,0 +1,139 @@+-- | Command utilities+module Calamity.Commands.CommandUtils+    ( TypedCommandC+    , CommandForParsers+    , buildCommand+    , buildCommand'+    , buildParser+    , buildCallback+    , runCommand+    , invokeCommand ) where++import           Calamity.Commands.Check+import           Calamity.Commands.Command+import           Calamity.Commands.Context+import           Calamity.Commands.Error+import           Calamity.Commands.Group+import           Calamity.Commands.Parser+import           Calamity.Internal.RunIntoIO+import           Calamity.Internal.Utils++import           Control.Lens                hiding ( (<.>), Context )+import           Control.Monad++import           Data.Foldable+import           Data.Kind+import           Data.Maybe+import           Data.Text                   as S+import           Data.Text.Lazy              as L+import           Data.Typeable++import qualified Polysemy                    as P+import qualified Polysemy.Error              as P+import qualified Polysemy.Fail               as P++buildCommand' :: P.Member (P.Final IO) r+              => S.Text+              -> Maybe Group+              -> [Check]+              -> (Context -> L.Text)+              -> (Context -> P.Sem r (Either CommandError a))+              -> ((Context, a) -> P.Sem (P.Fail ': r) ())+              -> P.Sem r Command+buildCommand' name parent checks help parser cb = do+  cb' <- buildCallback cb+  parser' <- buildParser name parser+  pure $ Command name parent checks help parser' cb'++buildCommand :: forall ps a r.+             (P.Member (P.Final IO) r, TypedCommandC ps a r)+             => S.Text+             -> Maybe Group+             -> [Check]+             -> (Context -> L.Text)+             -> (Context -> CommandForParsers ps r)+             -> P.Sem r Command+buildCommand name parent checks help command = let (parser, cb) = buildTypedCommand @ps command+                                               in buildCommand' name parent checks help parser cb++buildParser :: P.Member (P.Final IO) r+            => S.Text+            -> (Context -> P.Sem r (Either CommandError a))+            -> P.Sem r (Context -> IO (Either CommandError a))+buildParser cmdName cb = do+  cb' <- bindSemToIO cb+  let cb'' ctx = fromMaybe (Left $ ParseError ("Parser for command: " <> cmdName) "failed internally") <$> cb' ctx+  pure cb''++buildCallback+  :: P.Member (P.Final IO) r => ((Context, a) -> P.Sem (P.Fail ': r) ()) -> P.Sem r ((Context, a) -> IO (Maybe L.Text))+buildCallback cb = do+  cb' <- bindSemToIO (\x -> P.runFail (cb x) <&> \case+                        Left e -> Just $ L.pack e+                        _      -> Nothing)+  let cb'' = fromMaybe (Just "failed internally") <.> cb'+  pure cb''++runCommand :: P.Member (P.Embed IO) r => Context -> Command -> P.Sem r (Either CommandError ())+runCommand ctx Command { name, parser, callback } = P.embed (parser ctx) >>= \case+  Left e   -> pure $ Left e+  Right p' -> P.embed (callback (ctx, p')) <&> justToEither . (InvokeError name <$>)++invokeCommand :: P.Member (P.Embed IO) r => Context -> Command -> P.Sem r (Either CommandError ())+invokeCommand ctx cmd@Command { checks } = P.runError $ do+  for_ checks (P.fromEither <=< runCheck ctx)+  P.fromEither =<< runCommand ctx cmd++type CommandSemType r = P.Sem (P.Fail ': r) ()++type TypedCommandC ps a r =+  ( ApplyTupRes a (CommandSemType r) ~ CommandForParsers ps r+  , a ~ ParamsFromParsers ps+  , BuildTypedCommandParser ps r+  , ApplyTup a (CommandSemType r))++buildTypedCommand+  :: forall (ps :: [Type]) a r.+  TypedCommandC ps a r+  => (Context -> CommandForParsers ps r)+  -> ( Context+         -> P.Sem r (Either CommandError a)+     , (Context, a)+         -> P.Sem (P.Fail ': r) ())+buildTypedCommand cmd = let parser ctx = buildTypedCommandParser @ps (ctx, ctx ^. #unparsedParams)+                            consumer (ctx, r) = applyTup (cmd ctx) r+                        in (parser, consumer)++class ApplyTup a b where+  type ApplyTupRes a b++  applyTup :: ApplyTupRes a b -> a -> b++instance ApplyTup as b => ApplyTup (a, as) b where+  type ApplyTupRes (a, as) b = a -> ApplyTupRes as b++  applyTup f (a, as) = applyTup (f a) as++instance ApplyTup () b where+  type ApplyTupRes () b = b++  applyTup r () = r++class BuildTypedCommandParser (ps :: [Type]) r where+  buildTypedCommandParser :: (Context, L.Text) -> P.Sem r (Either CommandError (ParamsFromParsers ps))++instance BuildTypedCommandParser '[] r where+  buildTypedCommandParser (_, _) = pure $ Right ()++instance (Typeable x, Parser x r, BuildTypedCommandParser xs r) => BuildTypedCommandParser (x ': xs) r where+  buildTypedCommandParser (ctx, msg) = parse @x (ctx, msg) >>= \case+    Right (a, msg') -> (a, ) <<$>> buildTypedCommandParser @xs (ctx, msg')+    Left e          -> pure $ Left (ParseError (S.pack . show . typeRep $ Proxy @x) e)++type family ParamsFromParsers (ps :: [Type]) where+  ParamsFromParsers '[] = ()+  ParamsFromParsers (x ': xs) = (ParserResult x, ParamsFromParsers xs)++type family CommandForParsers (ps :: [Type]) r where+  CommandForParsers '[] r = P.Sem (P.Fail ': r) ()+  CommandForParsers (x ': xs) r = ParserResult x -> CommandForParsers xs r
+ src/Calamity/Commands/Context.hs view
@@ -0,0 +1,34 @@+-- | Command invokation context+module Calamity.Commands.Context+    ( Context(..) ) where++import {-# SOURCE #-} Calamity.Commands.Command+import           Calamity.Types.Model.Channel+import           Calamity.Types.Model.Guild+import           Calamity.Types.Model.User+import           Calamity.Types.Snowflake+import           Calamity.Types.Tellable++import qualified Data.Text.Lazy               as L++import           GHC.Generics++import           TextShow+import qualified TextShow.Generic             as TSG++data Context = Context+  { message        :: Message+  , guild          :: Maybe Guild+  , member         :: Maybe Member+  , channel        :: Channel+  , user           :: User+  , command        :: Command+  , prefix         :: L.Text+  , unparsedParams :: L.Text+    -- ^ The message remaining after consuming the prefix+  }+  deriving ( Show, Generic )+  deriving ( TextShow ) via TSG.FromGeneric Context++instance Tellable Context where+  getChannel Context { channel } = pure . getID $ channel
+ src/Calamity/Commands/Context.hs-boot view
@@ -0,0 +1,10 @@+-- | Command invokation context+module Calamity.Commands.Context+    ( Context ) where++import           TextShow++data Context++instance Show Context+instance TextShow Context
+ src/Calamity/Commands/Dsl.hs view
@@ -0,0 +1,108 @@+{-# LANGUAGE RecursiveDo #-}++-- | A DSL for generating commands and groups+module Calamity.Commands.Dsl+    ( command'+    , command+    , help+    , requires+    , requires'+    , requiresPure+    , group+    , DSLState+    , raiseDSL ) where++import           Calamity.Commands.Check+import           Calamity.Commands.Command     hiding ( help )+import           Calamity.Commands.CommandUtils+import           Calamity.Commands.Context     hiding ( command )+import           Calamity.Commands.Error+import           Calamity.Commands.Group       hiding ( help )+import           Calamity.Commands.LocalWriter++import qualified Data.HashMap.Lazy             as LH+import qualified Data.Text                     as S+import qualified Data.Text.Lazy                as L++import qualified Polysemy                      as P+import qualified Polysemy.Fail                 as P+import qualified Polysemy.Fixpoint             as P+import qualified Polysemy.Reader               as P++type DSLState r = (LocalWriter (LH.HashMap S.Text Command) ':+                       LocalWriter (LH.HashMap S.Text Group) ':+                       P.Reader (Maybe Group) ':+                       P.Reader (Context -> L.Text) ':+                       P.Reader [Check] ':+                       P.Fixpoint ': r)++raiseDSL :: P.Sem r a -> P.Sem (DSLState r) a+raiseDSL = P.raise . P.raise . P.raise . P.raise . P.raise . P.raise++-- | Build a command with an already prepared invokation action+command'+  :: P.Member (P.Final IO) r+  => S.Text+  -> (Context -> P.Sem r (Either CommandError a))+  -> ((Context, a) -> P.Sem (P.Fail ': r) ())+  -> P.Sem (DSLState r) Command+command' name parser cb = do+  parent <- P.ask @(Maybe Group)+  checks <- P.ask @[Check]+  help' <- P.ask @(Context -> L.Text)+  cmd <- raiseDSL $ buildCommand' name parent checks help' parser cb+  ltell $ LH.singleton name cmd+  pure cmd++command :: forall ps a r.+        ( P.Member (P.Final IO) r,+          TypedCommandC ps a r)+        => S.Text+        -> (Context -> CommandForParsers ps r)+        -> P.Sem (DSLState r) Command+command name cmd = do+  parent <- P.ask @(Maybe Group)+  checks <- P.ask @[Check]+  help' <- P.ask @(Context -> L.Text)+  cmd' <- raiseDSL $ buildCommand @ps name parent checks help' cmd+  ltell $ LH.singleton name cmd'+  pure cmd'++help :: P.Member (P.Reader (Context -> L.Text)) r+     => (Context -> L.Text)+     -> P.Sem r a+     -> P.Sem r a+help = P.local . const++requires :: [Check]+         -> P.Sem (DSLState r) a+         -> P.Sem (DSLState r) a+requires = P.local . (<>)++requires' :: P.Member (P.Final IO) r+          => S.Text+          -> (Context -> P.Sem r (Maybe L.Text))+          -> P.Sem (DSLState r) a+          -> P.Sem (DSLState r) a+requires' name cb m = do+  check <- raiseDSL $ buildCheck name cb+  requires [check] m++requiresPure :: [(S.Text, Context -> Maybe L.Text)]+             -> P.Sem (DSLState r) a+             -> P.Sem (DSLState r) a+requiresPure checks = requires $ map (uncurry buildCheckPure) checks++group :: P.Member (P.Final IO) r+         => S.Text+         -> P.Sem (DSLState r) a+         -> P.Sem (DSLState r) a+group name m = mdo+  parent <- P.ask @(Maybe Group)+  checks <- P.ask @[Check]+  help'  <- P.ask @(Context -> L.Text)+  let group' = Group name parent commands children help' checks+  (children, (commands, res)) <- llisten @(LH.HashMap S.Text Group) $ llisten @(LH.HashMap S.Text Command) $ P.local (const $ Just group') m+  ltell $ LH.singleton name group'+  pure res+
+ src/Calamity/Commands/Error.hs view
@@ -0,0 +1,21 @@+-- | Command errors+module Calamity.Commands.Error+    ( CommandError(..) ) where++import qualified Data.Text        as S+import qualified Data.Text.Lazy   as L++import           GHC.Generics++import           TextShow+import qualified TextShow.Generic as TSG++data CommandError+  = ParseError S.Text -- ^ The type of the parser+               L.Text -- ^ The reason that parsing failed+  | CheckError S.Text -- ^ The name of the check that failed+               L.Text -- ^ The reason for the check failing+  | InvokeError S.Text -- ^ The name of the command that failed+                L.Text -- ^ The reason for failing+  deriving ( Show, Generic )+  deriving ( TextShow ) via TSG.FromGeneric CommandError
+ src/Calamity/Commands/Group.hs view
@@ -0,0 +1,23 @@+-- | Command groups+module Calamity.Commands.Group+    ( Group(..) ) where++import           Calamity.Commands.Check+import {-# SOURCE #-} Calamity.Commands.Command+import {-# SOURCE #-} Calamity.Commands.Context++import qualified Data.HashMap.Lazy         as LH+import qualified Data.Text                 as S+import qualified Data.Text.Lazy            as L++import           GHC.Generics++data Group = Group+  { name     :: S.Text+  , parent   :: Maybe Group+  , commands :: LH.HashMap S.Text Command+  , children :: LH.HashMap S.Text Group+  , help     :: Context -> L.Text+  , checks   :: [Check]+  }+  deriving ( Generic )
+ src/Calamity/Commands/Handler.hs view
@@ -0,0 +1,129 @@+-- | A command handler+module Calamity.Commands.Handler+    ( CommandHandler(..)+    , addCommands+    , buildCommands+    , buildContext ) where++import           Calamity.Cache.Eff+import           Calamity.Client.Client+import           Calamity.Client.Types+import           Calamity.Commands.Command+import           Calamity.Commands.CommandUtils+import           Calamity.Commands.Context+import           Calamity.Commands.Dsl+import           Calamity.Commands.Error+import           Calamity.Commands.Group+import           Calamity.Commands.LocalWriter+import           Calamity.Commands.ParsePrefix+import           Calamity.Internal.Utils+import           Calamity.Types.Model.Channel+import           Calamity.Types.Model.User+import           Calamity.Types.Snowflake++import           Control.Lens                   hiding ( Context )+import           Control.Monad++import           Data.Char                      ( isSpace )+import qualified Data.HashMap.Lazy              as LH+import qualified Data.Text                      as S+import qualified Data.Text.Lazy                 as L++import           GHC.Generics++import qualified Polysemy                       as P+import qualified Polysemy.Error                 as P+import qualified Polysemy.Fail                  as P+import qualified Polysemy.Fixpoint              as P+import qualified Polysemy.Reader                as P++data CommandHandler = CommandHandler+  { groups   :: LH.HashMap S.Text Group+    -- ^ Top level groups+  , commands :: LH.HashMap S.Text Command+    -- ^ Top level commands+  }+  deriving ( Generic )++mapLeft :: (e -> e') -> Either e a -> Either e' a+mapLeft f (Left x)  = Left $ f x+mapLeft _ (Right x) = Right x++data FailReason+  = NoPrefix+  | NoCtx+  | NF [L.Text]+  | ERR Context CommandError++addCommands :: (BotC r, P.Member ParsePrefix r) => P.Sem (DSLState r) a -> P.Sem r (P.Sem r (), CommandHandler, a)+addCommands m = do+  (handler, res) <- buildCommands m+  remove <- react @'MessageCreateEvt $ \msg -> do+    err <- P.runError $ do+      (prefix, rest) <- P.note NoPrefix =<< parsePrefix msg+      (command, unparsedParams) <- P.fromEither $ mapLeft NF $ findCommand handler rest+      ctx <- P.note NoCtx =<< buildContext msg prefix command unparsedParams+      P.fromEither . mapLeft (ERR ctx) =<< invokeCommand ctx (ctx ^. #command)+      pure ctx+    case err of+      Left (ERR ctx e) -> fire $ customEvt @"command-error" (ctx, e)+      Left (NF path)   -> fire $ customEvt @"command-not-found" path+      Left _           -> pure () -- "ignore if no prefix or if context couldn't be built"+      Right ctx        -> fire $ customEvt @"command-run" ctx+  pure (remove, handler, res)+++buildCommands :: P.Member (P.Final IO) r+              => P.Sem (DSLState r) a+              -> P.Sem r (CommandHandler, a)+buildCommands =+  ((\(groups, (cmds, a)) -> (CommandHandler groups cmds, a)) <$>) .+  P.fixpointToFinal .+  P.runReader [] .+  P.runReader (const "This command or group has no help.") .+  P.runReader Nothing .+  runLocalWriter @(LH.HashMap S.Text Group) .+  runLocalWriter @(LH.HashMap S.Text Command)++buildContext :: BotC r => Message -> L.Text -> Command -> L.Text -> P.Sem r (Maybe Context)+buildContext msg prefix command unparsed = (rightToMaybe <$>) . P.runFail $ do+  guild <- join <$> getGuild `traverse` (msg ^. #guildID)+  let member = guild ^? _Just . #members . ix (coerceSnowflake $ getID @User msg)+  let gchan = guild ^? _Just . #channels . ix (coerceSnowflake $ getID @Channel msg)+  Just channel <- case gchan of+    Just chan -> pure . pure $ GuildChannel' chan+    _         -> DMChannel' <<$>> getDM (coerceSnowflake $ getID @Channel msg)+  Just user <- getUser $ getID msg++  pure $ Context msg guild member channel user command prefix unparsed++nextWord :: L.Text -> (L.Text, L.Text)+nextWord = L.break isSpace . L.stripStart++firstEither :: Either e a -> Either e a -> Either e a+firstEither (Right l) _ = Right l+firstEither l (Left _)  = l+firstEither _ r         = r++findCommand :: CommandHandler -> L.Text -> Either [L.Text] (Command, L.Text)+findCommand handler msg = goH $ nextWord msg+  where+    goH :: (L.Text, L.Text) -> Either [L.Text] (Command, L.Text)+    goH ("", _) = Left []+    goH (x, xs) = attachSoFar x+      (((, xs) <$> attachInitial (LH.lookup (L.toStrict x) (handler ^. #commands)))+       `firstEither` (attachInitial (LH.lookup (L.toStrict x) (handler ^. #groups)) >>= goG (nextWord xs)))++    goG :: (L.Text, L.Text) -> Group -> Either [L.Text] (Command, L.Text)+    goG ("", _) _ = Left []+    goG (x, xs) g = attachSoFar x+      (((, xs) <$> attachInitial (LH.lookup (L.toStrict x) (g ^. #commands)))+       `firstEither` (attachInitial (LH.lookup (L.toStrict x) (g ^. #children)) >>= goG (nextWord xs)))++    attachInitial :: Maybe a -> Either [L.Text] a+    attachInitial (Just a) = Right a+    attachInitial Nothing = Left []++    attachSoFar :: L.Text -> Either [L.Text] a -> Either [L.Text] a+    attachSoFar cmd (Left xs) = Left (cmd:xs)+    attachSoFar _ r = r
+ src/Calamity/Commands/LocalWriter.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE TemplateHaskell #-}++-- | A Writer monad that supports local writing, reverse reader I guess?+module Calamity.Commands.LocalWriter+    ( LocalWriter(..)+    , ltell+    , llisten+    , runLocalWriter ) where++import qualified Polysemy       as P+import qualified Polysemy.State as P++data LocalWriter o m a where+  Ltell :: o -> LocalWriter o m ()+  Llisten :: m a -> LocalWriter o m (o, a)++P.makeSem ''LocalWriter++runLocalWriter :: Monoid o => P.Sem (LocalWriter o ': r) a -> P.Sem r (o, a)+runLocalWriter = P.runState mempty . P.reinterpretH+  (\case+     Ltell o   -> do+       P.modify' (<> o) >>= P.pureT+     Llisten m -> do+       mm <- P.runT m+       (o, fa) <- P.raise $ runLocalWriter mm+       pure $ fmap (o, ) fa)
+ src/Calamity/Commands/ParsePrefix.hs view
@@ -0,0 +1,24 @@+{-# LANGUAGE TemplateHaskell #-}++-- | Command prefix parsing effect+module Calamity.Commands.ParsePrefix+    ( ParsePrefix(..)+    , parsePrefix+    , useConstantPrefix ) where++import           Calamity.Types.Model.Channel.Message++import           Control.Lens++import qualified Data.Text.Lazy                       as L++import qualified Polysemy                             as P++data ParsePrefix m a where+  ParsePrefix :: Message -> ParsePrefix m (Maybe (L.Text, L.Text))++P.makeSem ''ParsePrefix++useConstantPrefix :: L.Text -> P.Sem (ParsePrefix ': r) a -> P.Sem r a+useConstantPrefix pre = P.interpret (\case+                                       ParsePrefix m -> pure ((pre, ) <$> L.stripPrefix pre (m ^. #content)))
+ src/Calamity/Commands/Parser.hs view
@@ -0,0 +1,154 @@+-- | Something that can parse user input+module Calamity.Commands.Parser+    ( Parser(..)+    , KleeneConcat ) where++import           Calamity.Cache.Eff+import           Calamity.Commands.Context+import           Calamity.Internal.Utils+import           Calamity.Types.Model.Channel ( Channel )+import           Calamity.Types.Model.Guild   ( Emoji, Member, Role )+import           Calamity.Types.Model.User    ( User )+import           Calamity.Types.Snowflake++import           Control.Lens                 hiding ( Context )+import           Control.Monad++import           Data.Bifunctor+import           Data.Char                    ( isSpace )+import           Data.Kind+import           Data.List.NonEmpty           ( NonEmpty, nonEmpty )+import qualified Data.Text.Lazy               as L+import           Data.Text.Lazy               ( Text )+import           Data.Typeable++import qualified Polysemy                     as P++import           Text.Megaparsec              hiding ( parse )+import           Text.Megaparsec.Char++import           TextShow++class Parser (a :: Type) r where+  type ParserResult a++  type ParserResult a = a++  parse :: (Context, Text) -> P.Sem r (Either Text (ParserResult a, Text))++instance Parser Text r where+  parse (_ctx, msg) = pure $ runParserToCommandError item msg++instance Parser a r => Parser [a] r where+  type ParserResult [a] = [ParserResult a]++  parse (ctx, msg) = Right <$> go msg []+    where+      go :: Text -> [ParserResult a] -> P.Sem r ([ParserResult a], Text)+      go t l = parse @a (ctx, t) >>= \case+        Left _        -> pure (l, t)+        Right (v, t') -> go t' (l <> [v])++instance (Parser a r, Typeable a) => Parser (NonEmpty a) r where+  type ParserResult (NonEmpty a) = NonEmpty (ParserResult a)++  parse (ctx, msg) = parse @[a] (ctx, msg)+    <&> (\case+           Right (res, rest) -> case nonEmpty res of+             Just res' -> Right (res', rest)+             Nothing   -> Left ("Couldn't parse at least one of " <> (L.pack . show . typeRep $ Proxy @a))+           Left e            -> Left e)++data KleeneConcat a++instance (Monoid (ParserResult a), Parser a r) => Parser (KleeneConcat a) r where+  type ParserResult (KleeneConcat a) = ParserResult a++  parse (ctx, msg) = (first mconcat) <<$>> parse @[a] (ctx, msg)++instance {-# OVERLAPS #-}Parser (KleeneConcat Text) r where+  type ParserResult (KleeneConcat Text) = ParserResult Text++  -- consume rest on text just takes everything remaining+  parse (_ctx, msg) = pure $ runParserToCommandError (someSingle) msg++instance Parser (Snowflake a) r where+  parse (_ctx, msg) = pure $ runParserToCommandError snowflake msg++instance {-# OVERLAPS #-}Parser (Snowflake User) r where+  parse (_ctx, msg) = pure $ runParserToCommandError (try (ping "@") <|> snowflake) msg++instance {-# OVERLAPS #-}Parser (Snowflake Member) r where+  parse (_ctx, msg) = pure $ runParserToCommandError (try (ping "@") <|> snowflake) msg++instance {-# OVERLAPS #-}Parser (Snowflake Channel) r where+  parse (_ctx, msg) = pure $ runParserToCommandError (try (ping "#") <|> snowflake) msg++instance {-# OVERLAPS #-}Parser (Snowflake Role) r where+  parse (_ctx, msg) = pure $ runParserToCommandError (try (ping "@&") <|> snowflake) msg++instance {-# OVERLAPS #-}Parser (Snowflake Emoji) r where+  parse (_ctx, msg) = pure $runParserToCommandError+    (try emoji <|> snowflake) msg+++instance Parser Member r where+  parse (ctx, msg) = parse @(Snowflake Member) (ctx, msg)+    <&> (>>= \(mid, rest) -> case ctx ^? #guild . _Just . #members . ix mid of+           Just member -> Right (member, rest)+           _           -> Left ("Couldn't find member with id: " <> showtl mid))++instance P.Member CacheEff r => Parser User r where+  parse (ctx, msg) = do+    r <- parse @(Snowflake User) (ctx, msg)+    case r of+      Right (uid, rest) -> getUser uid <&> \case+        Just member -> Right (member, rest)+        _           -> Left ("Couldn't find user with id: " <> showtl uid)+      Left e            -> pure $ Left e++instance ShowErrorComponent Text where+  showErrorComponent = L.unpack+  errorComponentLen = fromIntegral . L.length++runParserToCommandError :: Parsec Text Text a -> Text -> Either Text (a, Text)+runParserToCommandError m t = case runParser (space *> andRemaining m) "" t of+  Right a -> Right a+  Left s  -> Left . L.pack . errorBundlePretty $ s++ping :: MonadParsec e Text m => Text -> m (Snowflake a)+ping c = chunk ("<" <> c) *> optional (chunk "!") *> snowflake <* chunk ">"++ping' :: MonadParsec e Text m => m () -> m (Snowflake a)+ping' m = chunk "<" *> m *> snowflake <* chunk ">"++snowflake :: MonadParsec e Text m => m (Snowflake a)+snowflake = (Snowflake . read) <$> some digitChar++emoji :: MonadParsec e Text m => m (Snowflake a)+emoji = ping' (optional (chunk "a") *> between (chunk ":") (chunk ":") (void $ takeWhileP Nothing $ not . (== ':')))++andRemaining :: MonadParsec e s m => m a -> m (a, Tokens s)+andRemaining m = do+  a <- m+  rest <- manySingle+  pure (a, rest)++item :: MonadParsec e Text m => m Text+item = try quotedString <|> someNonWS++manySingle :: MonadParsec e s m => m (Tokens s)+manySingle = takeWhileP (Just "Any character") (const True)++someSingle :: MonadParsec e s m => m (Tokens s)+someSingle = takeWhile1P (Just "Any character") (const True)++quotedString :: MonadParsec e Text m => m Text+quotedString = try (between (chunk "'") (chunk "'") (takeWhileP Nothing $ not . (== '\''))) <|>+               between (chunk "\"") (chunk "\"") (takeWhileP Nothing $ not . (== '"'))++-- manyNonWS :: (Token s ~ Char, MonadParsec e s m) => m (Tokens s)+-- manyNonWS = takeWhileP (Just "Any Non-Whitespace") (not . isSpace)++someNonWS :: (Token s ~ Char, MonadParsec e s m) => m (Tokens s)+someNonWS = takeWhile1P (Just "Any Non-Whitespace") (not . isSpace)
src/Calamity/Internal/RunIntoIO.hs view
@@ -4,23 +4,28 @@ module Calamity.Internal.RunIntoIO     ( IntoIO(..)     , runIntoIOFinal-    , intoIO ) where--import           Control.Monad+    , intoIO+    , bindSemToIO ) where  import           Data.Functor  import qualified Polysemy                         as P import qualified Polysemy.Final                   as P -data IntoIO p m a where-  IntoIO :: (p -> m ()) -> IntoIO p m (p -> IO ())+data IntoIO p r m a where+  IntoIO :: (p -> m r) -> IntoIO p r m (p -> IO (Maybe r)) -runIntoIOFinal :: forall r p a. P.Member (P.Final IO) r => P.Sem (IntoIO p ': r) a -> P.Sem r a+runIntoIOFinal :: forall r p b a. P.Member (P.Final IO) r => P.Sem (IntoIO p b ': r) a -> P.Sem r a runIntoIOFinal = P.interpretFinal $ \case   IntoIO m -> do     istate <- P.getInitialStateS     m' <- P.bindS m-    P.liftS $ pure (\x -> void (m' $ istate $> x))+    ins <- P.getInspectorS+    P.liftS $ pure (\x -> P.inspect ins <$> m' (istate $> x))  P.makeSem ''IntoIO++bindSemToIO :: forall r p a. P.Member (P.Final IO) r => (p -> P.Sem r a) -> P.Sem r (p -> IO (Maybe a))+bindSemToIO f = runIntoIOFinal go+  where go :: P.Sem (IntoIO p a ': r) (p -> IO (Maybe a))+        go = intoIO (P.raise . f)
src/Calamity/Internal/Utils.hs view
@@ -6,7 +6,12 @@     , whenM     , unlessM     , lastMaybe+    , leftToMaybe+    , rightToMaybe+    , justToEither     , (<<$>>)+    , (<<*>>)+    , (<.>)     , debug     , info     , Calamity.Internal.Utils.error@@ -14,18 +19,20 @@  import           Calamity.Types.LogEff +import           Control.Applicative+ import           Data.Default.Class-import qualified Data.HashMap.Lazy   as LH-import qualified Data.Map            as M-import           Data.Semigroup      ( Last(..) )+import qualified Data.HashMap.Lazy     as LH+import qualified Data.Map              as M+import           Data.Semigroup        ( Last(..) ) import           Data.Text.Lazy import           Data.Time-import qualified Data.Vector.Unboxed as VU-import           Data.Vector.Unboxed ( Vector )+import qualified Data.Vector.Unboxed   as VU+import           Data.Vector.Unboxed   ( Vector ) -import qualified DiPolysemy          as Di+import qualified DiPolysemy            as Di -import qualified Polysemy            as P+import qualified Polysemy              as P  import           TextShow @@ -43,11 +50,33 @@ lastMaybe :: Maybe a -> Maybe a -> Maybe a lastMaybe l r = getLast <$> fmap Last l <> fmap Last r +leftToMaybe :: Either e a -> Maybe e+leftToMaybe (Left x) = Just x+leftToMaybe _        = Nothing++rightToMaybe :: Either e a -> Maybe a+rightToMaybe (Right x) = Just x+rightToMaybe _         = Nothing++justToEither :: Maybe e -> Either e ()+justToEither (Just x) = Left x+justToEither _        = Right ()+ (<<$>>) :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b) (<<$>>) = fmap . fmap  infixl 4 <<$>> +(<<*>>) :: (Applicative f, Applicative g) => f (g (a -> b)) -> f (g a) -> f (g b)+(<<*>>) = liftA2 (<*>)++infixl 4 <<*>>++(<.>) :: Functor f => (a -> b) -> (c -> f a) -> (c -> f b)+(<.>) f g x = f <$> g x++infixl 4 <.>+ debug :: P.Member LogEff r => Text -> P.Sem r () debug = Di.debug @@ -66,7 +95,6 @@ instance (TextShow a, VU.Unbox a) => TextShow (Vector a) where   showb = showbList . VU.toList -    -- lazy and strict use the same internal data structure instance (Show k, Show v) => TextShow (LH.HashMap k v) where   showb = fromString . show 
src/Calamity/Types/Tellable.hs view
@@ -17,7 +17,8 @@ import           Data.ByteString.Lazy         ( ByteString ) import           Data.Default.Class import           Data.Monoid-import           Data.Text                    ( Text )+import qualified Data.Text                    as S+import qualified Data.Text.Lazy               as L  import           GHC.Generics @@ -33,16 +34,24 @@ -- Can be used to compose text, embeds, and files. /e.g./ -- -- @--- 'intoMsg' @'Text' "A message" '<>' 'intoMsg' @'Embed' ('def' '&' #description '?~' "Embed description")+-- 'intoMsg' @'L.Text' "A message" '<>' 'intoMsg' @'Embed' ('def' '&' #description '?~' "Embed description") -- @ class ToMessage a where   -- | Turn @a@ into a 'CreateMessageOptions' builder   intoMsg :: a -> Endo CreateMessageOptions  -- | Message content, '(<>)' concatenates the content-instance ToMessage Text where+instance ToMessage L.Text where+  intoMsg t = Endo (#content %~ (<> Just (L.toStrict t)))++-- | Message content, '(<>)' concatenates the content+instance ToMessage S.Text where   intoMsg t = Endo (#content %~ (<> Just t)) +-- | Message content, '(<>)' concatenates the content+instance ToMessage String where+  intoMsg t = Endo (#content %~ (<> Just (S.pack t)))+ -- | Message embed, '(<>)' merges embeds using '(<>)' instance ToMessage Embed where   intoMsg e = Endo (#embed %~ (<> Just e))@@ -85,6 +94,12 @@   P.fromEither r  instance Tellable DMChannel where+  getChannel = pure . getID++instance Tellable (Snowflake Channel) where+  getChannel = pure++instance Tellable Channel where   getChannel = pure . getID  instance Tellable (Snowflake DMChannel) where