Files
Hulumath-Web/backend/toolbox/consumers.py
T

101 lines
3.3 KiB
Python

import json
from channels.db import database_sync_to_async
from channels.generic.websocket import AsyncJsonWebsocketConsumer
from .models import BoardSession
class BoardConsumer(AsyncJsonWebsocketConsumer):
async def connect(self):
self.session_id = self.scope["url_route"]["kwargs"]["session_id"]
self.group_name = f"board_{self.session_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",
"session_id": str(self.session_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 not in {"canvas", "geometry"}:
await self.send_json({"type": "error", "message": "不支持的画板消息"})
return
if not await self._is_active():
await self.send_json({"type": "error", "message": "画板尚未开始或已经结束"})
return
payload = content.get("payload")
if event_type == "canvas":
valid = (
isinstance(payload, str)
and payload.startswith("data:image/")
and len(payload) <= 700_000
)
else:
valid = isinstance(payload, dict) and len(
json.dumps(payload, ensure_ascii=False)
) <= 100_000
if not valid:
await self.send_json({"type": "error", "message": "画板消息无效或过大"})
return
await self.channel_layer.group_send(
self.group_name,
{
"type": "board.update",
"event_type": event_type,
"payload": payload,
"user_id": str(self.scope["user"].id),
},
)
async def board_update(self, event):
await self.send_json(
{
"type": event["event_type"],
"payload": event["payload"],
"user_id": event["user_id"],
}
)
async def board_state(self, event):
await self.send_json(
{
"type": "state",
"reason": event["reason"],
"session_id": str(self.session_id),
}
)
@database_sync_to_async
def _is_participant(self, user_id):
return BoardSession.objects.filter(id=self.session_id).filter(
host_id=user_id
).exists() or BoardSession.objects.filter(
id=self.session_id,
guest_id=user_id,
).exists()
@database_sync_to_async
def _is_active(self):
return BoardSession.objects.filter(
id=self.session_id,
status=BoardSession.Status.ACTIVE,
).exists()