-- | Contains stuff for managing shards
module Calamity.Client.ShardManager (shardBot) where

import Calamity.Client.Types
import Calamity.Gateway
import Calamity.HTTP
import Calamity.Internal.Utils
import Control.Concurrent.MVar
import Control.Concurrent.STM
import Control.Monad
import qualified Data.Text as T
import Data.Traversable
import Optics
import Polysemy (Sem)
import qualified Polysemy as P
import qualified Polysemy.Fail as P
import qualified Polysemy.Reader as P
import PyF

mapLeft :: (a -> c) -> Either a b -> Either c b
mapLeft :: forall a c b. (a -> c) -> Either a b -> Either c b
mapLeft a -> c
f (Left a
a) = forall a b. a -> Either a b
Left forall a b. (a -> b) -> a -> b
$ a -> c
f a
a
mapLeft a -> c
_ (Right b
b) = forall a b. b -> Either a b
Right b
b

-- | Connects the bot to the gateway over n shards
shardBot :: BotC r => Maybe StatusUpdateData -> Intents -> Sem r (Either StartupError ())
shardBot :: forall (r :: EffectRow).
BotC r =>
Maybe StatusUpdateData -> Intents -> Sem r (Either StartupError ())
shardBot Maybe StatusUpdateData
initialStatus Intents
intents = (forall a c b. (a -> c) -> Either a b -> Either c b
mapLeft String -> StartupError
StartupError forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (r :: EffectRow) a.
Sem (Fail : r) a -> Sem r (Either String a)
P.runFail forall a b. (a -> b) -> a -> b
$ do
  MVar Int
numShardsVar <- forall i j (r :: EffectRow).
Member (Reader i) r =>
(i -> j) -> Sem r j
P.asks Client -> MVar Int
numShards
  TVar [(InChan ControlMessage, Async (Maybe ()))]
shardsVar <- forall i j (r :: EffectRow).
Member (Reader i) r =>
(i -> j) -> Sem r j
P.asks Client -> TVar [(InChan ControlMessage, Async (Maybe ()))]
shards

  Bool
hasShards <- forall (m :: * -> *) (r :: EffectRow) a.
Member (Embed m) r =>
m a -> Sem r a
P.embed forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) a. Foldable t => t a -> Bool
null forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. TVar a -> IO a
readTVarIO TVar [(InChan ControlMessage, Async (Maybe ()))]
shardsVar
  forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
hasShards forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"don't use shardBot on an already running bot."

  Token
token <- forall i j (r :: EffectRow).
Member (Reader i) r =>
(i -> j) -> Sem r j
P.asks Client -> Token
Calamity.Client.Types.token
  InChan CalamityEvent
inc <- forall i j (r :: EffectRow).
Member (Reader i) r =>
(i -> j) -> Sem r j
P.asks (forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. forall a. IsLabel "eventsIn" a => a
#eventsIn)

  Right BotGatewayResponse
gateway <- forall (r :: EffectRow) a.
(Members '[RatelimitEff, TokenEff, LogEff, MetricEff, Embed IO] r,
 Request a, ReadResponse (Result a)) =>
a -> Sem r (Either RestError (Result a))
invoke MiscRequest BotGatewayResponse
GetGatewayBot

  let numShards' :: Int
numShards' = BotGatewayResponse
gateway forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. forall a. IsLabel "shards" a => a
#shards
  let host :: Text
host = BotGatewayResponse
gateway forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. forall a. IsLabel "url" a => a
#url
  forall (m :: * -> *) (r :: EffectRow) a.
Member (Embed m) r =>
m a -> Sem r a
P.embed forall a b. (a -> b) -> a -> b
$ forall a. MVar a -> a -> IO ()
putMVar MVar Int
numShardsVar Int
numShards'

  forall (r :: EffectRow). Member LogEff r => Text -> Sem r ()
info forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack forall a b. (a -> b) -> a -> b
$ String
"Number of shards: " forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> String
show Int
numShards'

  [(InChan ControlMessage, Async (Maybe ()))]
shards <- forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
t a -> (a -> f b) -> f (t b)
for [Int
0 .. Int
numShards' forall a. Num a => a -> a -> a
- Int
1] forall a b. (a -> b) -> a -> b
$ \Int
id ->
    forall (r :: EffectRow).
Members '[LogEff, MetricEff, Embed IO, Final IO, Async] r =>
Text
-> Int
-> Int
-> Token
-> Maybe StatusUpdateData
-> Intents
-> InChan CalamityEvent
-> Sem r (InChan ControlMessage, Async (Maybe ()))
newShard Text
host Int
id Int
numShards' Token
token Maybe StatusUpdateData
initialStatus Intents
intents InChan CalamityEvent
inc

  forall (m :: * -> *) (r :: EffectRow) a.
Member (Embed m) r =>
m a -> Sem r a
P.embed forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. STM a -> IO a
atomically forall a b. (a -> b) -> a -> b
$ forall a. TVar a -> a -> STM ()
writeTVar TVar [(InChan ControlMessage, Async (Maybe ()))]
shardsVar [(InChan ControlMessage, Async (Maybe ()))]
shards