feat: notify on new room (wip)

This commit is contained in:
qvalentin 2025-01-19 13:30:56 +01:00
parent 6e285076d4
commit 92a97e8f8a
12 changed files with 119 additions and 14 deletions

View file

@ -3,7 +3,8 @@
## Development ## Development
`stack build` `stack build`
`stack stack run`
`stack run`
## Debug ## Debug
@ -12,3 +13,44 @@ Using websocat
`websocat ws://127.0.0.1:9160` `websocat ws://127.0.0.1:9160`
`curl --data 'body data' localhost:8081` `curl --data 'body data' localhost:8081`
```
curl -X POST localhost:8081/roomdata \
-H "Content-Type: application/json" \
-d '[
{
"roomName": "ConferenceRoom1",
"participants": [
{
"jid": "participant1@example.com",
"email": "participant1@example.com",
"displayName": "Alice",
"avatarURL": "https://example.com/avatars/alice.png"
},
{
"jid": "participant2@example.com",
"email": "participant2@example.com",
"displayName": "Bob",
"avatarURL": "https://example.com/avatars/bob.png"
}
]
},
{
"roomName": "ConferenceRoom2",
"participants": [
{
"jid": "participant3@example.com",
"email": "participant3@example.com",
"displayName": "Charlie",
"avatarURL": "https://example.com/avatars/charlie.png"
},
{
"jid": "participant4@example.com",
"email": "participant4@example.com",
"displayName": "Dana",
"avatarURL": "https://example.com/avatars/dana.png"
}
]
}
]'
```

View file

@ -8,4 +8,6 @@ import Lib (runBothServers)
main :: IO () main :: IO ()
main = do main = do
setLocaleEncoding utf8 setLocaleEncoding utf8
hSetBuffering stdout LineBuffering
hSetBuffering stderr LineBuffering
runBothServers runBothServers

View file

@ -1,6 +1,6 @@
cabal-version: 1.12 cabal-version: 1.12
-- This file has been generated from package.yaml by hpack version 0.35.2. -- This file has been generated from package.yaml by hpack version 0.37.0.
-- --
-- see: https://github.com/sol/hpack -- see: https://github.com/sol/hpack
@ -62,6 +62,7 @@ library
, http-types , http-types
, lifted-base , lifted-base
, mtl , mtl
, process
, text , text
, time , time
, uuid , uuid
@ -89,6 +90,7 @@ executable jitsi-rooms-exe
, jitsi-rooms , jitsi-rooms
, lifted-base , lifted-base
, mtl , mtl
, process
, text , text
, time , time
, uuid , uuid

View file

@ -34,6 +34,7 @@ dependencies:
- mtl - mtl
- time - time
- wai-extra - wai-extra
- process
ghc-options: ghc-options:
- -Wall - -Wall

10
backend/shell.nix Normal file
View file

@ -0,0 +1,10 @@
{ pkgs ? import <nixpkgs> { } }:
pkgs.mkShell {
buildInputs = with pkgs; [
haskell-language-server
stack
stylish-haskell
];
}

View file

@ -10,6 +10,7 @@ import ClassyPrelude
import Control.Monad.Except (MonadError, throwError) import Control.Monad.Except (MonadError, throwError)
import Data.Aeson (eitherDecodeStrict) import Data.Aeson (eitherDecodeStrict)
import Data.Aeson.Types (FromJSON) import Data.Aeson.Types (FromJSON)
import GHC.IO.Exception (ExitCode (ExitSuccess))
import Network.HTTP.Types (status200, status500) import Network.HTTP.Types (status200, status500)
import Network.Wai import Network.Wai
( ResponseReceived, ( ResponseReceived,
@ -21,8 +22,12 @@ import State.RoomDataState
MonadRoomDataStateRead, MonadRoomDataStateRead,
) )
import State.RoomsState import State.RoomsState
( roomStateDiffers, ( roomStateDiffInOpenRooms,
roomStateDiffers,
) )
import System.Process
import Text.Printf (printf)
import Types.RoomData (RoomData, prettyPrintOpenedRoom)
import Types.WebEnv import Types.WebEnv
( HasWebEnv (getRequest), ( HasWebEnv (getRequest),
getRespond, getRespond,
@ -42,6 +47,11 @@ roomDataHandler = do
newRoomData <- parseBodyOrBadRequest newRoomData <- parseBodyOrBadRequest
liftIO $ putStrLn "Got triggered from prosody" liftIO $ putStrLn "Got triggered from prosody"
whenM (roomStateDiffers newRoomData) $ do whenM (roomStateDiffers newRoomData) $ do
(openedRooms, closedRooms) <- roomStateDiffInOpenRooms newRoomData
mapM_ notifyRoomOpend openedRooms
mapM_ notifyRoomClosed closedRooms
setRoomDataState newRoomData setRoomDataState newRoomData
broadcastUserData broadcastUserData
success success
@ -55,6 +65,7 @@ parseBodyOrBadRequest ::
) => ) =>
m a m a
parseBodyOrBadRequest = do parseBodyOrBadRequest = do
liftIO $ putStrLn "Parsing body"
body <- getRequestBody body <- getRequestBody
case eitherDecodeStrict body of case eitherDecodeStrict body of
Left errorMessage -> do Left errorMessage -> do
@ -103,3 +114,17 @@ success = do
status200 status200
[("Content-Type", "text/plain")] [("Content-Type", "text/plain")]
"" ""
notifyRoomOpend :: (MonadIO m) => RoomData -> m ()
notifyRoomOpend room = do
let (name, user) = prettyPrintOpenedRoom room
liftIO $ printf "Room %s opened by %s\n" name user
exitCode <- liftIO $ system $ printf "/nix/store/23nz5mjk3dj7027byc6g5avx3mfwwnqm-libnotify-0.8.3/bin/notify-send 'Room %s opened by %s'" name user
when (exitCode /= ExitSuccess) $ liftIO $ printf "Failed to notify room %s opened by %s\n" name user
notifyRoomClosed :: (MonadIO m) => RoomData -> m ()
notifyRoomClosed room = do
let (name, _) = prettyPrintOpenedRoom room
liftIO $ printf "Room %s closed\n" name
exitCode <- liftIO $ system $ printf "/nix/store/23nz5mjk3dj7027byc6g5avx3mfwwnqm-libnotify-0.8.3/bin/notify-send 'Room %s closed'" name
when (exitCode /= ExitSuccess) $ liftIO $ printf "Failed to notify room %s closed\n" name

