from fastapi import APIRouter, WebSocket, WebSocketDisconnect from core.websocket import get_ws_current_user from domains.realtime.ws_manager import manager from domains.realtime.presence_service import ( user_join_group, user_leave_group, list_online_users ) from domains.realtime.speaker_service import ( request_speak, stop_speaking, current_speaker ) from integrations.livekit.token_service import generate_join_token from db.session import AsyncSessionLocal router = APIRouter() @router.websocket("/ws/groups/{group_id}") async def group_ws(websocket: WebSocket, group_id: str): user = await get_ws_current_user(websocket) user_id = str(user.id) # connect websocket await manager.connect(group_id, websocket) # add presence await user_join_group(group_id, user_id) # give listener token listener_token = generate_join_token( user_id=user_id, group_id=group_id, can_publish=False ) await websocket.send_json({ "type": "livekit_token", "token": listener_token }) # broadcast updated presence await manager.broadcast( group_id, { "type": "presence", "users": await list_online_users(group_id) } ) try: while True: data = await websocket.receive_json() event = data.get("type") # user wants to speak if event == "request_speak": async with AsyncSessionLocal() as db: token = await request_speak( db, group_id, user_id ) if token: await manager.broadcast( group_id, { "type": "speaker", "user_id": user_id } ) await websocket.send_json({ "type": "speaker_granted", "token": token }) else: async with AsyncSessionLocal() as db: speaker = await current_speaker(db, group_id) await websocket.send_json({ "type": "speaker_busy", "speaker": speaker }) # user stops speaking elif event == "stop_speak": async with AsyncSessionLocal() as db: await stop_speaking(db, group_id, user_id) await manager.broadcast( group_id, { "type": "speaker_released" } ) except WebSocketDisconnect: manager.disconnect(group_id, websocket) await user_leave_group(group_id, user_id) await manager.broadcast( group_id, { "type": "presence", "users": await list_online_users(group_id) } )