CI / test (push) Failing after 39s
Hjernen (party/brain) skriver kommentarer til løgnene, dømmer løgne (sandheden eller upassende), personlige drillerier og en slut-anmeldelse, alt i baggrunden med faste replikker som fallback. Udbyderen vælges med BRAIN_PROVIDER: anthropic (Claude), openai (OpenAI-kompatibel, fx Codex) eller ollama (fx Gemma). Rummets mikrofon er eksperimentel: storskærmen sender korte lydstumper til talegenkendelse (STT_PROVIDER), og kun et kort resumé af latter og udsagn huskes til hjernen. Musikken er nu lavet med en musikmodel. Claude-Session: https://claude.ai/code/session_01JmhJG6JnhXfzNiKv8f2vHu
171 lines
6.0 KiB
Python
171 lines
6.0 KiB
Python
"""WebSocket-forbindelser for storskærm (vært) og telefoner (spillere/publikum)."""
|
|
|
|
import logging
|
|
import time
|
|
|
|
from channels.generic.websocket import AsyncJsonWebsocketConsumer
|
|
|
|
from fupogfakta.engine import GameError
|
|
|
|
from . import stt
|
|
from .auth import scope_identity
|
|
from .rooms import get_room
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
class _RoomConsumer(AsyncJsonWebsocketConsumer):
|
|
room = None
|
|
|
|
async def connect(self):
|
|
self.room = get_room(self.scope["url_route"]["kwargs"]["code"])
|
|
if not self.authorized():
|
|
await self.close(code=4403)
|
|
return
|
|
await self.accept()
|
|
if not self.room:
|
|
await self.send_error("unknown_room", "Det spil findes ikke. Tjek koden.")
|
|
await self.close(code=4404)
|
|
return
|
|
self.room.connections.add(self)
|
|
self.room.touch()
|
|
await self.on_open()
|
|
|
|
def authorized(self) -> bool:
|
|
return True
|
|
|
|
async def on_open(self):
|
|
pass
|
|
|
|
async def disconnect(self, code):
|
|
if self.room:
|
|
self.room.connections.discard(self)
|
|
await self.on_close()
|
|
|
|
async def on_close(self):
|
|
pass
|
|
|
|
async def send_error(self, code, message):
|
|
await self.send_json({"type": "error", "code": code, "message": message})
|
|
|
|
async def receive_json(self, content, **kwargs):
|
|
if not self.room or not isinstance(content, dict):
|
|
return
|
|
action = content.get("action")
|
|
if action == "ping":
|
|
await self.send_json({"type": "pong"})
|
|
return
|
|
version = self.room.game.version
|
|
try:
|
|
await self.handle(action, content, time.time())
|
|
except GameError as e:
|
|
await self.send_error(e.code, e.message)
|
|
except (TypeError, ValueError, KeyError):
|
|
await self.send_error("bad_request", "Ugyldig besked.")
|
|
if self.room.game.version != version:
|
|
self.room.changed()
|
|
else:
|
|
self.room.handle_events() # fx "det var sandheden", der afvises men kommenteres
|
|
|
|
async def handle(self, action, content, now):
|
|
raise NotImplementedError
|
|
|
|
async def send_state(self):
|
|
raise NotImplementedError
|
|
|
|
|
|
class HostConsumer(_RoomConsumer):
|
|
def authorized(self):
|
|
return scope_identity(self.scope) is not None
|
|
|
|
async def on_open(self):
|
|
self.room.host_opened()
|
|
await self.send_state()
|
|
|
|
async def send_state(self):
|
|
state = self.room.game.snapshot(time.time(), host=True)
|
|
state["narration"] = self.room.narration
|
|
state["phase_key"] = self.room.game.phase_key()
|
|
state["mic"] = {"on": self.room.mic_on(), "laughs": self.room.mic_laughs, "available": stt.enabled()}
|
|
await self.send_json({"type": "state", "state": state})
|
|
|
|
async def handle(self, action, content, now):
|
|
g = self.room.game
|
|
if action in ("start", "skip", "play_again"):
|
|
self.room.cut_speech() # værtens knap vinder over speakeren
|
|
if action == "start":
|
|
g.start(None, now)
|
|
elif action == "skip":
|
|
g.skip(now)
|
|
elif action == "play_again":
|
|
g.play_again(None, now)
|
|
elif action == "kick":
|
|
g.kick(str(content["player_id"]), now)
|
|
else:
|
|
await self.send_error("bad_request", "Ukendt handling.")
|
|
|
|
|
|
class PlayConsumer(_RoomConsumer):
|
|
member_id = None
|
|
|
|
async def on_close(self):
|
|
if self.member_id:
|
|
# Er spilleren stadig forbundet fra en anden fane/telefon, er de ikke væk.
|
|
still = any(getattr(c, "member_id", None) == self.member_id for c in self.room.connections)
|
|
if not still:
|
|
version = self.room.game.version
|
|
self.room.game.set_connected(self.member_id, False, time.time())
|
|
if self.room.game.version != version:
|
|
self.room.changed()
|
|
|
|
def _member(self):
|
|
g = self.room.game
|
|
return g.players.get(self.member_id) or g.audience.get(self.member_id)
|
|
|
|
async def send_state(self):
|
|
if not self.member_id:
|
|
return
|
|
member = self._member()
|
|
if not member: # smidt ud af værten
|
|
self.member_id = None
|
|
await self.send_error("unknown_token", "Du er ikke længere med i spillet.")
|
|
return
|
|
await self.send_json({"type": "state", "state": self.room.game.snapshot(time.time(), viewer=member)})
|
|
|
|
async def _attach(self, member, now):
|
|
self.member_id = member.id
|
|
self.room.game.set_connected(member.id, True, now)
|
|
await self.send_json({"type": "joined", "token": member.token, "player_id": member.id, "role": member.role})
|
|
await self.send_state()
|
|
|
|
async def handle(self, action, content, now):
|
|
g = self.room.game
|
|
if action == "join":
|
|
await self._attach(g.join(str(content.get("name", ""))), now)
|
|
return
|
|
if action == "resume":
|
|
await self._attach(g.resume(str(content.get("token", ""))), now)
|
|
return
|
|
if not self.member_id or not self._member():
|
|
raise GameError("unknown_token", "Join spillet først.")
|
|
mid = self.member_id
|
|
if action in ("start", "play_again") and mid == g.vip_id():
|
|
self.room.cut_speech()
|
|
if action == "start":
|
|
g.start(mid, now)
|
|
elif action == "choose_category":
|
|
g.choose_category(mid, int(content["category_id"]), now)
|
|
elif action == "submit_lie":
|
|
self.room.prepare_judging()
|
|
g.submit_lie(mid, str(content.get("text", "")), now)
|
|
elif action == "lie_for_me":
|
|
await self.send_json({"type": "suggestion", "text": g.lie_for_me(mid)})
|
|
elif action == "guess":
|
|
g.guess(mid, str(content["answer_id"]), now)
|
|
elif action == "like":
|
|
g.like(mid, str(content["answer_id"]))
|
|
elif action == "play_again":
|
|
g.play_again(mid, now)
|
|
else:
|
|
await self.send_error("bad_request", "Ukendt handling.")
|