backend/app/main.py

197 lines
6.3 KiB
Python
Raw Permalink Normal View History

2019-07-06 21:54:35 +02:00
import redis
import json
import uuid
import os
2019-07-06 21:54:35 +02:00
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
2019-07-07 17:26:53 +02:00
from starlette.middleware.cors import CORSMiddleware
2019-07-06 21:54:35 +02:00
hashes = hashids.Hashids("a very good salt yes this is nice ok", 4, "ABCDEFGHJKLMNPQRSTUVXYZ23456789")
2019-07-06 21:54:35 +02:00
app = FastAPI(title="Cards against idiots")
2019-07-07 17:26:53 +02:00
r = redis.Redis(
host=os.getenv("REDIS_HOST", "redis"),
port=os.getenv("REDIS_PORT", 6379),
password=os.getenv("REDIS_PASS", "yeet")
)
app.add_middleware(CORSMiddleware, allow_origins=['*'], allow_methods=["*"], allow_headers=["*"])
2019-07-06 21:54:35 +02:00
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()
2019-07-07 17:26:53 +02:00
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)
2019-07-06 21:54:35 +02:00
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
2019-07-07 23:10:02 +02:00
if len(name) > 25:
return ws.close(1009)
2019-07-06 21:54:35 +02:00
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())
2019-07-07 17:26:53 +02:00
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())
2019-07-06 21:54:35 +02:00
elif msg.msgtype is MessageType.START:
pid: str = str(msg.data)
if pid == str(room.admin_uuid):
room.started = True
2019-07-07 17:26:53 +02:00
r.hset("rooms", room.code, room.json())
2019-07-06 21:54:35 +02:00
2019-07-07 17:26:53 +02:00
r.publish(code, Message(msgtype=MessageType.ROOM,
data=PubRoom(**room.dict())).json())
if room.started and not next_round(code):
2019-07-06 21:54:35 +02:00
await ws.close()
elif msg.msgtype is MessageType.ANSWER:
answer = AnswerReceived(**json.loads(msg.data))
2019-07-07 17:26:53 +02:00
mapper = {str(p.uuid): i for i, p in enumerate(room.players)}
2019-07-06 21:54:35 +02:00
2019-07-07 17:26:53 +02:00
mapped = AnswerSending(**answer.dict(), index=mapper[str(answer.uuid)])
2019-07-06 21:54:35 +02:00
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))
2019-07-07 17:26:53 +02:00
if str(answer.uuid) != str(room.players[room.czar].uuid):
2019-07-06 21:54:35 +02:00
continue
r.publish(code, Message(
msgtype=MessageType.PICK, data=answer).json())
room.players[answer.index].points += 1
r.hset("rooms", room.code, room.json())
2019-07-07 17:26:53 +02:00
r.publish(code, Message(msgtype=MessageType.ROOM,
data=PubRoom(**room.dict())).json())
2019-07-06 21:54:35 +02:00
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)
2019-07-07 17:26:53 +02:00
2019-07-06 21:54:35 +02:00
r.hset("rooms", room.code, room.json())
r.publish(code, Message(
msgtype=MessageType.START,
data=RoundStart(czar=room.czar, card=cards["black"][cardid])).json())
2019-07-07 17:26:53 +02:00
r.publish(code, Message(msgtype=MessageType.ROOM,
data=PubRoom(**room.dict())).json())
2019-07-06 21:54:35 +02:00
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()}