packages feed

calamity 0.1.19.2 → 0.1.20.0

raw patch · 8 files changed

+137/−85 lines, 8 filesdep +di-coredep ~di-polysemyPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: di-core

Dependency ranges changed: di-polysemy

API changes (from Hackage documentation)

- Calamity.Internal.Utils: instance (GHC.Show.Show k, GHC.Show.Show v) => TextShow.Classes.TextShow (Data.HashMap.Base.HashMap k v)
+ Calamity.Client.Types: [$sel:initialDi:Client] :: Client -> Maybe (Di Level Path Message)
+ Calamity.Internal.Utils: instance (GHC.Show.Show k, GHC.Show.Show v) => TextShow.Classes.TextShow (Data.HashMap.Internal.HashMap k v)
+ Calamity.Internal.Utils: untilJustFinalIO :: Member (Final IO) r => Sem r (Maybe a) -> Sem r a
+ Calamity.Internal.Utils: whileMFinalIO :: Member (Final IO) r => Sem r Bool -> Sem r ()
- Calamity.Cache.Eff: delDM :: forall r_a9zV1. MemberWithError CacheEff r_a9zV1 => Snowflake DMChannel -> Sem r_a9zV1 ()
+ Calamity.Cache.Eff: delDM :: forall r_a9AjK. MemberWithError CacheEff r_a9AjK => Snowflake DMChannel -> Sem r_a9AjK ()
- Calamity.Cache.Eff: delGuild :: forall r_a9zUU. MemberWithError CacheEff r_a9zUU => Snowflake Guild -> Sem r_a9zUU ()
+ Calamity.Cache.Eff: delGuild :: forall r_a9AjD. MemberWithError CacheEff r_a9AjD => Snowflake Guild -> Sem r_a9AjD ()
- Calamity.Cache.Eff: delMessage :: forall r_a9zVm. MemberWithError CacheEff r_a9zVm => Snowflake Message -> Sem r_a9zVm ()
+ Calamity.Cache.Eff: delMessage :: forall r_a9Ak5. MemberWithError CacheEff r_a9Ak5 => Snowflake Message -> Sem r_a9Ak5 ()
- Calamity.Cache.Eff: delUnavailableGuild :: forall r_a9zVf. MemberWithError CacheEff r_a9zVf => Snowflake Guild -> Sem r_a9zVf ()
+ Calamity.Cache.Eff: delUnavailableGuild :: forall r_a9AjY. MemberWithError CacheEff r_a9AjY => Snowflake Guild -> Sem r_a9AjY ()
- Calamity.Cache.Eff: delUser :: forall r_a9zV8. MemberWithError CacheEff r_a9zV8 => Snowflake User -> Sem r_a9zV8 ()
+ Calamity.Cache.Eff: delUser :: forall r_a9AjR. MemberWithError CacheEff r_a9AjR => Snowflake User -> Sem r_a9AjR ()
- Calamity.Cache.Eff: getBotUser :: forall r_a9zUM. MemberWithError CacheEff r_a9zUM => Sem r_a9zUM (Maybe User)
+ Calamity.Cache.Eff: getBotUser :: forall r_a9Ajv. MemberWithError CacheEff r_a9Ajv => Sem r_a9Ajv (Maybe User)
- Calamity.Cache.Eff: getDM :: forall r_a9zUY. MemberWithError CacheEff r_a9zUY => Snowflake DMChannel -> Sem r_a9zUY (Maybe DMChannel)
+ Calamity.Cache.Eff: getDM :: forall r_a9AjH. MemberWithError CacheEff r_a9AjH => Snowflake DMChannel -> Sem r_a9AjH (Maybe DMChannel)
- Calamity.Cache.Eff: getDMs :: forall r_a9zV0. MemberWithError CacheEff r_a9zV0 => Sem r_a9zV0 [DMChannel]
+ Calamity.Cache.Eff: getDMs :: forall r_a9AjJ. MemberWithError CacheEff r_a9AjJ => Sem r_a9AjJ [DMChannel]
- Calamity.Cache.Eff: getGuild :: forall r_a9zUP. MemberWithError CacheEff r_a9zUP => Snowflake Guild -> Sem r_a9zUP (Maybe Guild)
+ Calamity.Cache.Eff: getGuild :: forall r_a9Ajy. MemberWithError CacheEff r_a9Ajy => Snowflake Guild -> Sem r_a9Ajy (Maybe Guild)
- Calamity.Cache.Eff: getGuildChannel :: forall r_a9zUR. MemberWithError CacheEff r_a9zUR => Snowflake GuildChannel -> Sem r_a9zUR (Maybe GuildChannel)
+ Calamity.Cache.Eff: getGuildChannel :: forall r_a9AjA. MemberWithError CacheEff r_a9AjA => Snowflake GuildChannel -> Sem r_a9AjA (Maybe GuildChannel)
- Calamity.Cache.Eff: getGuilds :: forall r_a9zUT. MemberWithError CacheEff r_a9zUT => Sem r_a9zUT [Guild]
+ Calamity.Cache.Eff: getGuilds :: forall r_a9AjC. MemberWithError CacheEff r_a9AjC => Sem r_a9AjC [Guild]
- Calamity.Cache.Eff: getMessage :: forall r_a9zVj. MemberWithError CacheEff r_a9zVj => Snowflake Message -> Sem r_a9zVj (Maybe Message)
+ Calamity.Cache.Eff: getMessage :: forall r_a9Ak2. MemberWithError CacheEff r_a9Ak2 => Snowflake Message -> Sem r_a9Ak2 (Maybe Message)
- Calamity.Cache.Eff: getMessages :: forall r_a9zVl. MemberWithError CacheEff r_a9zVl => Sem r_a9zVl [Message]
+ Calamity.Cache.Eff: getMessages :: forall r_a9Ak4. MemberWithError CacheEff r_a9Ak4 => Sem r_a9Ak4 [Message]
- Calamity.Cache.Eff: getUnavailableGuilds :: forall r_a9zVe. MemberWithError CacheEff r_a9zVe => Sem r_a9zVe [Snowflake Guild]
+ Calamity.Cache.Eff: getUnavailableGuilds :: forall r_a9AjX. MemberWithError CacheEff r_a9AjX => Sem r_a9AjX [Snowflake Guild]
- Calamity.Cache.Eff: getUser :: forall r_a9zV5. MemberWithError CacheEff r_a9zV5 => Snowflake User -> Sem r_a9zV5 (Maybe User)
+ Calamity.Cache.Eff: getUser :: forall r_a9AjO. MemberWithError CacheEff r_a9AjO => Snowflake User -> Sem r_a9AjO (Maybe User)
- Calamity.Cache.Eff: getUsers :: forall r_a9zV7. MemberWithError CacheEff r_a9zV7 => Sem r_a9zV7 [User]
+ Calamity.Cache.Eff: getUsers :: forall r_a9AjQ. MemberWithError CacheEff r_a9AjQ => Sem r_a9AjQ [User]
- Calamity.Cache.Eff: isUnavailableGuild :: forall r_a9zVc. MemberWithError CacheEff r_a9zVc => Snowflake Guild -> Sem r_a9zVc Bool
+ Calamity.Cache.Eff: isUnavailableGuild :: forall r_a9AjV. MemberWithError CacheEff r_a9AjV => Snowflake Guild -> Sem r_a9AjV Bool
- Calamity.Cache.Eff: setBotUser :: forall r_a9zUK. MemberWithError CacheEff r_a9zUK => User -> Sem r_a9zUK ()
+ Calamity.Cache.Eff: setBotUser :: forall r_a9Ajt. MemberWithError CacheEff r_a9Ajt => User -> Sem r_a9Ajt ()
- Calamity.Cache.Eff: setDM :: forall r_a9zUW. MemberWithError CacheEff r_a9zUW => DMChannel -> Sem r_a9zUW ()
+ Calamity.Cache.Eff: setDM :: forall r_a9AjF. MemberWithError CacheEff r_a9AjF => DMChannel -> Sem r_a9AjF ()
- Calamity.Cache.Eff: setGuild :: forall r_a9zUN. MemberWithError CacheEff r_a9zUN => Guild -> Sem r_a9zUN ()
+ Calamity.Cache.Eff: setGuild :: forall r_a9Ajw. MemberWithError CacheEff r_a9Ajw => Guild -> Sem r_a9Ajw ()
- Calamity.Cache.Eff: setMessage :: forall r_a9zVh. MemberWithError CacheEff r_a9zVh => Message -> Sem r_a9zVh ()
+ Calamity.Cache.Eff: setMessage :: forall r_a9Ak0. MemberWithError CacheEff r_a9Ak0 => Message -> Sem r_a9Ak0 ()
- Calamity.Cache.Eff: setUnavailableGuild :: forall r_a9zVa. MemberWithError CacheEff r_a9zVa => Snowflake Guild -> Sem r_a9zVa ()
+ Calamity.Cache.Eff: setUnavailableGuild :: forall r_a9AjT. MemberWithError CacheEff r_a9AjT => Snowflake Guild -> Sem r_a9AjT ()
- Calamity.Cache.Eff: setUser :: forall r_a9zV3. MemberWithError CacheEff r_a9zV3 => User -> Sem r_a9zV3 ()
+ Calamity.Cache.Eff: setUser :: forall r_a9AjM. MemberWithError CacheEff r_a9AjM => User -> Sem r_a9AjM ()
- Calamity.Client.Client: runBotIO :: forall r a. (Members '[Embed IO, Final IO, CacheEff, MetricEff] r, Typeable (SetupEff r)) => Token -> Sem (SetupEff r) a -> Sem r (Maybe StartupError)
+ Calamity.Client.Client: runBotIO :: forall r a. (Members '[Embed IO, Final IO, CacheEff, MetricEff, LogEff] r, Typeable (SetupEff r)) => Token -> Sem (SetupEff r) a -> Sem r (Maybe StartupError)
- Calamity.Client.Client: runBotIO' :: forall r a. (Members '[Embed IO, Final IO, CacheEff, MetricEff] r, Typeable (SetupEff r)) => Token -> Maybe Session -> Maybe StatusUpdateData -> Maybe Intents -> Sem (SetupEff r) a -> Sem r (Maybe StartupError)
+ Calamity.Client.Client: runBotIO' :: forall r a. (Members '[Embed IO, Final IO, CacheEff, MetricEff, LogEff] r, Typeable (SetupEff r)) => Token -> Maybe Session -> Maybe StatusUpdateData -> Maybe Intents -> Sem (SetupEff r) a -> Sem r (Maybe StartupError)
- Calamity.Client.Types: Client :: TVar [(InChan ControlMessage, Async (Maybe ()))] -> MVar Int -> Token -> RateLimitState -> InChan CalamityEvent -> OutChan CalamityEvent -> IORef Integer -> Session -> Client
+ Calamity.Client.Types: Client :: TVar [(InChan ControlMessage, Async (Maybe ()))] -> MVar Int -> Token -> RateLimitState -> InChan CalamityEvent -> OutChan CalamityEvent -> IORef Integer -> Session -> Maybe (Di Level Path Message) -> Client
- Calamity.Client.Types: type SetupEff r = (LogEff : Reader Client : AtomicState EventHandlers : Async : r)
+ Calamity.Client.Types: type SetupEff r = (Reader Client : AtomicState EventHandlers : Async : r)
- Calamity.Commands.ParsePrefix: parsePrefix :: forall r_a7bCT. MemberWithError ParsePrefix r_a7bCT => Message -> Sem r_a7bCT (Maybe (Text, Text))
+ Calamity.Commands.ParsePrefix: parsePrefix :: forall r_a7c1C. MemberWithError ParsePrefix r_a7c1C => Message -> Sem r_a7c1C (Maybe (Text, Text))
- Calamity.Internal.LocalWriter: llisten :: forall o_aEHK r_aEXK a_XEHK. MemberWithError (LocalWriter o_aEHK) r_aEXK => Sem r_aEXK a_XEHK -> Sem r_aEXK (o_aEHK, a_XEHK)
+ Calamity.Internal.LocalWriter: llisten :: forall o_aEHX r_aEXX a_XEHX. MemberWithError (LocalWriter o_aEHX) r_aEXX => Sem r_aEXX a_XEHX -> Sem r_aEXX (o_aEHX, a_XEHX)
- Calamity.Internal.LocalWriter: ltell :: forall o_aEHG r_aEXI. MemberWithError (LocalWriter o_aEHG) r_aEXI => o_aEHG -> Sem r_aEXI ()
+ Calamity.Internal.LocalWriter: ltell :: forall o_aEHT r_aEXV. MemberWithError (LocalWriter o_aEHT) r_aEXV => o_aEHT -> Sem r_aEXV ()
- Calamity.Metrics.Eff: addCounter :: forall r_aKi6. MemberWithError MetricEff r_aKi6 => Int -> Counter -> Sem r_aKi6 Int
+ Calamity.Metrics.Eff: addCounter :: forall r_aKIm. MemberWithError MetricEff r_aKIm => Int -> Counter -> Sem r_aKIm Int
- Calamity.Metrics.Eff: modifyGauge :: forall r_aKi9. MemberWithError MetricEff r_aKi9 => (Double -> Double) -> Gauge -> Sem r_aKi9 Double
+ Calamity.Metrics.Eff: modifyGauge :: forall r_aKIp. MemberWithError MetricEff r_aKIp => (Double -> Double) -> Gauge -> Sem r_aKIp Double
- Calamity.Metrics.Eff: observeHistogram :: forall r_aKic. MemberWithError MetricEff r_aKic => Double -> Histogram -> Sem r_aKic HistogramSample
+ Calamity.Metrics.Eff: observeHistogram :: forall r_aKIs. MemberWithError MetricEff r_aKIs => Double -> Histogram -> Sem r_aKIs HistogramSample
- Calamity.Metrics.Eff: registerCounter :: forall r_aKhW. MemberWithError MetricEff r_aKhW => Text -> [(Text, Text)] -> Sem r_aKhW Counter
+ Calamity.Metrics.Eff: registerCounter :: forall r_aKIc. MemberWithError MetricEff r_aKIc => Text -> [(Text, Text)] -> Sem r_aKIc Counter
- Calamity.Metrics.Eff: registerGauge :: forall r_aKhZ. MemberWithError MetricEff r_aKhZ => Text -> [(Text, Text)] -> Sem r_aKhZ Gauge
+ Calamity.Metrics.Eff: registerGauge :: forall r_aKIf. MemberWithError MetricEff r_aKIf => Text -> [(Text, Text)] -> Sem r_aKIf Gauge
- Calamity.Metrics.Eff: registerHistogram :: forall r_aKi2. MemberWithError MetricEff r_aKi2 => Text -> [(Text, Text)] -> [Double] -> Sem r_aKi2 Histogram
+ Calamity.Metrics.Eff: registerHistogram :: forall r_aKIi. MemberWithError MetricEff r_aKIi => Text -> [(Text, Text)] -> [Double] -> Sem r_aKIi Histogram

