ai-voicebot/voicebot/session_manager.py

119 lines
3.7 KiB
Python

"""
Session and lobby management for voicebot.
This module handles session creation and lobby management functionality.
"""
import json
import ssl
import urllib.request
import urllib.error
import urllib.parse
import sys
import os
from pydantic import ValidationError
# Add the parent directory to sys.path to allow absolute imports
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# Import shared models
from shared.models import SessionModel, LobbyCreateResponse
from voicebot.utils import http_base_url
def create_or_get_session(
server_url: str, session_id: str | None = None, insecure: bool = False
) -> str:
"""Call GET /api/session to obtain a session_id (unless one was provided).
Uses urllib so no extra runtime deps are required.
"""
if session_id:
return session_id
http_base = http_base_url(server_url)
url = f"{http_base}/api/session"
req = urllib.request.Request(url, method="GET")
# Prepare SSL context if requested (accept self-signed certs)
ssl_ctx = None
if insecure:
ssl_ctx = ssl.create_default_context()
ssl_ctx.check_hostname = False
ssl_ctx.verify_mode = ssl.CERT_NONE
try:
with urllib.request.urlopen(req, timeout=10, context=ssl_ctx) as resp:
body = resp.read()
data = json.loads(body)
# Validate response shape using Pydantic
try:
session = SessionModel.model_validate(data)
except ValidationError as e:
raise RuntimeError(f"Invalid session response from {url}: {e}")
sid = session.id
if not sid:
raise RuntimeError(f"No session id returned from {url}: {data}")
return sid
except urllib.error.HTTPError as e:
raise RuntimeError(f"HTTP error getting session: {e}")
except Exception as e:
raise RuntimeError(f"Error getting session: {e}")
def create_or_get_lobby(
server_url: str,
session_id: str,
lobby_name: str,
private: bool = False,
insecure: bool = False,
) -> str:
"""Call POST /api/lobby/{session_id} to create or lookup a lobby by name.
Returns the lobby id.
"""
http_base = http_base_url(server_url)
url = f"{http_base}/api/lobby/{urllib.parse.quote(session_id)}"
payload = json.dumps(
{
"type": "lobby_create",
"data": {"name": lobby_name, "private": private},
}
).encode("utf-8")
req = urllib.request.Request(
url, data=payload, headers={"Content-Type": "application/json"}, method="POST"
)
# Prepare SSL context if requested (accept self-signed certs)
ssl_ctx = None
if insecure:
ssl_ctx = ssl.create_default_context()
ssl_ctx.check_hostname = False
ssl_ctx.verify_mode = ssl.CERT_NONE
try:
with urllib.request.urlopen(req, timeout=10, context=ssl_ctx) as resp:
body = resp.read()
data = json.loads(body)
# Expect shape: { "type": "lobby_created", "data": {"id":..., ...}}
try:
lobby_resp = LobbyCreateResponse.model_validate(data)
except ValidationError as e:
raise RuntimeError(f"Invalid lobby response from {url}: {e}")
lobby_id = lobby_resp.data.id
if not lobby_id:
raise RuntimeError(f"No lobby id returned from {url}: {data}")
return lobby_id
except urllib.error.HTTPError as e:
# Try to include response body for debugging
try:
body = e.read()
msg = body.decode("utf-8", errors="ignore")
except Exception:
msg = str(e)
raise RuntimeError(f"HTTP error creating lobby: {msg}")
except Exception as e:
raise RuntimeError(f"Error creating lobby: {e}")