Source code for src.monster.server
from typing import Generator
import json
import os
from aiohttp.web import Request, Response, HTTPServiceUnavailable, FileField
from src.monster.static import get, get_safe, Challenge, Mutator, Artifact, Character
from src.webpage import router
from src.utils import get_req_data
from src.typehints import ContextType
from src.configuration import config
[docs]
class MonsterSave:
def __init__(self, file):
data = None
try:
with open(os.path.join("data", file), "r") as f:
data = json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
pass
self._data = data
[docs]
def update_data(self, data: dict):
self._data = data
@property
def main_class(self) -> str:
main = self._data["startingConditions"]["mainClassInfo"]
if "className" in main:
return _get_sanitized(main["className"])
return _get_sanitized(main["classId"])
@property
def main_exiled(self) -> bool:
return bool(self._data["startingConditions"]["mainClassInfo"]["championIndex"])
@property
def sub_class(self) -> str:
sub = self._data["startingConditions"]["subclassInfo"]
if "className" in sub:
return _get_sanitized(sub["className"])
return _get_sanitized(sub["classId"])
@property
def sub_exiled(self) -> bool:
return bool(self._data["startingConditions"]["subclassInfo"]["championIndex"])
@property
def artifacts(self) -> Generator[Artifact, None, None]:
for art in self._data["blessings"]:
yield get(art["relicDataID"])
@property
def challenge(self) -> Challenge | None:
ch = self._data["startingConditions"]["spChallengeId"]
if ch:
return get(ch)
@property
def covenant_level(self) -> int:
return self._data["startingConditions"]["ascensionLevel"]
@property
def pyre(self) -> Character:
return get(self._data["startingConditions"]["pyreCharacterId"])
@property
def mutators(self) -> list[Mutator]:
return [get(x) for x in self._data["startingConditions"]["mutators"]]
_savefile = MonsterSave("monster-train-save.json")
_save2 = MonsterSave("monster-train-2-save.json")
[docs]
async def get_savefile(ctx: ContextType | None = None) -> MonsterSave:
if (_savefile._data is not None and (_savefile.main_class and _savefile.sub_class)):
return _savefile
if (_save2._data is not None and (_save2.main_class and _save2.sub_class)):
return _save2
if ctx is not None:
await ctx.reply("Not in a run.")
@router.post("/sync/monster")
async def get_data(req: Request):
save = (await get_req_data(req, "save"))[0]
data = json.loads(save)
_savefile.update_data(data)
with open(os.path.join("data", "monster-train-save.json"), "w") as f:
json.dump(data, f, indent=config.server.json_indent)
# handle database stuff
post = await req.post()
def write_db(name: str):
value = post[name]
if isinstance(value, FileField):
value = value.file.read()
with open(os.path.join("data", f"mt-runs-{name}.sqlite3"), "wb") as f:
f.write(value)
for k in post:
if k == "main" or k.isdigit() or k.endswith(".db"):
write_db(k)
return Response()
[docs]
@router.post("/sync/monster-2")
async def get_data(req: Request):
save = (await get_req_data(req, "save"))[0]
data = json.loads(save)
_save2.update_data(data)
with open(os.path.join("data", "monster-train-2-save.json"), "w") as f:
json.dump(data, f, indent=config.server.json_indent)
# handle database stuff
post = await req.post()
def write_db(name: str):
value = post[name]
if isinstance(value, FileField):
value = value.file.read()
with open(os.path.join("data", f"mt2-runs-{name}.sqlite3"), "wb") as f:
f.write(value)
for k in post:
if k == "main" or k.isdigit() or k.endswith(".db"):
write_db(k)
return Response()
[docs]
@router.get("/mt/debug")
async def mt_current(req: Request):
save = await get_savefile()
if save is None:
raise HTTPServiceUnavailable(reason="No savefile present on server")
data = save._data
if "raw" not in req.query:
data = _get_sanitized(data)
return Response(text=json.dumps(data, indent=4), content_type="application/json")
def _get_sanitized(x):
match x:
case str():
return get_safe(x)
case list():
return [_get_sanitized(a) for a in x]
case dict():
return {k: _get_sanitized(v) for k,v in x.items()}
case _:
return x