packages feed

calamity 0.7.0.1 → 0.7.1.0

raw patch · 5 files changed

+86/−68 lines, 5 filesdep ~PyFdep ~aesondep ~http-api-dataPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: PyF, aeson, http-api-data, req, typerep-map, vector

API changes (from Hackage documentation)

- Calamity.Cache.Eff: delDM :: forall r_a4YKB. Member CacheEff r_a4YKB => Snowflake DMChannel -> Sem r_a4YKB ()
+ Calamity.Cache.Eff: delDM :: forall r_a4Xt4. Member CacheEff r_a4Xt4 => Snowflake DMChannel -> Sem r_a4Xt4 ()
- Calamity.Cache.Eff: delGuild :: forall r_a4YKu. Member CacheEff r_a4YKu => Snowflake Guild -> Sem r_a4YKu ()
+ Calamity.Cache.Eff: delGuild :: forall r_a4XsX. Member CacheEff r_a4XsX => Snowflake Guild -> Sem r_a4XsX ()
- Calamity.Cache.Eff: delMessage :: forall r_a4YKW. Member CacheEff r_a4YKW => Snowflake Message -> Sem r_a4YKW ()
+ Calamity.Cache.Eff: delMessage :: forall r_a4Xtp. Member CacheEff r_a4Xtp => Snowflake Message -> Sem r_a4Xtp ()
- Calamity.Cache.Eff: delUnavailableGuild :: forall r_a4YKP. Member CacheEff r_a4YKP => Snowflake Guild -> Sem r_a4YKP ()
+ Calamity.Cache.Eff: delUnavailableGuild :: forall r_a4Xti. Member CacheEff r_a4Xti => Snowflake Guild -> Sem r_a4Xti ()
- Calamity.Cache.Eff: delUser :: forall r_a4YKI. Member CacheEff r_a4YKI => Snowflake User -> Sem r_a4YKI ()
+ Calamity.Cache.Eff: delUser :: forall r_a4Xtb. Member CacheEff r_a4Xtb => Snowflake User -> Sem r_a4Xtb ()
- Calamity.Cache.Eff: getBotUser :: forall r_a4YKm. Member CacheEff r_a4YKm => Sem r_a4YKm (Maybe User)
+ Calamity.Cache.Eff: getBotUser :: forall r_a4XsP. Member CacheEff r_a4XsP => Sem r_a4XsP (Maybe User)
- Calamity.Cache.Eff: getDM :: forall r_a4YKy. Member CacheEff r_a4YKy => Snowflake DMChannel -> Sem r_a4YKy (Maybe DMChannel)
+ Calamity.Cache.Eff: getDM :: forall r_a4Xt1. Member CacheEff r_a4Xt1 => Snowflake DMChannel -> Sem r_a4Xt1 (Maybe DMChannel)
- Calamity.Cache.Eff: getDMs :: forall r_a4YKA. Member CacheEff r_a4YKA => Sem r_a4YKA [DMChannel]
+ Calamity.Cache.Eff: getDMs :: forall r_a4Xt3. Member CacheEff r_a4Xt3 => Sem r_a4Xt3 [DMChannel]
- Calamity.Cache.Eff: getGuild :: forall r_a4YKp. Member CacheEff r_a4YKp => Snowflake Guild -> Sem r_a4YKp (Maybe Guild)
+ Calamity.Cache.Eff: getGuild :: forall r_a4XsS. Member CacheEff r_a4XsS => Snowflake Guild -> Sem r_a4XsS (Maybe Guild)
- Calamity.Cache.Eff: getGuildChannel :: forall r_a4YKr. Member CacheEff r_a4YKr => Snowflake GuildChannel -> Sem r_a4YKr (Maybe GuildChannel)
+ Calamity.Cache.Eff: getGuildChannel :: forall r_a4XsU. Member CacheEff r_a4XsU => Snowflake GuildChannel -> Sem r_a4XsU (Maybe GuildChannel)
- Calamity.Cache.Eff: getGuilds :: forall r_a4YKt. Member CacheEff r_a4YKt => Sem r_a4YKt [Guild]
+ Calamity.Cache.Eff: getGuilds :: forall r_a4XsW. Member CacheEff r_a4XsW => Sem r_a4XsW [Guild]
- Calamity.Cache.Eff: getMessage :: forall r_a4YKT. Member CacheEff r_a4YKT => Snowflake Message -> Sem r_a4YKT (Maybe Message)
+ Calamity.Cache.Eff: getMessage :: forall r_a4Xtm. Member CacheEff r_a4Xtm => Snowflake Message -> Sem r_a4Xtm (Maybe Message)
- Calamity.Cache.Eff: getMessages :: forall r_a4YKV. Member CacheEff r_a4YKV => Sem r_a4YKV [Message]
+ Calamity.Cache.Eff: getMessages :: forall r_a4Xto. Member CacheEff r_a4Xto => Sem r_a4Xto [Message]
- Calamity.Cache.Eff: getUnavailableGuilds :: forall r_a4YKO. Member CacheEff r_a4YKO => Sem r_a4YKO [Snowflake Guild]
+ Calamity.Cache.Eff: getUnavailableGuilds :: forall r_a4Xth. Member CacheEff r_a4Xth => Sem r_a4Xth [Snowflake Guild]
- Calamity.Cache.Eff: getUser :: forall r_a4YKF. Member CacheEff r_a4YKF => Snowflake User -> Sem r_a4YKF (Maybe User)
+ Calamity.Cache.Eff: getUser :: forall r_a4Xt8. Member CacheEff r_a4Xt8 => Snowflake User -> Sem r_a4Xt8 (Maybe User)
- Calamity.Cache.Eff: getUsers :: forall r_a4YKH. Member CacheEff r_a4YKH => Sem r_a4YKH [User]
+ Calamity.Cache.Eff: getUsers :: forall r_a4Xta. Member CacheEff r_a4Xta => Sem r_a4Xta [User]
- Calamity.Cache.Eff: isUnavailableGuild :: forall r_a4YKM. Member CacheEff r_a4YKM => Snowflake Guild -> Sem r_a4YKM Bool
+ Calamity.Cache.Eff: isUnavailableGuild :: forall r_a4Xtf. Member CacheEff r_a4Xtf => Snowflake Guild -> Sem r_a4Xtf Bool
- Calamity.Cache.Eff: setBotUser :: forall r_a4YKk. Member CacheEff r_a4YKk => User -> Sem r_a4YKk ()
+ Calamity.Cache.Eff: setBotUser :: forall r_a4XsN. Member CacheEff r_a4XsN => User -> Sem r_a4XsN ()
- Calamity.Cache.Eff: setDM :: forall r_a4YKw. Member CacheEff r_a4YKw => DMChannel -> Sem r_a4YKw ()
+ Calamity.Cache.Eff: setDM :: forall r_a4XsZ. Member CacheEff r_a4XsZ => DMChannel -> Sem r_a4XsZ ()
- Calamity.Cache.Eff: setGuild :: forall r_a4YKn. Member CacheEff r_a4YKn => Guild -> Sem r_a4YKn ()
+ Calamity.Cache.Eff: setGuild :: forall r_a4XsQ. Member CacheEff r_a4XsQ => Guild -> Sem r_a4XsQ ()
- Calamity.Cache.Eff: setMessage :: forall r_a4YKR. Member CacheEff r_a4YKR => Message -> Sem r_a4YKR ()
+ Calamity.Cache.Eff: setMessage :: forall r_a4Xtk. Member CacheEff r_a4Xtk => Message -> Sem r_a4Xtk ()
- Calamity.Cache.Eff: setUnavailableGuild :: forall r_a4YKK. Member CacheEff r_a4YKK => Snowflake Guild -> Sem r_a4YKK ()
+ Calamity.Cache.Eff: setUnavailableGuild :: forall r_a4Xtd. Member CacheEff r_a4Xtd => Snowflake Guild -> Sem r_a4Xtd ()
- Calamity.Cache.Eff: setUser :: forall r_a4YKD. Member CacheEff r_a4YKD => User -> Sem r_a4YKD ()
+ Calamity.Cache.Eff: setUser :: forall r_a4Xt6. Member CacheEff r_a4Xt6 => User -> Sem r_a4Xt6 ()
- Calamity.HTTP.Internal.Ratelimit: getRatelimitState :: forall r_a3TPe. Member RatelimitEff r_a3TPe => Sem r_a3TPe RateLimitState
+ Calamity.HTTP.Internal.Ratelimit: getRatelimitState :: forall r_a3SEC. Member RatelimitEff r_a3SEC => Sem r_a3SEC RateLimitState
- Calamity.Interactions.Eff: getInteraction :: forall r_a3LVR. Member InteractionEff r_a3LVR => Sem r_a3LVR Interaction
+ Calamity.Interactions.Eff: getInteraction :: forall r_a3IND. Member InteractionEff r_a3IND => Sem r_a3IND Interaction
- Calamity.Interactions.View: endView :: forall ret_a6xeN inp_a6xeO sendResp_a6xeP r_a6xsv. Member (ViewEff ret_a6xeN inp_a6xeO sendResp_a6xeP) r_a6xsv => ret_a6xeN -> Sem r_a6xsv ()
+ Calamity.Interactions.View: endView :: forall ret_a6vo6 inp_a6vo7 sendResp_a6vo8 r_a6vBO. Member (ViewEff ret_a6vo6 inp_a6vo7 sendResp_a6vo8) r_a6vBO => ret_a6vo6 -> Sem r_a6vBO ()
- Calamity.Interactions.View: getSendResponse :: forall ret_a6xeV inp_a6xeW sendResp_a6xeX r_a6xsA. Member (ViewEff ret_a6xeV inp_a6xeW sendResp_a6xeX) r_a6xsA => Sem r_a6xsA sendResp_a6xeX
+ Calamity.Interactions.View: getSendResponse :: forall ret_a6voe inp_a6vof sendResp_a6vog r_a6vBT. Member (ViewEff ret_a6voe inp_a6vof sendResp_a6vog) r_a6vBT => Sem r_a6vBT sendResp_a6vog
- Calamity.Interactions.View: replaceView :: forall ret_a6xeT inp_a6xeR sendResp_a6xeU r_a6xsx. Member (ViewEff ret_a6xeT inp_a6xeR sendResp_a6xeU) r_a6xsx => View inp_a6xeR -> ([Component] -> Sem r_a6xsx ()) -> Sem r_a6xsx ()
+ Calamity.Interactions.View: replaceView :: forall ret_a6voc inp_a6voa sendResp_a6vod r_a6vBQ. Member (ViewEff ret_a6voc inp_a6voa sendResp_a6vod) r_a6vBQ => View inp_a6voa -> ([Component] -> Sem r_a6vBQ ()) -> Sem r_a6vBQ ()
- Calamity.Internal.LocalWriter: llisten :: forall o_atuZ r_atwp a_X0. Member (LocalWriter o_atuZ) r_atwp => Sem r_atwp a_X0 -> Sem r_atwp (o_atuZ, a_X0)
+ Calamity.Internal.LocalWriter: llisten :: forall o_attO r_atve a_X0. Member (LocalWriter o_attO) r_atve => Sem r_atve a_X0 -> Sem r_atve (o_attO, a_X0)
- Calamity.Internal.LocalWriter: ltell :: forall o_atuV r_atwn. Member (LocalWriter o_atuV) r_atwn => o_atuV -> Sem r_atwn ()
+ Calamity.Internal.LocalWriter: ltell :: forall o_attK r_atvc. Member (LocalWriter o_attK) r_atvc => o_attK -> Sem r_atvc ()
- Calamity.Internal.Utils: whileMFinalIO :: Sem r Bool -> Sem r ()
+ Calamity.Internal.Utils: whileMFinalIO :: Member (Final IO) r => Sem r Bool -> Sem r ()
- Calamity.Metrics.Eff: addCounter :: forall r_auT0. Member MetricEff r_auT0 => Int -> Counter -> Sem r_auT0 Int
+ Calamity.Metrics.Eff: addCounter :: forall r_auRR. Member MetricEff r_auRR => Int -> Counter -> Sem r_auRR Int
- Calamity.Metrics.Eff: modifyGauge :: forall r_auT3. Member MetricEff r_auT3 => (Double -> Double) -> Gauge -> Sem r_auT3 Double
+ Calamity.Metrics.Eff: modifyGauge :: forall r_auRU. Member MetricEff r_auRU => (Double -> Double) -> Gauge -> Sem r_auRU Double
- Calamity.Metrics.Eff: observeHistogram :: forall r_auT6. Member MetricEff r_auT6 => Double -> Histogram -> Sem r_auT6 HistogramSample
+ Calamity.Metrics.Eff: observeHistogram :: forall r_auRX. Member MetricEff r_auRX => Double -> Histogram -> Sem r_auRX HistogramSample
- Calamity.Metrics.Eff: registerCounter :: forall r_auSQ. Member MetricEff r_auSQ => Text -> [(Text, Text)] -> Sem r_auSQ Counter
+ Calamity.Metrics.Eff: registerCounter :: forall r_auRH. Member MetricEff r_auRH => Text -> [(Text, Text)] -> Sem r_auRH Counter
- Calamity.Metrics.Eff: registerGauge :: forall r_auST. Member MetricEff r_auST => Text -> [(Text, Text)] -> Sem r_auST Gauge
+ Calamity.Metrics.Eff: registerGauge :: forall r_auRK. Member MetricEff r_auRK => Text -> [(Text, Text)] -> Sem r_auRK Gauge
- Calamity.Metrics.Eff: registerHistogram :: forall r_auSW. Member MetricEff r_auSW => Text -> [(Text, Text)] -> [Double] -> Sem r_auSW Histogram
+ Calamity.Metrics.Eff: registerHistogram :: forall r_auRN. Member MetricEff r_auRN => Text -> [(Text, Text)] -> [Double] -> Sem r_auRN Histogram
- Calamity.Types.TokenEff: getBotToken :: forall r_a1FbI. Member TokenEff r_a1FbI => Sem r_a1FbI Token
+ Calamity.Types.TokenEff: getBotToken :: forall r_a1ELy. Member TokenEff r_a1ELy => Sem r_a1ELy Token