View file

@ -3,6 +3,7 @@ module State.RoomsState
initRoomsState, initRoomsState,
HasRoomsState (..), HasRoomsState (..),
roomStateDiffers, roomStateDiffers,
roomStateDiffInOpenRooms,
updateRoomState, updateRoomState,
getRoomState, getRoomState,
) )
@ -11,7 +12,7 @@ where
import ClassyPrelude import ClassyPrelude
import State.GenericTVarState import State.GenericTVarState
import State.RoomDataState (MonadRoomDataStateRead (getRoomDataState)) import State.RoomDataState (MonadRoomDataStateRead (getRoomDataState))
import Types.RoomData (RoomsData) import Types.RoomData (RoomsData, roomNotEmpty, sameName)
type RoomsState = GenericTVarState RoomsData type RoomsState = GenericTVarState RoomsData
@ -52,5 +53,18 @@ roomStateDiffers ::
roomStateDiffers newData = do roomStateDiffers newData = do
not . eqIgnoreOrdering newData <$> getRoomDataState not . eqIgnoreOrdering newData <$> getRoomDataState
roomStateDiffInOpenRooms ::
( MonadRoomDataStateRead m
) =>
RoomsData ->
m (RoomsData, RoomsData)
roomStateDiffInOpenRooms newData = do
current <- getRoomDataState
let newRooms = filter roomNotEmpty $ filter (\newRoom -> isNothing $ find (sameName newRoom) current) newData
let oldRooms = filter (\newRoom -> isJust $ find (sameName newRoom) current) newData
return (newRooms, oldRooms)
eqIgnoreOrdering :: (Eq a) => [a] -> [a] -> Bool eqIgnoreOrdering :: (Eq a) => [a] -> [a] -> Bool
eqIgnoreOrdering a b = length a == length b && all (`elem` b) a eqIgnoreOrdering a b = length a == length b && all (`elem` b) a

View file

@ -1,6 +1,6 @@
{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveGeneric #-}
module Types.Participant (Participant) where module Types.Participant (Participant (Participant, displayName)) where
import ClassyPrelude import ClassyPrelude
import Data.Aeson (FromJSON, ToJSON) import Data.Aeson (FromJSON, ToJSON)

View file

@ -1,10 +1,10 @@
{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveGeneric #-}
module Types.RoomData (RoomData, RoomsData) where module Types.RoomData (RoomData, RoomsData, sameName, roomNotEmpty, prettyPrintOpenedRoom) where
import ClassyPrelude import ClassyPrelude
import Data.Aeson (FromJSON, ToJSON) import Data.Aeson (FromJSON, ToJSON)
import Types.Participant (Participant) import Types.Participant (Participant (Participant, displayName))
data RoomData = RoomData data RoomData = RoomData
{ roomName :: RoomName, { roomName :: RoomName,
@ -12,6 +12,16 @@ data RoomData = RoomData
} }
deriving (Generic, Show, Eq) deriving (Generic, Show, Eq)
sameName :: RoomData -> RoomData -> Bool
sameName RoomData {roomName = name1} RoomData {roomName = name2} = name1 == name2
roomNotEmpty :: RoomData -> Bool
roomNotEmpty RoomData {participants = participants} = not $ null participants
prettyPrintOpenedRoom :: RoomData -> (Text, Text)
prettyPrintOpenedRoom RoomData {roomName = roomName, participants = participants} =
(roomName, fromMaybe "" (headMay (map (\Participant {displayName = displayName} -> displayName) participants)))
type RoomName = Text type RoomName = Text
type RoomsData = [RoomData] type RoomsData = [RoomData]

View file

@ -16,7 +16,7 @@
# this way we can use prebuild binaries for hls # this way we can use prebuild binaries for hls
#resolver: nightly-2022-11-12 #resolver: nightly-2022-11-12
#resolver: ghc-9.2.4 #resolver: ghc-9.2.4
resolver: lts-20.26 resolver: lts-22.43
# #
# The location of a snapshot can be provided as a file or url. Stack assumes # The location of a snapshot can be provided as a file or url. Stack assumes
# a snapshot provided as a file might change, whereas a url resource does not. # a snapshot provided as a file might change, whereas a url resource does not.
@ -73,5 +73,3 @@ packages:
# compiler-check: newer-minor # compiler-check: newer-minor
ghc-options: ghc-options:
"$everything": -haddock "$everything": -haddock

View file

@ -6,7 +6,7 @@
packages: [] packages: []
snapshots: snapshots:
- completed: - completed:
sha256: 5a59b2a405b3aba3c00188453be172b85893cab8ebc352b1ef58b0eae5d248a2 sha256: 08bd13ce621b41a8f5e51456b38d5b46d7783ce114a50ab604d6bbab0d002146
size: 650475 size: 720271
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/26.yaml url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/22/43.yaml
original: lts-20.26 original: lts-22.43

1
frontend/result Symbolic link
View file

@ -0,0 +1 @@
/nix/store/x3fqdrf05dcg52s7x6dkzql551g0bfxw-my-node-app-1.0.0