summaryrefslogtreecommitdiff
path: root/fig-frontend
diff options
context:
space:
mode:
authorLLLL Colonq <llll@colonq>2024-08-04 02:09:37 -0400
committerLLLL Colonq <llll@colonq>2024-08-04 02:09:37 -0400
commit7b101b8c3c0481d76733c77008b5b763ceb3b535 (patch)
treea2a8b3c46f4b6ae87eb288d7834be8b254522da2 /fig-frontend
parent790b517b8e64c46bcd6bb71637d2d0eb68c51abd (diff)
Live monitor endpoint
Diffstat (limited to 'fig-frontend')
-rw-r--r--fig-frontend/fig-frontend.cabal1
-rw-r--r--fig-frontend/src/Fig/Frontend.hs47
-rw-r--r--fig-frontend/src/Fig/Frontend/Utils.hs12
3 files changed, 52 insertions, 8 deletions
diff --git a/fig-frontend/fig-frontend.cabal b/fig-frontend/fig-frontend.cabal
index f3c4f2e..9fc8f8e 100644
--- a/fig-frontend/fig-frontend.cabal
+++ b/fig-frontend/fig-frontend.cabal
@@ -42,6 +42,7 @@ common deps
, wai
, wai-extra
, wai-middleware-static
+ , wai-websockets
, warp
, websockets
, wuss
diff --git a/fig-frontend/src/Fig/Frontend.hs b/fig-frontend/src/Fig/Frontend.hs
index 51938dc..635df45 100644
--- a/fig-frontend/src/Fig/Frontend.hs
+++ b/fig-frontend/src/Fig/Frontend.hs
@@ -7,14 +7,18 @@ import Fig.Prelude
import System.Random (randomRIO)
import Control.Lens (use, (^?), Ixed (..))
+import qualified Control.Concurrent.Chan as Chan
+import qualified Control.Concurrent.MVar as MVar
import qualified Data.Text as Text
import qualified Data.Text.Lazy as Text.L
import qualified Data.ByteString.Base64 as BS.Base64
+import qualified Data.Set as Set
import qualified Network.Wai as Wai
import qualified Network.Wai.Middleware.Static as Wai.Static
import qualified Network.Wai.Handler.Warp as Warp
+import qualified Network.WebSockets as WS
import qualified Web.Scotty as Sc
@@ -25,22 +29,43 @@ import Fig.Frontend.Auth
import Fig.Frontend.State
import qualified Fig.Frontend.DB as DB
+data LiveEvent
+ = LiveEventOnline Text
+ | LiveEventOffline Text
+ deriving (Show, Eq, Ord)
+
server :: Config -> (Text, Text) -> IO ()
server cfg busAddr = do
log $ "Frontend server running on port " <> tshow cfg.port
+ liveEvents <- Chan.newChan @LiveEvent
+ currentlyLive <- MVar.newMVar Set.empty
busClient busAddr
(\cmds -> do
log "Connected to bus!"
- Warp.run cfg.port =<< app cfg cmds
+ cmds.subscribe [sexp|(monitor twitch stream online)|]
+ cmds.subscribe [sexp|(monitor twitch stream offline)|]
+ Warp.run cfg.port =<< app cfg cmds liveEvents currentlyLive
+ )
+ (\_cmds d -> do
+ case d of
+ SExprList [ev, SExprString user]
+ | ev == [sexp|(monitor twitch stream online)|] -> do
+ log $ "Stream online: " <> user
+ MVar.modifyMVar_ currentlyLive (pure . Set.insert user)
+ Chan.writeChan liveEvents $ LiveEventOnline user
+ | ev == [sexp|(monitor twitch stream offline)|] -> do
+ log $ "Stream offline: " <> user
+ MVar.modifyMVar_ currentlyLive (pure . Set.delete user)
+ Chan.writeChan liveEvents $ LiveEventOffline user
+ _ -> log $ "Invalid event: " <> tshow d
)
- (\_ _ -> pure ())
(pure ())
sexprStr :: Text -> SExpr
sexprStr = SExprString . BS.Base64.encodeBase64 . encodeUtf8
-app :: Config -> Commands IO -> IO Wai.Application
-app cfg cmds = do
+app :: Config -> Commands IO -> Chan.Chan LiveEvent -> MVar.MVar (Set.Set Text) -> IO Wai.Application
+app cfg cmds liveEvents currentlyLive = do
log "Connecting to database..."
db <- DB.connect cfg
log "Connected! Server active."
@@ -56,10 +81,6 @@ app cfg cmds = do
DB.get db "motd" >>= \case
Nothing -> Sc.text ""
Just val -> Sc.text . Text.L.fromStrict $ decodeUtf8 val
- Sc.get "/api/motd" do
- DB.get db "motd" >>= \case
- Nothing -> Sc.text ""
- Just val -> Sc.text . Text.L.fromStrict $ decodeUtf8 val
Sc.get "/api/catchphrase" do
let catchphrases =
[ "vtuber (male)"
@@ -127,5 +148,15 @@ app cfg cmds = do
log . tshow $ "partial handshake from " <> me <> " to " <> target
DB.sadd db ("pokeinbox:" <> target) [me]
Sc.text "partial"
+ Sc.get "/api/circle" do
+ live <- liftIO $ MVar.readMVar currentlyLive
+ Sc.text . Text.L.fromStrict . pretty . SExprList @Void $ sexprStr <$> Set.toList live
+ websocket "/api/circle/events" \conn -> do
+ c <- Chan.dupChan liveEvents
+ forever do
+ ev <- liftIO $ Chan.readChan c
+ WS.sendTextData conn $ case ev of
+ LiveEventOnline u -> "online " <> u
+ LiveEventOffline u -> "offline " <> u
Sc.notFound do
Sc.text "not found"
diff --git a/fig-frontend/src/Fig/Frontend/Utils.hs b/fig-frontend/src/Fig/Frontend/Utils.hs
index 3081ddb..090c6ba 100644
--- a/fig-frontend/src/Fig/Frontend/Utils.hs
+++ b/fig-frontend/src/Fig/Frontend/Utils.hs
@@ -5,12 +5,17 @@ module Fig.Frontend.Utils
( FigFrontendException(..)
, loadConfig
, Config(..)
+ , websocket
, module Network.HTTP.Types.Status
) where
import Fig.Prelude
import Network.HTTP.Types.Status
+import qualified Network.Wai.Handler.WebSockets as Wai.WS
+import qualified Network.WebSockets as WS
+
+import qualified Web.Scotty as Sc
import qualified Toml
@@ -39,3 +44,10 @@ loadConfig :: FilePath -> IO Config
loadConfig path = Toml.decodeFileEither configCodec path >>= \case
Left err -> throwM . FigFrontendException $ tshow err
Right config -> pure config
+
+websocket :: ByteString -> (WS.Connection -> IO ()) -> Sc.ScottyM ()
+websocket pat h = Sc.middleware $ Wai.WS.websocketsOr WS.defaultConnectionOptions handler
+ where
+ handler pending = if WS.requestPath (WS.pendingRequest pending) == pat
+ then WS.acceptRequest pending >>= h
+ else WS.rejectRequest pending ""