calamity-0.1.7.0: src/Calamity/Client/Types.hs
-- | Types for the client
module Calamity.Client.Types
( Client(..)
, EventType(..)
, EHType
, BotC
, SetupEff
, EventHandlers(..)
, InsertEventHandler(..)
, getEventHandlers
, getCustomEventHandlers ) where
import Calamity.Cache.Eff
import Calamity.Gateway.DispatchEvents ( CalamityEvent(..), ReadyData )
import Calamity.Gateway.Types ( ControlMessage )
import Calamity.HTTP.Internal.Types
import Calamity.Metrics.Eff
import Calamity.Types.LogEff
import Calamity.Types.Model.Channel
import Calamity.Types.Model.Guild
import Calamity.Types.Model.User
import Calamity.Types.Token
import Calamity.Types.UnixTimestamp
import Control.Concurrent.Async
import Control.Concurrent.Chan.Unagi
import Control.Concurrent.MVar
import Control.Concurrent.STM.TVar
import Data.Default.Class
import Data.Dynamic
import Data.Functor
import qualified Data.HashMap.Lazy as LH
import Data.Maybe
import Data.Time
import qualified Data.TypeRepMap as TM
import Data.TypeRepMap ( TypeRepMap, WrapTypeable(..) )
import Data.Typeable
import Data.Void
import GHC.Exts ( fromList )
import GHC.Generics
import qualified Polysemy as P
import qualified Polysemy.Async as P
import qualified Polysemy.AtomicState as P
import qualified Polysemy.Reader as P
data Client = Client
{ shards :: TVar [(InChan ControlMessage, Async (Maybe ()))]
, numShards :: MVar Int
, token :: Token
, rlState :: RateLimitState
, eventsIn :: InChan CalamityEvent
, eventsOut :: OutChan CalamityEvent
}
deriving ( Generic )
type BotC r =
( P.Members '[LogEff, MetricEff, CacheEff, P.Reader Client,
P.AtomicState EventHandlers, P.Embed IO, P.Final IO, P.Async] r
, Typeable r)
-- | A concrete effect stack used inside the bot
type SetupEff r = P.Sem (LogEff ': P.Reader Client ': P.AtomicState EventHandlers ': P.Async ': r) ()
-- | A Data Kind used to fire custom events
data EventType
= ReadyEvt
| ChannelCreateEvt
| ChannelUpdateEvt
| ChannelDeleteEvt
| ChannelpinsUpdateEvt
| GuildCreateEvt
| GuildUpdateEvt
| GuildDeleteEvt
| GuildBanAddEvt
| GuildBanRemoveEvt
| GuildEmojisUpdateEvt
| GuildIntegrationsUpdateEvt
| GuildMemberAddEvt
| GuildMemberRemoveEvt
| GuildMemberUpdateEvt
| GuildMembersChunkEvt
| GuildRoleCreateEvt
| GuildRoleUpdateEvt
| GuildRoleDeleteEvt
| MessageCreateEvt
| MessageUpdateEvt
| MessageDeleteEvt
| MessageDeleteBulkEvt
| MessageReactionAddEvt
| MessageReactionRemoveEvt
| MessageReactionRemoveAllEvt
| TypingStartEvt
| UserUpdateEvt
| forall s a. CustomEvt s a
-- ^ A custom event, @s@ is the name and @a@ is the data sent to the handler
-- | A type family to decide what the parameters for an event handler should be
-- determined by the type of event it is handling.
type family EHType (d :: EventType) m where
EHType 'ReadyEvt m = ReadyData -> m ()
EHType 'ChannelCreateEvt m = Channel -> m ()
EHType 'ChannelUpdateEvt m = Channel -> Channel -> m ()
EHType 'ChannelDeleteEvt m = Channel -> m ()
EHType 'ChannelpinsUpdateEvt m = Channel -> Maybe UTCTime -> m ()
EHType 'GuildCreateEvt m = Guild -> Bool -> m ()
EHType 'GuildUpdateEvt m = Guild -> Guild -> m ()
EHType 'GuildDeleteEvt m = Guild -> Bool -> m ()
EHType 'GuildBanAddEvt m = Guild -> User -> m ()
EHType 'GuildBanRemoveEvt m = Guild -> User -> m ()
EHType 'GuildEmojisUpdateEvt m = Guild -> [Emoji] -> m ()
EHType 'GuildIntegrationsUpdateEvt m = Guild -> m ()
EHType 'GuildMemberAddEvt m = Member -> m ()
EHType 'GuildMemberRemoveEvt m = Member -> m ()
EHType 'GuildMemberUpdateEvt m = Member -> Member -> m ()
EHType 'GuildMembersChunkEvt m = Guild -> [Member] -> m ()
EHType 'GuildRoleCreateEvt m = Guild -> Role -> m ()
EHType 'GuildRoleUpdateEvt m = Guild -> Role -> Role -> m ()
EHType 'GuildRoleDeleteEvt m = Guild -> Role -> m ()
EHType 'MessageCreateEvt m = Message -> m ()
EHType 'MessageUpdateEvt m = Message -> Message -> m ()
EHType 'MessageDeleteEvt m = Message -> m ()
EHType 'MessageDeleteBulkEvt m = [Message] -> m ()
EHType 'MessageReactionAddEvt m = Message -> Reaction -> m ()
EHType 'MessageReactionRemoveEvt m = Message -> Reaction -> m ()
EHType 'MessageReactionRemoveAllEvt m = Message -> m ()
EHType 'TypingStartEvt m = Channel -> Maybe Member -> UnixTimestamp -> m ()
EHType 'UserUpdateEvt m = User -> User -> m ()
EHType ('CustomEvt s a) m = a -> m ()
type family EHType' (d :: EventType) where
EHType' 'ReadyEvt = Dynamic
EHType' 'ChannelCreateEvt = Dynamic
EHType' 'ChannelUpdateEvt = Dynamic
EHType' 'ChannelDeleteEvt = Dynamic
EHType' 'ChannelpinsUpdateEvt = Dynamic
EHType' 'GuildCreateEvt = Dynamic
EHType' 'GuildUpdateEvt = Dynamic
EHType' 'GuildDeleteEvt = Dynamic
EHType' 'GuildBanAddEvt = Dynamic
EHType' 'GuildBanRemoveEvt = Dynamic
EHType' 'GuildEmojisUpdateEvt = Dynamic
EHType' 'GuildIntegrationsUpdateEvt = Dynamic
EHType' 'GuildMemberAddEvt = Dynamic
EHType' 'GuildMemberRemoveEvt = Dynamic
EHType' 'GuildMemberUpdateEvt = Dynamic
EHType' 'GuildMembersChunkEvt = Dynamic
EHType' 'GuildRoleCreateEvt = Dynamic
EHType' 'GuildRoleUpdateEvt = Dynamic
EHType' 'GuildRoleDeleteEvt = Dynamic
EHType' 'MessageCreateEvt = Dynamic
EHType' 'MessageUpdateEvt = Dynamic
EHType' 'MessageDeleteEvt = Dynamic
EHType' 'MessageDeleteBulkEvt = Dynamic
EHType' 'MessageReactionAddEvt = Dynamic
EHType' 'MessageReactionRemoveEvt = Dynamic
EHType' 'MessageReactionRemoveAllEvt = Dynamic
EHType' 'TypingStartEvt = Dynamic
EHType' 'UserUpdateEvt = Dynamic
EHType' ('CustomEvt _ _) = Dynamic
newtype EventHandlers = EventHandlers (TypeRepMap EventHandler)
type family EHStorageType t where
EHStorageType ('CustomEvt s a) = LH.HashMap TypeRep (LH.HashMap TypeRep [Dynamic])
EHStorageType t = [EHType' t]
newtype EventHandler t = EH
{ unwrapEventHandler :: (Semigroup (EHStorageType t), Monoid (EHStorageType t)) => EHStorageType t
}
instance Semigroup (EventHandler t) where
EH a <> EH b = EH $ a <> b
instance Monoid (EventHandler t) where
mempty = EH mempty
instance Default EventHandlers where
def = EventHandlers $ fromList [ WrapTypeable $ EH @'ReadyEvt []
, WrapTypeable $ EH @'ChannelCreateEvt []
, WrapTypeable $ EH @'ChannelUpdateEvt []
, WrapTypeable $ EH @'ChannelDeleteEvt []
, WrapTypeable $ EH @'ChannelpinsUpdateEvt []
, WrapTypeable $ EH @'GuildCreateEvt []
, WrapTypeable $ EH @'GuildUpdateEvt []
, WrapTypeable $ EH @'GuildDeleteEvt []
, WrapTypeable $ EH @'GuildBanAddEvt []
, WrapTypeable $ EH @'GuildBanRemoveEvt []
, WrapTypeable $ EH @'GuildEmojisUpdateEvt []
, WrapTypeable $ EH @'GuildIntegrationsUpdateEvt []
, WrapTypeable $ EH @'GuildMemberAddEvt []
, WrapTypeable $ EH @'GuildMemberRemoveEvt []
, WrapTypeable $ EH @'GuildMemberUpdateEvt []
, WrapTypeable $ EH @'GuildMembersChunkEvt []
, WrapTypeable $ EH @'GuildRoleCreateEvt []
, WrapTypeable $ EH @'GuildRoleUpdateEvt []
, WrapTypeable $ EH @'GuildRoleDeleteEvt []
, WrapTypeable $ EH @'MessageCreateEvt []
, WrapTypeable $ EH @'MessageUpdateEvt []
, WrapTypeable $ EH @'MessageDeleteEvt []
, WrapTypeable $ EH @'MessageDeleteBulkEvt []
, WrapTypeable $ EH @'MessageReactionAddEvt []
, WrapTypeable $ EH @'MessageReactionRemoveEvt []
, WrapTypeable $ EH @'MessageReactionRemoveAllEvt []
, WrapTypeable $ EH @'TypingStartEvt []
, WrapTypeable $ EH @'UserUpdateEvt []
, WrapTypeable $ EH @('CustomEvt Void Void) LH.empty
]
instance Semigroup EventHandlers where
(EventHandlers a) <> (EventHandlers b) = EventHandlers $ TM.unionWith (<>) a b
instance Monoid EventHandlers where
mempty = def
-- not sure what to think of this
type family EHInstanceSelector (d :: EventType) :: Bool where
EHInstanceSelector ('CustomEvt _ _) = 'True
EHInstanceSelector _ = 'False
-- | A helper typeclass that is used to decide how to register regular
-- events, and custom events which require storing in a map at runtime.
class InsertEventHandler a m where
makeEventHandlers :: Proxy a -> Proxy m -> EHType a m -> EventHandlers
instance (EHInstanceSelector a ~ flag, InsertEventHandler' flag a m) => InsertEventHandler a m where
makeEventHandlers = makeEventHandlers' (Proxy @flag)
class InsertEventHandler' (flag :: Bool) a m where
makeEventHandlers' :: Proxy flag -> Proxy a -> Proxy m -> EHType a m -> EventHandlers
instance (Typeable a, Typeable s, Typeable (EHType ('CustomEvt s a) m))
=> InsertEventHandler' 'True ('CustomEvt s a) m where
makeEventHandlers' _ _ _ handler = EventHandlers . TM.one $ EH @('CustomEvt Void Void)
(LH.singleton (typeRep $ Proxy @s) (LH.singleton (typeRep $ Proxy @a) [toDyn handler]))
instance (Typeable s, EHStorageType s ~ [Dynamic], Typeable (EHType s m)) => InsertEventHandler' 'False s m where
makeEventHandlers' _ _ _ handler = EventHandlers . TM.one $ EH @s [toDyn handler]
class GetEventHandlers a m where
getEventHandlers :: EventHandlers -> [EHType a m]
instance (EHInstanceSelector a ~ flag, GetEventHandlers' flag a m) => GetEventHandlers a m where
getEventHandlers = getEventHandlers' (Proxy @a) (Proxy @m) (Proxy @flag)
class GetEventHandlers' (flag :: Bool) a m where
getEventHandlers' :: Proxy a -> Proxy m -> Proxy flag -> EventHandlers -> [EHType a m]
instance (Typeable a, Typeable s, Typeable (EHType ('CustomEvt s a) m)) => GetEventHandlers' 'True ('CustomEvt s a) m where
getEventHandlers' _ _ _ (EventHandlers handlers) =
let handlerMap = unwrapEventHandler @('CustomEvt Void Void) $ fromJust
(TM.lookup handlers :: Maybe (EventHandler ('CustomEvt Void Void)))
in concat $ LH.lookup (typeRep $ Proxy @s) handlerMap >>= LH.lookup (typeRep $ Proxy @a) <&> map
(fromJust . fromDynamic)
instance (Typeable s, Typeable (EHType s m), EHStorageType s ~ [Dynamic]) => GetEventHandlers' 'False s m where
getEventHandlers' _ _ _ (EventHandlers handlers) =
let theseHandlers = unwrapEventHandler @s $ fromJust (TM.lookup handlers :: Maybe (EventHandler s))
in map (fromJust . fromDynamic) theseHandlers
getCustomEventHandlers :: TypeRep -> TypeRep -> EventHandlers -> [Dynamic]
getCustomEventHandlers s a (EventHandlers handlers) =
let handlerMap = unwrapEventHandler @('CustomEvt Void Void) $ fromJust
(TM.lookup handlers :: Maybe (EventHandler ('CustomEvt Void Void)))
in concat $ LH.lookup s handlerMap >>= LH.lookup a