{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_HADDOCK prune, not-home #-}
module Discord.Gateway.Cache where
import Prelude hiding (log)
import Data.Monoid ((<>))
import Control.Concurrent (forkIO)
import Control.Concurrent.MVar
import Control.Concurrent.Chan
import qualified Data.Map.Strict as M
import Discord.Types
import Discord.Gateway.EventLoop
data Cache = Cache
{ _currentUser :: User
, _dmChannels :: M.Map ChannelId Channel
, _guilds :: M.Map GuildId (Guild, GuildInfo)
, _channels :: M.Map ChannelId Channel
} deriving (Show)
emptyCache :: IO (MVar (Either GatewayException Cache))
emptyCache = newEmptyMVar
cacheAddEventLoopFork :: MVar (Either GatewayException Cache) -> Chan (Either GatewayException Event) -> Chan String -> IO ()
cacheAddEventLoopFork cache eventChan log = do
ready <- readChan eventChan
case ready of
Right (Ready _ user dmChannels _unavailableGuilds _) -> do
let dmChans = M.fromList (zip (map channelId dmChannels) dmChannels)
putMVar cache (Right (Cache user dmChans M.empty M.empty))
_ <- forkIO loop
pure ()
Right r -> do
writeChan log ("cache - expected Ready event, but got " <> show r)
cacheAddEventLoopFork cache eventChan log
Left e -> do
writeChan log "cache - gateway exception, stopping cache"
putMVar cache (Left e)
where
loop :: IO ()
loop = do
eventOrExcept <- readChan eventChan
minfo <- takeMVar cache
case (eventOrExcept, minfo) of
(_, Left _) -> pure ()
(Left exception, _) -> do putMVar cache (Left exception)
(Right event, Right info) -> do
putMVar cache (Right (adjustCache info event))
loop
adjustCache :: Cache -> Event -> Cache
adjustCache minfo event = case event of
GuildCreate guild info ->
let newChans = map (setChanGuildID (guildId guild)) $ guildChannels info
g = M.insert (guildId guild) (guild, info { guildChannels = newChans }) (_guilds minfo)
c = M.unionWith (\a _ -> a)
(M.fromList [ (channelId ch, ch) | ch <- newChans ])
(_channels minfo)
in minfo { _guilds = g, _channels = c }
_ -> minfo
setChanGuildID :: GuildId -> Channel -> Channel
setChanGuildID s c = if channelIsInGuild c
then c { channelGuild = s }
else c