♻️ Cleaned socket_server up by using classes instead of dicts

This commit is contained in:
Mawoka
2022-06-11 22:21:14 +02:00
parent 69ac9678a1
commit 9201a4763e
+95 -52
View File
@@ -4,8 +4,10 @@ import os
import aiohttp import aiohttp
import socketio import socketio
from typing import Any
from classquiz.config import redis, settings from classquiz.config import redis, settings
from classquiz.db.models import PlayGame from classquiz.db.models import PlayGame
from pydantic import BaseModel, ValidationError
sio = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins=[]) sio = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins=[])
settings = settings() settings = settings()
@@ -22,16 +24,44 @@ async def generate_final_results(game_data: PlayGame, game_pin: str) -> dict:
return results return results
class _JoinGameData(BaseModel):
username: str
game_pin: str
captcha: str | None
class _GameSessionPlayer(BaseModel):
username: str
sid: str
class _GameSession(BaseModel):
admin: str
game_id: str
players: list[_GameSessionPlayer | None]
answers: list[Any]
@sio.event @sio.event
async def join_game(sid: str, data: dict): async def join_game(sid: str, data: dict):
async with aiohttp.ClientSession() as session:
redis_res = await redis.get(f"game:{data['game_pin']}") redis_res = await redis.get(f"game:{data['game_pin']}")
if redis_res is None:
await sio.emit("game_not_found", room=sid)
return
try:
data = _JoinGameData(**data)
except ValidationError as e:
await sio.emit("error", room=sid)
print(e)
return
# +++ START checking captcha +++
async with aiohttp.ClientSession() as session:
try: try:
if json.loads(redis_res)["captcha_enabled"]: if json.loads(redis_res)["captcha_enabled"]:
try: try:
async with session.post( async with session.post(
"https://hcaptcha.com/siteverify", "https://hcaptcha.com/siteverify",
data={"response": data["captcha"], "secret": settings.hcaptcha_key}, data={"response": data.captcha, "secret": settings.hcaptcha_key},
) as resp: ) as resp:
resp_data = await resp.json() resp_data = await resp.json()
if not resp_data["success"]: if not resp_data["success"]:
@@ -43,37 +73,28 @@ async def join_game(sid: str, data: dict):
return return
except TypeError: except TypeError:
pass pass
if redis_res is None: # --- END checking captcha ---
await sio.emit("game_not_found", room=sid)
else:
session = { session = {
"game_pin": data["game_pin"], "game_pin": data.game_pin,
"username": data["username"], "username": data.username,
"admin": False, "admin": False,
} }
await sio.save_session(sid, session) await sio.save_session(sid, session)
await sio.emit("joined_game", redis_res, room=sid) await sio.emit("joined_game", redis_res, room=sid)
redis_res = await redis.get(f"game_session:{data['game_pin']}") redis_res = await redis.get(f"game_session:{data.game_pin}")
redis_res = json.loads(redis_res) redis_res = _GameSession.parse_raw(redis_res)
redis_res["players"].append({"username": data["username"], "sid": sid}) redis_res.players.append(_GameSessionPlayer(username=data.username, sid=sid))
await redis.set( await redis.set(
f"game_session:{data['game_pin']}", f"game_session:{data.game_pin}",
json.dumps( _GameSession(admin=redis_res.admin, game_id=redis_res.game_id, players=redis_res.players, answers=[]).json(),
{
"admin": redis_res["admin"],
"game_id": redis_res["game_id"],
"players": redis_res["players"],
"answers": [],
}
),
ex=18000, ex=18000,
) )
await sio.emit( await sio.emit(
"player_joined", "player_joined",
{"username": data["username"], "sid": sid}, {"username": data.username, "sid": sid},
room=redis_res["admin"], room=redis_res.admin,
) )
sio.enter_room(sid, data["game_pin"]) sio.enter_room(sid, data.game_pin)
@sio.event @sio.event
@@ -83,26 +104,37 @@ async def start_game(sid: str, _data: dict):
await sio.emit("start_game", room=session["game_pin"]) await sio.emit("start_game", room=session["game_pin"])
class _RegisterAsAdminData(BaseModel):
game_pin: str
game_id: str
@sio.event @sio.event
async def register_as_admin(sid: str, data: dict): async def register_as_admin(sid: str, data: dict):
game_pin = data["game_pin"] try:
game_id = data["game_id"] data = _RegisterAsAdminData(**data)
except ValidationError as e:
await sio.emit("error", room=sid)
print(e)
return
game_pin = data.game_pin
game_id = data.game_id
if (await redis.get(f"game_session:{game_pin}")) is None: if (await redis.get(f"game_session:{game_pin}")) is None:
await redis.set( await redis.set(
f"game_session:{game_pin}", f"game_session:{game_pin}",
json.dumps({"admin": sid, "game_id": game_id, "players": []}), _GameSession(admin=sid, game_id=game_id, answers=[], players=[]).json(),
ex=18000, ex=18000,
) )
await sio.emit( await sio.emit(
"registered_as_admin", "registered_as_admin",
{"game_id": game_id, "game": await redis.get(f"game:{data['game_pin']}")}, {"game_id": game_id, "game": await redis.get(f"game:{game_pin}")},
room=sid, room=sid,
) )
async with sio.session(sid) as session: async with sio.session(sid) as session:
session["game_pin"] = data["game_pin"] session["game_pin"] = game_pin
session["admin"] = True session["admin"] = True
sio.enter_room(sid, data["game_pin"]) sio.enter_room(sid, game_pin)
else: else:
await sio.emit("already_registered_as_admin", room=sid) await sio.emit("already_registered_as_admin", room=sid)
@@ -117,48 +149,59 @@ async def get_question_results(sid: str, data: dict):
@sio.event @sio.event
async def set_question_number(sid, data): async def set_question_number(sid, data: str):
# data is just a number (as a str) of the question
session = await sio.get_session(sid) session = await sio.get_session(sid)
if session["admin"]: if session["admin"]:
game_pin = session["game_pin"] game_pin = session["game_pin"]
await sio.emit("set_question_number", data, room=game_pin) await sio.emit("set_question_number", data, room=game_pin)
class _SubmitAnswerData(BaseModel):
question_index: int
answer: str
class _AnswerData(BaseModel):
username: str
answer: str
right: bool
class _AnswerDataList(BaseModel):
# Just a method to make a top-level list
__root__: list[_AnswerData]
@sio.event @sio.event
async def submit_answer(sid: str, data: dict): async def submit_answer(sid: str, data: dict):
try:
data = _SubmitAnswerData(**data)
except ValidationError as e:
await sio.emit("error", room=sid)
print(e)
return
session = await sio.get_session(sid) session = await sio.get_session(sid)
game_data = PlayGame(**json.loads(await redis.get(f"game:{session['game_pin']}"))) game_data = PlayGame.parse_raw(await redis.get(f"game:{session['game_pin']}"))
answer_right = False answer_right = False
for answer in game_data.questions[int(data["question_index"])].answers: for answer in game_data.questions[int(data.question_index)].answers:
if answer.answer == data["answer"] and answer.right: if answer.answer == data.answer and answer.right:
answer_right = True answer_right = True
break break
answers = await redis.get(f"game_session:{session['game_pin']}:{data['question_index']}") answers = await redis.get(f"game_session:{session['game_pin']}:{data.question_index}")
if answers is None: if answers is None:
await redis.set( await redis.set(
f"game_session:{session['game_pin']}:{data['question_index']}", f"game_session:{session['game_pin']}:{data.question_index}",
json.dumps( _AnswerDataList(
[ __root__=[_AnswerData(username=session["username"], answer=data.answer, right=answer_right)]
{ ).json(),
"username": session["username"],
"answer": data["answer"],
"right": answer_right,
}
]
),
ex=18000, ex=18000,
) )
else: else:
answers = json.loads(answers) answers = _AnswerDataList.parse_raw(answers)
answers.append( answers.__root__.append(_AnswerData(username=session["username"], answer=data.answer, right=answer_right))
{
"username": session["username"],
"answer": data["answer"],
"right": answer_right,
}
)
await redis.set( await redis.set(
f"game_session:{session['game_pin']}:{data['question_index']}", f"game_session:{session['game_pin']}:{data.question_index}",
json.dumps(answers), json.dumps(answers),
ex=18000, ex=18000,
) )