Files

ChangeLog.md view
@@ -1,5 +1,9 @@ # Changelog for Calamity +## 0.1.20.0++* Migrate do di-polysemy 0.2, runBotIO no longer handles the Log effect.+ ## 0.1.19.2  * Fix broken `Float` parsing.
README.md view
@@ -69,6 +69,7 @@ import           Data.Text.Lazy              ( Text, fromStrict ) import           Data.Text.Strict.Lens +import qualified Di import qualified DiPolysemy                  as DiP  import qualified Polysemy                    as P@@ -108,55 +109,56 @@ main :: IO () main = do   token <- view packed <$> getEnv "BOT_TOKEN"-  void . P.runFinal . P.embedToFinal . runCounterAtomic . runCacheInMemory . runMetricsNoop . useConstantPrefix "!"-    $ runBotIO (BotToken token) $ do-    addCommands $ do-      helpCommand-      command @'[User] "utest" $ \ctx u -> do-        void $ tellt ctx $ "got user: " <> showtl u-      command @'[Named "u" User, Named "u1" User] "utest2" $ \ctx u u1 -> do-        void $ tellt ctx $ "got user: " <> showtl u <> "\nand: " <> showtl u1-      command @'[L.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 @'[[L.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 @'[KleenePlusConcat L.Text] "this" $ \ctx msg -> do-            void $ tellt ctx msg-      command @'[Snowflake Emoji] "etest" $ \ctx e -> do-        void $ tellt ctx $ "got emoji: " <> showtl e-      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" :: L.Text, ctx ^. #message)-      command @'[L.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-      "!say hi" -> replicateM_ 3 . P.async $ do-        info "saying heya"-        Right msg' <- tellt msg "heya"-        info "sleeping"-        P.embed $ threadDelay (5 * 1000 * 1000)-        info "slept"-        void . invoke $ EditMessage (msg ^. #channelID) msg' (Just "lol") Nothing-        info "edited"-      _ -> pure ()-    react @('CustomEvt "command-error" (CommandContext.Context, CommandError)) $ \(ctx, e) -> do-      info $ "Command failed with reason: " <> showt e-      case e of-        ParseError n r -> void . tellt ctx $ "Failed to parse parameter: `" <> L.fromStrict n <> "`, with reason: ```\n" <> r <> "```"-    react @('CustomEvt "my-event" (L.Text, Message)) $ \(s, m) ->-      void $ tellt m ("Somebody told me to tell you about: " <> s)+  Di.new $ \di ->+    void . P.runFinal . P.embedToFinal . DiP.runDiToIO di . runCounterAtomic . runCacheInMemory . runMetricsNoop . useConstantPrefix "!"+      $ runBotIO (BotToken token) $ do+      addCommands $ do+        helpCommand+        command @'[User] "utest" $ \ctx u -> do+          void $ tellt ctx $ "got user: " <> showtl u+        command @'[Named "u" User, Named "u1" User] "utest2" $ \ctx u u1 -> do+          void $ tellt ctx $ "got user: " <> showtl u <> "\nand: " <> showtl u1+        command @'[L.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 @'[[L.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 @'[KleenePlusConcat L.Text] "this" $ \ctx msg -> do+              void $ tellt ctx msg+        command @'[Snowflake Emoji] "etest" $ \ctx e -> do+          void $ tellt ctx $ "got emoji: " <> showtl e+        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" :: L.Text, ctx ^. #message)+        command @'[L.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+        "!say hi" -> replicateM_ 3 . P.async $ do+          info "saying heya"+          Right msg' <- tellt msg "heya"+          info "sleeping"+          P.embed $ threadDelay (5 * 1000 * 1000)+          info "slept"+          void . invoke $ EditMessage (msg ^. #channelID) msg' (Just "lol") Nothing+          info "edited"+        _ -> pure ()+      react @('CustomEvt "command-error" (CommandContext.Context, CommandError)) $ \(ctx, e) -> do+        info $ "Command failed with reason: " <> showt e+        case e of+          ParseError n r -> void . tellt ctx $ "Failed to parse parameter: `" <> L.fromStrict n <> "`, with reason: ```\n" <> r <> "```"+      react @('CustomEvt "my-event" (L.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: 16ef7c5601edfc65ae3b7b277d4c413b36765b77311b207855e4975c892dd8cb+-- hash: 0c8bab4fd581f5309d85bd6e640b7f1f3f953f8862ad5b5c607caabf95e1cb3e  name:           calamity-version:        0.1.19.2+version:        0.1.20.0 synopsis:       A library for writing discord bots in haskell description:    Please see the README on GitHub at <https://github.com/nitros12/calamity#readme> category:       Network, Web@@ -149,7 +149,8 @@     , deepseq >=1.4.4.0 && <2     , deque >=0.4 && <0.5     , df1 >=0.3 && <0.5-    , di-polysemy >=0.1.4 && <0.2+    , di-core >=1.0.4 && <1.1+    , di-polysemy >=0.2 && <0.3     , exceptions >=0.10 && <0.11     , fmt >=0.6 && <0.7     , focus >=1.0 && <2
src/Calamity/Client/Client.hs view
@@ -33,6 +33,7 @@ import           Calamity.Types.Model.User import           Calamity.Types.Snowflake import           Calamity.Types.Token+import           Calamity.Types.LogEff  import           Control.Concurrent.Chan.Unagi import           Control.Concurrent.MVar@@ -52,6 +53,8 @@ import           Data.Time.Clock.POSIX import           Data.Typeable +import qualified Di.Core                           as DC+import qualified Df1 import qualified DiPolysemy                        as Di  import           Network.Wreq.Session            ( Session, newAPISession )@@ -77,8 +80,8 @@   pure (duration, res)  -newClient :: Token -> Session -> IO Client-newClient token session = do+newClient :: Token -> Session -> Maybe (DC.Di Df1.Level Df1.Path Df1.Message) -> IO Client+newClient token session initialDi = do   shards'        <- newTVarIO []   numShards'     <- newEmptyMVar   rlState'       <- newRateLimitState@@ -93,31 +96,41 @@                 outc                 ehidCounter                 session+                initialDi  -- | Create a bot, run your setup action, and then loop until the bot closes. runBotIO :: forall r a.-         (P.Members '[P.Embed IO, P.Final IO, CacheEff, MetricEff] r, Typeable (SetupEff r))+         (P.Members '[P.Embed IO, P.Final IO, CacheEff, MetricEff, LogEff] r, Typeable (SetupEff r))          => Token          -> P.Sem (SetupEff r) a          -> P.Sem r (Maybe StartupError) runBotIO token setup = runBotIO' token Nothing Nothing Nothing setup +resetDi :: BotC r => P.Sem r a -> P.Sem r a+resetDi m = do+  initialDi <- P.asks (^. #initialDi)+  Di.local (flip fromMaybe initialDi) m+ -- | Create a bot, run your setup action, and then loop until the bot closes. ----- This version allows you to specify the session and initial status and intents.+-- This version allows you to specify the http session, initial status, and intents. runBotIO' :: forall r a.-          (P.Members '[P.Embed IO, P.Final IO, CacheEff, MetricEff] r, Typeable (SetupEff r))+          (P.Members '[P.Embed IO, P.Final IO, CacheEff, MetricEff, LogEff] r, Typeable (SetupEff r))           => Token           -> Maybe Session+          -- ^ The HTTP session to use, defaults to 'newAPISession'           -> Maybe StatusUpdateData+          -- ^ The initial status to send to the gateway           -> Maybe Intents+          -- ^ The intents to send to the gateway           -> P.Sem (SetupEff r) a           -> P.Sem r (Maybe StartupError) runBotIO' token session status intents setup = do+  initialDi <- Di.fetch   session' <- maybe (P.embed newAPISession) pure session-  client <- P.embed $ newClient token session'+  client <- P.embed $ newClient token session' initialDi   handlers <- P.embed $ newTVarIO def-  P.asyncToIOFinal . P.runAtomicStateTVar handlers . P.runReader client . Di.runDiToStderrIOFinal . Di.push "calamity" $ do+  P.asyncToIOFinal . P.runAtomicStateTVar handlers . P.runReader client . Di.push "calamity" $ do     void $ Di.push "calamity-setup" setup     r <- shardBot status intents     case r of@@ -316,13 +329,12 @@ clientLoop :: BotC r => P.Sem r () clientLoop = do   outc <- P.asks (^. #eventsOut)-  void . P.runError . forever $ do+  whileMFinalIO $ do     evt' <- P.embed $ readChan outc     case evt' of-      -- NOTE: these raise's are needed to raise into the Error effect-      Dispatch sid evt -> P.raise $ handleEvent sid evt-      Custom s d   -> P.raise $ handleCustomEvent s d-      ShutDown     -> P.throw ()+      Dispatch sid evt -> handleEvent sid evt >> pure True+      Custom s d       -> handleCustomEvent s d >> pure True+      ShutDown         -> pure False   debug "leaving client loop"  handleCustomEvent :: forall r. BotC r => TypeRep -> Dynamic -> P.Sem r ()@@ -349,7 +361,7 @@     evtCounter <- registerCounter "events_received" [("type", S.pack $ ctorName data'), ("shard", showt shardID)]     void $ addCounter 1 evtCounter     cacheUpdateHisto <- registerHistogram "cache_update" mempty [10, 20..100]-    (time, res) <- timeA $ Di.reset $ handleEvent' eventHandlers data'+    (time, res) <- timeA $ resetDi $ handleEvent' eventHandlers data'     void $ observeHistogram time cacheUpdateHisto     pure res 
src/Calamity/Client/Types.hs view
@@ -57,6 +57,8 @@  import qualified TextShow.Generic                as TSG import TextShow+import qualified Df1 as Df1+import qualified Di.Core as DC  data Client = Client   { shards              :: TVar [(InChan ControlMessage, Async (Maybe ()))]@@ -67,6 +69,7 @@   , eventsOut           :: OutChan CalamityEvent   , ehidCounter         :: IORef Integer   , session             :: Session+  , initialDi           :: Maybe (DC.Di Df1.Level Df1.Path Df1.Message)   }   deriving ( Generic ) @@ -76,7 +79,7 @@   , Typeable r)  -- | A concrete effect stack used inside the bot-type SetupEff r = (LogEff ': P.Reader Client ': P.AtomicState EventHandlers ': P.Async ': r)+type SetupEff r = (P.Reader Client ': P.AtomicState EventHandlers ': P.Async ': r)  -- | Some constraints that 'Calamity.Client.Client.react' needs to work. Don't -- worry about these since they are satisfied for any type @s@ can be
src/Calamity/Commands/CommandUtils.hs view
@@ -42,8 +42,9 @@ commandPath :: Command -> [S.Text] commandPath Command { names, parent } = maybe [] groupPath parent ++ [NE.head names] +-- | Format a command's parameters commandParams :: Command -> L.Text-commandParams Command { params } = L.fromStrict $ S.unwords params+commandParams Command { params } = L.fromStrict $ S.intercalate ", " params  -- | Given the properties of a 'Command' with the @parser@ and @callback@ in the -- 'P.Sem' monad, build a command by transforming the Polysemy actions into IO
src/Calamity/Gateway/Shard.hs view
@@ -31,7 +31,6 @@ import           Data.IORef import           Data.Maybe import qualified Data.Text.Lazy                  as L-import           Data.Void  import           DiPolysemy                      hiding ( debug, error, info ) @@ -97,10 +96,6 @@       P.embed . sendTextData wsConn $ encodedData     Nothing -> debug "tried to send to closed WS" -fromEitherVoid :: Either a Void -> a-fromEitherVoid (Left a) = a-fromEitherVoid (Right a) = absurd a -- yeet- tryWriteTBMQueue' :: TBMQueue a -> a -> STM Bool tryWriteTBMQueue' q v = do   v' <- tryWriteTBMQueue q v@@ -161,11 +156,8 @@                 when r inner    -- | The outer loop, sets up the ws conn, etc handles reconnecting and such-  -- Currently if this goes to the error path we just exit the forever loop-  -- and the shard stops, maybe we might want to do some extra logic to reboot-  -- the shard, or maybe force a resharding-  outerloop :: ShardC r => Sem r (Either ShardFlowControl ())-  outerloop = P.runError . forever $ do+  outerloop :: ShardC r => Sem r ()+  outerloop = whileMFinalIO $ do     shard :: Shard <- P.atomicGets (^. #shardS)     let host = shard ^. #gateway     let host' =  fromMaybe host $ L.stripPrefix "wss://" host@@ -176,14 +168,16 @@     case innerLoopVal of       Just ShardFlowShutDown -> do         info "Shutting down shard"-        P.throw ShardFlowShutDown+        pure False -      Just ShardFlowRestart ->+      Just ShardFlowRestart -> do         info "Restaring shard"+        pure True         -- we restart normally when we loop -      Nothing -> -- won't happen unless innerloop starts using a non-deterministic effect+      Nothing -> do -- won't happen unless innerloop starts using a non-deterministic effect         info "Restarting shard (abnormal reasons?)"+        pure True    -- | The inner loop, handles receiving a message from discord or a command message   -- and then decides what to do with it@@ -227,7 +221,7 @@         debug "handling events now"         _controlThread <- P.async . P.embed $ controlStream shard q         _discordThread <- P.async $ discordStream ws q-        (fromEitherVoid <$>) . P.raise . P.runError . forever $ do+        P.raise . untilJustFinalIO . (leftToMaybe <$>) . P.runError $ do           -- only we close the queue           msg <- P.embed . atomically $ readTBMQueue q           handleMsg =<< restartUnless "shard message stream closed by someone other than the sink" msg)@@ -315,7 +309,7 @@   P.atomicModify (#hbResponse .~ False)  heartBeatLoop :: ShardC r => Int -> Sem r ()-heartBeatLoop interval = void . P.runError . forever $ do+heartBeatLoop interval = untilJustFinalIO . (leftToMaybe <$>) . P.runError $ do   sendHeartBeat   P.embed . threadDelay $ interval * 1000   unlessM (P.atomicGets (^. #hbResponse)) $ do
src/Calamity/Internal/Utils.hs view
@@ -2,7 +2,9 @@  -- | Internal utilities and instances module Calamity.Internal.Utils-    ( whenJust+    ( whileMFinalIO+    , untilJustFinalIO+    , whenJust     , whenM     , unlessM     , lastMaybe@@ -18,6 +20,7 @@     , swap ) where  import           Calamity.Types.LogEff+import           Calamity.Internal.RunIntoIO  import           Control.Applicative @@ -37,6 +40,38 @@  import           TextShow import Data.Colour (Colour)++-- | Like whileM, but stateful effects are not preserved to mitigate memory leaks+--+-- This means Polysemy.Error won't work to break the loop, etc.+-- Instead, Error/Alternative will just result in the loop quitting.+whileMFinalIO :: P.Member (P.Final IO) r => P.Sem r Bool -> P.Sem r ()+whileMFinalIO action = do+  action' <- runSemToIO action+  P.embedFinal $ go action'+  where go action' = do+          r <- action'+          case r of+            Just True ->+              go action'+            _ ->+              pure ()++-- | Like untilJust, but stateful effects are not preserved to mitigate memory leaks+--+-- This means Polysemy.Error won't work to break the loop, etc.+-- Instead, Error/Alternative will just result in another loop.+untilJustFinalIO :: P.Member (P.Final IO) r => P.Sem r (Maybe a) -> P.Sem r a+untilJustFinalIO action = do+  action' <- runSemToIO action+  P.embedFinal $ go action'+  where go action' = do+          r <- action'+          case r of+            Just (Just a) ->+              pure a+            _ ->+              go action'  whenJust :: Applicative m => Maybe a -> (a -> m ()) -> m () whenJust = flip $ maybe (pure ())