backend/app/main.py
2019-07-07 23:10:02 +02:00

194 lines
6.3 KiB
Python

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
if len(name) > 25:
return ws.close(1009)
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()}