Files
weirsoe-party-protocol/party/consumers.py
T
agw fc76356f56
CI / test (push) Successful in 1m4s
Oplæsningsrunde, emoji-reaktioner, regler og renere løgne
- Runde 2 og finalen: speakeren læser svarene op ét ad gangen (med hjernen
  en overbevisende kommentar pr. svar), i samme takt på storskærm og telefoner
- Emoji-reaktioner fra telefonerne, der må spammes; de flyver op på storskærmen
  og tælles på svaret i fokus
- Opsummeringen følger speakeren: "lad os rydde op", eksplosioner, så pointene
- Speakeren kommenterer, hvordan løgnene skrider frem, uden at røbe indhold,
  og driller dem, der falder for svage løgne
- Reglerne forklares med kort og stemme før rummets første spil
- "Lyv for mig" låser forslaget i feltet (halve point), og feltet bliver ikke
  længere kæmpestort
- Svar vises med versaler, og spillernes løgne gemmes med små bogstaver, så
  store forbogstaver ikke afslører dem
- Løgne og navne renses: kun bogstaver, tal og lidt tegnsætning
- Valgfrit workspace-id til Anthropic-brugernøgler

Claude-Session: https://claude.ai/code/session_01JmhJG6JnhXfzNiKv8f2vHu
2026-10-03 18:18:38 +02:00

177 lines
6.2 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):
is_host = True
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 == "react":
reaction = g.react(mid, str(content.get("emoji", "")), now)
if reaction:
await self.room.forward_reaction(reaction)
elif action == "play_again":
g.play_again(mid, now)
else:
await self.send_error("bad_request", "Ukendt handling.")