from channels.db import database_sync_to_async from channels.generic.websocket import AsyncJsonWebsocketConsumer from .models import RealtimeMatch class MatchConsumer(AsyncJsonWebsocketConsumer): async def connect(self): self.match_id = self.scope["url_route"]["kwargs"]["match_id"] self.group_name = f"match_{self.match_id}" user = self.scope["user"] if not user.is_authenticated or not await self._is_participant(user.id): await self.close(code=4403) return await self.channel_layer.group_add(self.group_name, self.channel_name) await self.accept() await self.send_json({"type": "connected", "match_id": str(self.match_id)}) async def disconnect(self, close_code): if hasattr(self, "group_name"): await self.channel_layer.group_discard(self.group_name, self.channel_name) async def receive_json(self, content, **kwargs): event_type = content.get("type") if event_type == "ping": await self.send_json({"type": "pong"}) return if event_type == "progress": answered_count = max(0, min(int(content.get("answered_count", 0)), 100)) await self.channel_layer.group_send( self.group_name, { "type": "match.progress", "user_id": str(self.scope["user"].id), "answered_count": answered_count, }, ) async def match_progress(self, event): await self.send_json( { "type": "progress", "user_id": event["user_id"], "answered_count": event["answered_count"], } ) @database_sync_to_async def _is_participant(self, user_id): return RealtimeMatch.objects.filter(id=self.match_id).filter( player_one_id=user_id ).exists() or RealtimeMatch.objects.filter( id=self.match_id, player_two_id=user_id ).exists()