Files

Calamity/HTTP/Internal/Ratelimit.hs view
@@ -62,6 +62,27 @@     Nothing ->       pure $ UnknownRatelimit h +mergeBucketStates :: BucketState -> BucketState -> BucketState+mergeBucketStates old new =+  new+    { ongoing = old ^. #ongoing+    , -- we only ignore the previous 'remaining' if we've not reset yet and the+      -- reset time has changed+      remaining =+        if isJust (old ^. #resetTime) && old ^. #resetKey /= new ^. #resetKey+          then min (old ^. #remaining) (new ^. #remaining)+          else new ^. #remaining+    , -- only take the new resetTime if it actually changed+      resetTime =+        if old ^. #resetKey /= new ^. #resetKey+          then new ^. #resetTime+          else old ^. #resetTime+    }+++updateKnownBucket :: Bucket -> BucketState -> STM ()+updateKnownBucket bucket bucketState = modifyTVar' (bucket ^. #state) (`mergeBucketStates` bucketState)+ {- | Knowing the bucket for a route, and the ratelimit info, map the route to  the bucket key and retrieve the bucket -}@@ -75,32 +96,23 @@       bucket <- SC.lookup bucketKey' $ buckets s       case bucket of         Just bucket' -> do-          modifyTVar' (bucket' ^. #state) (`mergeStates` bucketState)+          modifyTVar' (bucket' ^. #state) (`mergeBucketStates` bucketState)           pure bucket'         Nothing -> Prelude.error "Not possible"     Nothing -> do-      -- the bucket key wasn't known, make a new bucket and insert it-      bs <- Bucket <$> newTVar bucketState-      SC.insert bs b $ buckets s+      -- we didn't know the key to this bucket, but we might know the bucket+      -- if we truly don't know the bucket, then make a new one+      bs <- do+        bucket <- SC.lookup b $ buckets s+        case bucket of+          Just bs -> pure bs+          Nothing -> do+            bs <- Bucket <$> newTVar bucketState+            SC.insert bs b $ buckets s+            pure bs+       SC.insert b h $ bucketKeys s       pure bs-  where-    mergeStates :: BucketState -> BucketState -> BucketState-    mergeStates old new =-      new-        { ongoing = old ^. #ongoing-        , -- we only ignore the previous 'remaining' if we've not reset yet and the-          -- reset time has changed-          remaining =-            if isJust (old ^. #resetTime) && old ^. #resetKey /= new ^. #resetKey-              then min (old ^. #remaining) (new ^. #remaining)-              else new ^. #remaining-        , -- only take the new resetTime if it actually changed-          resetTime =-            if old ^. #resetKey /= new ^. #resetKey-              then new ^. #resetTime-              else old ^. #resetTime-        }  resetBucket :: Bucket -> STM () resetBucket bucket =@@ -178,8 +190,6 @@             -- [0] wait and then retry after we can unlock the bucket             pure (intoWaitDelay $ s ^. #resetTime) -      -- putStrLn (show now <> ": Using bucket, waiting until: " <> show mWaitDelay <> ", uses: " <> show s <> ", " <> inf)-       case mWaitDelay of         WaitUntil waitUntil -> do           if waitUntil < now@@ -336,36 +346,41 @@       void . P.embed . atomically $ do         case rl of           KnownRatelimit bucket ->-            modifyTVar' (bucket ^. #state) (#ongoing %~ succ)+            modifyTVar' (bucket ^. #state) (#ongoing %~ pred)           _ -> pure ()-        case rlHeaders of-          Just (bs, bk) ->+        case (rl, rlHeaders) of+          (KnownRatelimit bucket, Just (bs, _bk)) ->+            updateKnownBucket bucket bs+          (_, Just (bs, bk)) ->             void $ updateBucket rlstate (routeKey route) bk bs-          Nothing -> pure ()+          (_, Nothing) -> pure ()       pure $ RGood v+     Ratelimited unlockWhen False (Just (bs, bk)) -> do       debug . T.pack $ "429 ratelimited on route, retrying at " <> show unlockWhen        P.embed . atomically $ do         case rl of-          KnownRatelimit bucket ->-            modifyTVar' (bucket ^. #state) (#ongoing %~ succ)-          _ -> pure ()-        void $ updateBucket rlstate (routeKey route) bk bs+          KnownRatelimit bucket -> do+            modifyTVar' (bucket ^. #state) (#ongoing %~ pred)+            updateKnownBucket bucket bs+          _ -> void $ updateBucket rlstate (routeKey route) bk bs        P.embed $ do         threadDelayUntil unlockWhen        pure $ Retry (HTTPError 429 Nothing)+     Ratelimited unlockWhen False _ -> do       debug "Internal error (ratelimited but no headers), retrying"       case rl of         KnownRatelimit bucket ->-          void . P.embed . atomically $ modifyTVar' (bucket ^. #state) (#ongoing %~ succ)+          void . P.embed . atomically $ modifyTVar' (bucket ^. #state) (#ongoing %~ pred)         _ -> pure ()        P.embed $ threadDelayUntil unlockWhen       pure $ Retry (HTTPError 429 Nothing)+     Ratelimited unlockWhen True bs -> do       debug "429 ratelimited globally" 
Calamity/HTTP/Internal/Route.hs view
@@ -174,17 +174,17 @@   Route buildRoute (UnsafeMkRouteBuilder route ids params) =   Route-    (foldl' (/:) baseURL $ map goR route)-    (T.concat (map goIdent route))+    (foldl' (/:) baseURL $ map goRoute route)+    (T.concat (map goKey route))     (Snowflake <$> lookup (typeRep (Proxy @Channel)) ids)     (Snowflake <$> lookup (typeRep (Proxy @Guild)) ids)   where-    goR (S' t) = t-    goR (PS' t) = fromJust $ lookup t params-    goR (ID' t) = TextShow.showt . fromJust $ lookup t ids+    goRoute (S' t) = t+    goRoute (PS' t) = fromJust $ lookup t params+    goRoute (ID' t) = TextShow.showt . fromJust $ lookup t ids -    goIdent (S' t) = t-    goIdent (PS' s) = T.pack s-    goIdent (ID' t) = TextShow.showt t+    goKey (S' t) = t+    goKey (PS' t) = T.pack t+    goKey (ID' t) = TextShow.showt t  $(makeFieldLabelsNoPrefix ''Route)
Calamity/Internal/Utils.hs view
@@ -26,37 +26,38 @@   CalamityToJSON' (..), ) where --- import Calamity.Internal.RunIntoIO+import Calamity.Internal.RunIntoIO import Calamity.Types.LogEff import Control.Applicative-import Control.Monad (when)+import qualified Data.Aeson as Aeson import Data.Aeson.Encoding (null_) import Data.Default.Class import qualified Data.Map as M+import Data.Maybe (catMaybes) import Data.Semigroup (Last (..)) import Data.Text import qualified Data.Vector.Unboxing as VU import qualified DiPolysemy as Di import qualified Polysemy as P import TextShow-import qualified Data.Aeson as Aeson-import Data.Maybe (catMaybes)  {- | 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.Sem r Bool -> P.Sem r ()+whileMFinalIO :: P.Member (P.Final IO) r => P.Sem r Bool -> P.Sem r () whileMFinalIO action = do-  go action+  action' <- runSemToIO action+  P.embedFinal $ go action'   where-    go action = do-      r <- action-      when r $ go_b action-    {-# INLINE go #-}-    go_b = go-    {-# NOINLINE go_b #-}+    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 @@ -65,18 +66,16 @@ -} untilJustFinalIO :: P.Member (P.Final IO) r => P.Sem r (Maybe a) -> P.Sem r a untilJustFinalIO action = do-  go action+  action' <- runSemToIO action+  P.embedFinal $ go action'   where-    go action = do-      r <- action+    go action' = do+      r <- action'       case r of-        Just a ->+        Just (Just a) ->           pure a         _ ->-          go_b action-    {-# INLINE go #-}-    go_b = go-    {-# NOINLINE go_b #-}+          go action'  whenJust :: Applicative m => Maybe a -> (a -> m ()) -> m () whenJust = flip $ maybe (pure ())
ChangeLog.md view
@@ -1,5 +1,9 @@ # Changelog for Calamity +## 0.7.1.0+++ Fix ratelimits being effectively broken (succ -> pred)+ ## 0.7.0.1  + Fix accidental semver breakage
calamity.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           calamity-version:        0.7.0.1+version:        0.7.1.0 synopsis:       A library for writing discord bots in haskell description:    Please see the README on GitHub at <https://github.com/simmsb/calamity#readme> category:       Network, Web@@ -18,7 +18,7 @@ license-file:   LICENSE build-type:     Simple tested-with:-    GHC == 8.10.7, GHC == 9.2.4+    GHC == 8.10.7, GHC == 9.2.5 extra-source-files:     README.md     ChangeLog.md@@ -186,8 +186,8 @@       QuasiQuotes   ghc-options: -fplugin=Polysemy.Plugin -funbox-strict-fields -Wall -fno-warn-name-shadowing   build-depends:-      PyF ==0.10.*-    , aeson ==2.0.*+      PyF ==0.11.*+    , aeson >=2.0 && <2.2     , async >=2.2 && <3     , base >=4.13 && <5     , bytestring >=0.10 && <0.12@@ -206,7 +206,7 @@     , exceptions ==0.10.*     , focus >=1.0 && <2     , hashable >=1.2 && <2-    , http-api-data >=0.4.3 && <0.5+    , http-api-data >=0.4.3 && <0.6     , http-client >=0.5 && <0.8     , http-date >=0.0.8 && <0.1     , http-types ==0.12.*@@ -216,7 +216,7 @@     , polysemy >=1.5 && <2     , polysemy-plugin >=0.3 && <0.5     , reflection >=2.1 && <3-    , req >=3.9.2 && <3.11+    , req >=3.9.2 && <3.14     , safe-exceptions >=0.1 && <2     , scientific ==0.3.*     , stm >=2.5 && <3@@ -226,11 +226,11 @@     , text-show >=3.8 && <4     , time >=1.8 && <1.13     , tls >=1.4 && <2-    , typerep-map >=0.3 && <0.6+    , typerep-map >=0.5 && <0.7     , unagi-chan ==0.4.*     , unboxing-vector ==0.2.*     , unordered-containers ==0.2.*-    , vector ==0.12.*+    , vector >=0.12 && <0.14     , websockets >=0.12 && <0.13     , x509-system >=1.6.6 && <1.7     , random >=1.2 && <1.3