import redis import json import uuid import random import hashids import asyncio from fastapi import FastAPI from starlette.responses import HTMLResponse from starlette.exceptions import HTTPException from pydantic import BaseModel from typing import List, Dict # pylint: disable=unused-wildcard-import from .models import * from starlette.websockets import WebSocket from starlette.middleware.cors import CORSMiddleware hashes = hashids.Hashids("a very good salt yes this is nice ok", 4, "ABCDEFGHJKLMNPQRSTUVXYZ23456789") app = FastAPI(title="Cards against idiots") r = redis.Redis(host='redis', port=6379, password='yeet') app.add_middleware(CORSMiddleware, allow_origins=[ '*'], allow_methods=["*"], allow_headers=["*"]) # loop = asyncio.get_event_loop() # r = aioredis.create_redis("redis://redis", loop=loop) cards = {} with open("cards.json", encoding="UTF-8") as file: cards = json.load(file) @app.get("/cards/random", response_model=Card) async def get_random_card(): return random.choice(cards["black"]) @app.get("/packs", response_model=Dict[str, Pack]) async def get_all_packs(): return cards["metadata"] @app.get("/packs/{id}") async def get_pack(id: str): return cards["metadata"][id] @app.websocket("/ws/room/{code}/{kind}") async def ws_room(ws: WebSocket, code: str, kind: str): await ws.accept() if kind == "sub": ps = r.pubsub() ps.subscribe(code) while True: # 1: Message from pub/sub msg = ps.get_message() if msg and type(msg["data"]) is bytes: await ws.send_bytes(msg["data"].decode("utf-8")) elif msg: await ws.send_text("1") else: await asyncio.sleep(0) elif kind == "pub": # 2: Message from WS while True: msg = Message(**(await ws.receive_json())) get_r = r.hget("rooms", code) # Room should already be created even if we are the first person to join if not get_r: await ws.close() return room = Room(**json.loads(get_r)) if msg.msgtype is MessageType.JOIN: # ps.subscribe(code) name: str = str(msg.data) if room.started: await ws.send_text("you fucked up dawg") await ws.close() return p = Player(name=name, uuid=uuid.uuid4()) if not room.admin_uuid: room.admin_uuid = p.uuid room.players.append(p) r.hset("rooms", room.code, room.json()) await ws.send_json({"msgtype": "JOIN", "data": {"owner": room.admin_uuid == p.uuid, "you": len(room.players)-1, "uuid": str(p.uuid)}}) await ws.send_json({"msgtype": "ROOM", "data": PubRoom(**room.dict()).dict()}) r.publish(code, Message(msgtype=MessageType.ROOM, data=PubRoom(**room.dict())).json()) elif msg.msgtype is MessageType.START: pid: str = str(msg.data) if pid == str(room.admin_uuid): room.started = True r.hset("rooms", room.code, room.json()) r.publish(code, Message(msgtype=MessageType.ROOM, data=PubRoom(**room.dict())).json()) if room.started and not next_round(code): await ws.close() elif msg.msgtype is MessageType.ANSWER: answer = AnswerReceived(**json.loads(msg.data)) mapper = {str(p.uuid): i for i, p in enumerate(room.players)} mapped = AnswerSending(**answer.dict(), index=mapper[str(answer.uuid)]) room.answers.append(mapped) r.hset("rooms", room.code, room.json()) if len(room.answers) == (len(room.players) - 1): r.publish(code, Message( msgtype=MessageType.ANSWER, data=room.answers).json()) elif msg.msgtype is MessageType.PICK: answer = AnswerSendingButItHasAUUIDBecauseItIsImportantToCheckIfItWasSentByTheCzar( **json.loads(msg.data)) if str(answer.uuid) != str(room.players[room.czar].uuid): continue r.publish(code, Message( msgtype=MessageType.PICK, data=answer).json()) room.players[answer.index].points += 1 r.hset("rooms", room.code, room.json()) r.publish(code, Message(msgtype=MessageType.ROOM, data=PubRoom(**room.dict())).json()) next_round(code) else: raise HTTPException(400, "That's an illegal (message type)!") else: await ws.close() def next_round(code: str): get_r = r.hget("rooms", code) # Room should exist if we want to start it if not get_r: return False room = Room(**json.loads(get_r)) room.answers = [] cardid = random.randrange(0, len(cards["black"])) while cardid in room.played_cards: cardid = random.randrange(0, len(cards["black"])) room.played_cards.append(cardid) room.czar = (room.czar + 1) % len(room.players) r.hset("rooms", room.code, room.json()) r.publish(code, Message( msgtype=MessageType.START, data=RoundStart(czar=room.czar, card=cards["black"][cardid])).json()) r.publish(code, Message(msgtype=MessageType.ROOM, data=PubRoom(**room.dict())).json()) return True @app.get("/") async def memes(): return cards @app.post("/room", response_model=Room) async def create_room(): n = r.incr("RoomCounter") room = Room(number=n, code=hashes.encode(n)) r.hset("rooms", room.code, room.json()) return room @app.get("/room/{code}", response_model=PubRoom) async def get_room_by_id(code: str): try: return Room(**json.loads(r.hget("rooms", code))) except: raise HTTPException(404, "Room not found!") @app.get("/rooms", response_model=Dict[str, PubRoom]) async def dump_redis(): return {k: Room(**json.loads(v)) for k, v in r.hgetall("rooms").items()}