jitsi-roomsv2/backend/src/WebSocket.hs

178 lines
4.1 KiB
Haskell
Raw Normal View History

2023-01-29 12:08:50 +01:00
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
2023-01-27 18:34:28 +01:00
{-# LANGUAGE UndecidableInstances #-}
2023-01-27 18:34:28 +01:00
2023-01-27 19:53:34 +01:00
module WebSocket (broadCastToClients, runWebSocketServer) where
2023-01-15 18:26:41 +01:00
2023-01-29 12:08:50 +01:00
import ClassyPrelude
import Data.UUID (UUID)
import Data.UUID.V4 (nextRandom)
import qualified Network.WebSockets as WS
import Types.AppTypes (Env (connectedClientsState),
HasConnectedClientState (getConnectedClientState))
import Types.ConnectionState (Client (..), ConnectedClients)
2023-01-27 19:53:34 +01:00
data WSEnv = WSEnv
2023-01-29 12:08:50 +01:00
{ appEnv :: Env,
2023-01-27 19:53:34 +01:00
connection :: WS.Connection,
2023-01-29 12:08:50 +01:00
clientId :: UUID
2023-01-27 19:53:34 +01:00
}
instance HasConnectedClientState WSEnv where
getConnectedClientState = connectedClientsState . appEnv
class HasWSEnv a where
getConn :: a -> WS.Connection
getClientId :: a -> UUID
instance HasWSEnv WSEnv where
getConn = connection
getClientId = clientId
addWSClient ::
( HasConnectedClientState env,
MonadReader env m,
MonadUnliftIO m
) =>
Client ->
m ()
addWSClient client = do
state <- getConnectedClientState <$> ask
modifyMVar_ state $ \s -> do
let s' = addClient client s
return s'
2023-01-15 18:26:41 +01:00
2023-01-27 18:34:28 +01:00
addClient :: Client -> ConnectedClients -> ConnectedClients
2023-01-15 18:26:41 +01:00
addClient client clients = client : clients
2023-01-27 19:53:34 +01:00
disconnectWsClient ::
( HasWSEnv env,
HasConnectedClientState env,
MonadReader env m,
MonadUnliftIO m
) =>
m ()
disconnectWsClient = do
clientId <- getClientId <$> ask
state <- getConnectedClientState <$> ask
modifyMVar_ state $ \s ->
let s' = removeClient clientId s
in return s'
joinRoom ::
( HasWSEnv env,
HasConnectedClientState env,
MonadReader env m,
MonadUnliftIO m
) =>
m ()
joinRoom = do
clientId <- getClientId <$> ask
state <- getConnectedClientState <$> ask
modifyMVar_ state $ \s ->
let s' = updateClient clientId (\c -> c {joinedRoom = True}) s
in return s'
2023-01-27 18:34:28 +01:00
removeClient :: UUID -> ConnectedClients -> ConnectedClients
removeClient toRemove = filter ((/= toRemove) . uuid)
2023-01-15 18:26:41 +01:00
updateClient :: UUID -> (Client -> Client) -> ConnectedClients -> ConnectedClients
updateClient toUpdate f allClients = do
client <- allClients
if (toUpdate == uuid client)
then (return $ f client)
else return client
2023-01-27 18:34:28 +01:00
broadcast :: Text -> ConnectedClients -> IO ()
2023-01-15 18:26:41 +01:00
broadcast message clients = do
putStrLn message
2023-01-27 18:34:28 +01:00
forM_ clients $ \client -> WS.sendTextData (conn client) message
runWebSocketServer ::
( MonadIO m,
MonadReader Env m
) =>
m ()
runWebSocketServer = do
putStrLn "Websocket up at 127.0.0.1:9160"
2023-01-27 19:53:34 +01:00
wsApp' <- runWSApp
liftIO $ WS.runServer "127.0.0.1" 9160 wsApp'
2023-01-27 18:34:28 +01:00
runWSApp ::
( MonadIO m,
MonadReader Env m
) =>
m WS.ServerApp
runWSApp = do
env <- ask
return
( \pending -> do
conn <- WS.acceptRequest pending
uuid <- nextRandom
let wsEnv = WSEnv {appEnv = env, connection = conn, clientId = uuid}
WS.withPingThread conn 30 (return ()) $ do
unWSApp wsApp wsEnv
)
newClient ::
( MonadIO m,
HasWSEnv env,
MonadReader env m
) =>
Text ->
m Client
newClient name = do
env <- ask
return $ Client {uuid = getClientId env, name = name, conn = getConn env, joinedRoom = False}
2023-01-27 18:34:28 +01:00
newtype WSApp env a = WSApp {unWSApp :: env -> IO a}
deriving
( Functor,
Applicative,
Monad,
MonadReader env,
MonadIO,
MonadUnliftIO
)
via ReaderT env IO
wsApp ::
( HasWSEnv env,
HasConnectedClientState env,
MonadReader env m,
MonadUnliftIO m
) =>
m ()
wsApp = do
msg <- getMessage
putStrLn msg
client <- newClient msg
2023-01-27 19:53:34 +01:00
addWSClient client
2023-01-27 18:34:28 +01:00
flip finally disconnectWsClient $ do
forever $ do
currentMsg <- getMessage
joinRoom
2023-01-27 18:34:28 +01:00
putStrLn currentMsg
getMessage ::
( HasWSEnv env,
MonadIO m,
MonadReader env m
) =>
m Text
getMessage = do
conn' <- getConn <$> ask
liftIO $ WS.receiveData conn'
2023-01-27 19:53:34 +01:00
broadCastToClients ::
2023-01-27 18:34:28 +01:00
( MonadIO m,
HasConnectedClientState env,
MonadReader env m
) =>
2023-01-27 19:53:34 +01:00
Text ->
2023-01-27 18:34:28 +01:00
m ()
2023-01-27 19:53:34 +01:00
broadCastToClients message = do
2023-01-27 18:34:28 +01:00
state <- getConnectedClientState <$> ask
2023-01-27 19:53:34 +01:00
liftIO $ withMVar state $ \currenState -> broadcast message currenState