From 14fa0f5e8e11e7351d9429285987cbeec18390fe Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Tue, 9 Jan 2024 03:07:04 -0500 Subject: [PATCH 01/32] begin move --- core/__init__.py | 4 +- core/adb_handlers/base.py | 2 +- core/aimedb.py | 178 ++++++----- core/allnet.py | 282 ++++++++++-------- core/app.py | 89 ++++++ core/config.py | 148 ++++------ core/data/alembic/README | 1 + core/data/alembic/alembic.ini | 89 ++++++ core/data/alembic/env.py | 77 +++++ core/data/alembic/script.py.mako | 24 ++ core/frontend.py | 13 +- core/mucha.py | 13 +- core/title.py | 40 +-- core/utils.py | 26 +- dbutils.py | 67 +---- example_config/core.yaml | 27 +- index.py | 375 +++++------------------- read.py | 2 +- requirements.txt | 8 +- titles/chuni/air.py | 4 +- titles/chuni/airplus.py | 4 +- titles/chuni/amazon.py | 4 +- titles/chuni/amazonplus.py | 4 +- titles/chuni/base.py | 86 +++--- titles/chuni/crystal.py | 4 +- titles/chuni/crystalplus.py | 4 +- titles/chuni/index.py | 58 ++-- titles/chuni/new.py | 68 ++--- titles/chuni/newplus.py | 4 +- titles/chuni/paradise.py | 4 +- titles/chuni/plus.py | 4 +- titles/chuni/star.py | 4 +- titles/chuni/starplus.py | 4 +- titles/chuni/sun.py | 4 +- titles/chuni/sunplus.py | 4 +- titles/cm/base.py | 14 +- titles/cm/cm135.py | 4 +- titles/cm/index.py | 35 ++- titles/cxb/base.py | 30 +- titles/cxb/index.py | 74 +++-- titles/cxb/rev.py | 80 ++--- titles/cxb/rss1.py | 62 ++-- titles/cxb/rss2.py | 62 ++-- titles/diva/base.py | 79 +++-- titles/diva/index.py | 46 ++- titles/idac/frontend.py | 2 +- titles/idac/index.py | 49 ++-- titles/idac/season2.py | 12 +- titles/idz/handlers/load_server_info.py | 20 +- titles/idz/index.py | 37 ++- titles/idz/userdb.py | 2 +- titles/mai2/base.py | 92 +++--- titles/mai2/dx.py | 54 ++-- titles/mai2/festival.py | 12 +- titles/mai2/festivalplus.py | 6 +- titles/mai2/index.py | 168 ++++------- titles/mai2/universe.py | 24 +- titles/mai2/universeplus.py | 4 +- titles/ongeki/base.py | 108 +++---- titles/ongeki/bright.py | 40 +-- titles/ongeki/brightmemory.py | 8 +- titles/ongeki/frontend.py | 2 +- titles/ongeki/index.py | 55 ++-- titles/ongeki/plus.py | 4 +- titles/ongeki/red.py | 4 +- titles/ongeki/redplus.py | 4 +- titles/ongeki/summer.py | 4 +- titles/ongeki/summerplus.py | 4 +- titles/pokken/base.py | 47 ++- titles/pokken/const.py | 7 + titles/pokken/frontend.py | 2 +- titles/pokken/index.py | 118 +++++--- titles/sao/base.py | 100 +++---- titles/sao/const.py | 10 + titles/sao/index.py | 39 +-- titles/wacca/base.py | 56 ++-- titles/wacca/frontend.py | 2 +- titles/wacca/index.py | 48 +-- titles/wacca/lily.py | 16 +- titles/wacca/lilyr.py | 8 +- titles/wacca/reverse.py | 10 +- titles/wacca/s.py | 2 +- 82 files changed, 1683 insertions(+), 1712 deletions(-) create mode 100644 core/app.py create mode 100644 core/data/alembic/README create mode 100644 core/data/alembic/alembic.ini create mode 100644 core/data/alembic/env.py create mode 100644 core/data/alembic/script.py.mako diff --git a/core/__init__.py b/core/__init__.py index 185d9bc..f5e306e 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -1,6 +1,6 @@ from core.config import CoreConfig -from core.allnet import AllnetServlet -from core.aimedb import AimedbFactory +from core.allnet import AllnetServlet, BillingServlet +from core.aimedb import AimedbServlette from core.title import TitleServlet from core.utils import Utils from core.mucha import MuchaServlet diff --git a/core/adb_handlers/base.py b/core/adb_handlers/base.py index 0f208dd..06b5267 100644 --- a/core/adb_handlers/base.py +++ b/core/adb_handlers/base.py @@ -102,7 +102,7 @@ class ADBHeader: magic, protocol_ver, cmd, length, status, game_id, store_id, keychip_id = struct.unpack_from("<5H6sI12s", data) head = cls(magic, protocol_ver, cmd, length, status, game_id, store_id, keychip_id) - if head.length != len(data): + if head.length > len(data): raise ADBHeaderException(f"Length is incorrect! Expect {head.length}, got {len(data)}") return head diff --git a/core/aimedb.py b/core/aimedb.py index e65c2c7..2fe1ef7 100644 --- a/core/aimedb.py +++ b/core/aimedb.py @@ -2,8 +2,8 @@ from twisted.internet.protocol import Factory, Protocol import logging, coloredlogs from Crypto.Cipher import AES import struct -from typing import Dict, Tuple, Callable, Union -from typing_extensions import Final +from typing import Dict, Tuple, Callable, Union, Optional +import asyncio from logging.handlers import TimedRotatingFileHandler from core.config import CoreConfig @@ -11,15 +11,37 @@ from core.utils import create_sega_auth_key from core.data import Data from .adb_handlers import * - -class AimedbProtocol(Protocol): +class AimedbServlette(): request_list: Dict[int, Tuple[Callable[[bytes, int], Union[ADBBaseResponse, bytes]], int, str]] = {} - - def __init__(self, core_cfg: CoreConfig) -> None: - self.logger = logging.getLogger("aimedb") - self.config = core_cfg + def __init__(self, core_cfg: CoreConfig) -> None: + self.config = core_cfg self.data = Data(core_cfg) - if core_cfg.aimedb.key == "": + + self.logger = logging.getLogger("aimedb") + if not hasattr(self.logger, "initted"): + log_fmt_str = "[%(asctime)s] Aimedb | %(levelname)s | %(message)s" + log_fmt = logging.Formatter(log_fmt_str) + + fileHandler = TimedRotatingFileHandler( + "{0}/{1}.log".format(self.config.server.log_dir, "aimedb"), + when="d", + backupCount=10, + ) + fileHandler.setFormatter(log_fmt) + + consoleHandler = logging.StreamHandler() + consoleHandler.setFormatter(log_fmt) + + self.logger.addHandler(fileHandler) + self.logger.addHandler(consoleHandler) + + self.logger.setLevel(self.config.aimedb.loglevel) + coloredlogs.install( + level=core_cfg.aimedb.loglevel, logger=self.logger, fmt=log_fmt_str + ) + self.logger.initted = True + + if not core_cfg.aimedb.key: self.logger.error("!!!KEY NOT SET!!!") exit(1) @@ -40,27 +62,30 @@ class AimedbProtocol(Protocol): self.register_handler(0x13, 0x14, self.handle_log_ex, 'aime_log_ex') self.register_handler(0x64, 0x65, self.handle_hello, 'hello') - self.register_handler(0x66, 0, self.handle_goodbye, 'goodbye') - + def register_handler(self, cmd: int, resp:int, handler: Callable[[bytes, int], Union[ADBBaseResponse, bytes]], name: str) -> None: self.request_list[cmd] = (handler, resp, name) + + def start(self) -> None: + self.logger.info(f"Start on port {self.config.aimedb.port}") + asyncio.create_task(asyncio.start_server(self.dataReceived, self.config.server.listen_address, self.config.aimedb.port)) + + async def dataReceived(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter): + self.logger.debug(f"Connection made from {writer.get_extra_info('peername')[0]}") + while True: + try: + data: bytes = await reader.read() + if len(data) == 0: + self.logger.debug("Connection closed") + return + await self.process_data(data, reader, writer) + await writer.drain() + except ConnectionResetError as e: + self.logger.warn("Connection reset, disconnecting") + return - def append_padding(self, data: bytes): - """Appends 0s to the end of the data until it's at the correct size""" - length = struct.unpack_from(" None: - self.logger.debug(f"{self.transport.getPeer().host} Connected") - - def connectionLost(self, reason) -> None: - self.logger.debug( - f"{self.transport.getPeer().host} Disconnected - {reason.value}" - ) - - def dataReceived(self, data: bytes) -> None: + async def process_data(self, data: bytes, reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> Optional[bytes]: + addr = writer.get_extra_info('peername')[0] cipher = AES.new(self.config.aimedb.key.encode(), AES.MODE_ECB) try: @@ -68,9 +93,9 @@ class AimedbProtocol(Protocol): except Exception as e: self.logger.error(f"Failed to decrypt {data.hex()} because {e}") - return None + return - self.logger.debug(f"{self.transport.getPeer().host} wrote {decrypted.hex()}") + self.logger.debug(f"{addr} wrote {decrypted.hex()}") try: head = ADBHeader.from_data(decrypted) @@ -79,7 +104,9 @@ class AimedbProtocol(Protocol): self.logger.error(f"Error parsing ADB header: {e}") try: encrypted = cipher.encrypt(ADBBaseResponse().make()) - self.transport.write(encrypted) + writer.write(encrypted) + await writer.drain() + return except Exception as e: self.logger.error(f"Failed to encrypt default response because {e}") @@ -89,46 +116,51 @@ class AimedbProtocol(Protocol): if head.keychip_id == "ABCD1234567" or head.store_id == 0xfff0: self.logger.warning(f"Request from uninitialized AMLib: {vars(head)}") + if head.cmd == 0x66: + self.logger.info("Goodbye") + writer.close() + return + handler, resp_code, name = self.request_list.get(head.cmd, (self.handle_default, None, 'default')) if resp_code is None: self.logger.warning(f"No handler for cmd {hex(head.cmd)}") elif resp_code > 0: - self.logger.info(f"{name} from {head.keychip_id} ({head.game_id}) @ {self.transport.getPeer().host}") + self.logger.info(f"{name} from {head.keychip_id} ({head.game_id}) @ {addr}") - resp = handler(decrypted, resp_code) + resp = await handler(decrypted, resp_code) if type(resp) == ADBBaseResponse or issubclass(type(resp), ADBBaseResponse): resp_bytes = resp.make() - if len(resp_bytes) != resp.head.length: - resp_bytes = self.append_padding(resp_bytes) elif type(resp) == bytes: resp_bytes = resp elif resp is None: # Nothing to send, probably a goodbye + self.logger.warn(f"None return by handler for {name}") return else: + self.logger.error(f"Unsupported type returned by ADB handler for {name}: {type(resp)}") raise TypeError(f"Unsupported type returned by ADB handler for {name}: {type(resp)}") - try: + try: encrypted = cipher.encrypt(resp_bytes) self.logger.debug(f"Response {resp_bytes.hex()}") - self.transport.write(encrypted) + writer.write(encrypted) except Exception as e: self.logger.error(f"Failed to encrypt {resp_bytes.hex()} because {e}") - - def handle_default(self, data: bytes, resp_code: int, length: int = 0x20) -> ADBBaseResponse: + + async def handle_default(self, data: bytes, resp_code: int, length: int = 0x20) -> ADBBaseResponse: req = ADBHeader.from_data(data) return ADBBaseResponse(resp_code, length, 1, req.game_id, req.store_id, req.keychip_id, req.protocol_ver) - def handle_hello(self, data: bytes, resp_code: int) -> ADBBaseResponse: - return self.handle_default(data, resp_code) + async def handle_hello(self, data: bytes, resp_code: int) -> ADBBaseResponse: + return await self.handle_default(data, resp_code) - def handle_campaign(self, data: bytes, resp_code: int) -> ADBBaseResponse: + async def handle_campaign(self, data: bytes, resp_code: int) -> ADBBaseResponse: h = ADBHeader.from_data(data) if h.protocol_ver >= 0x3030: req = h @@ -143,26 +175,26 @@ class AimedbProtocol(Protocol): # We don't currently support campaigns return resp - def handle_lookup(self, data: bytes, resp_code: int) -> ADBBaseResponse: + async def handle_lookup(self, data: bytes, resp_code: int) -> ADBBaseResponse: req = ADBLookupRequest(data) user_id = self.data.card.get_user_id_from_card(req.access_code) is_banned = self.data.card.get_card_banned(req.access_code) is_locked = self.data.card.get_card_locked(req.access_code) - + + ret = ADBLookupResponse.from_req(req.head, user_id) if is_banned and is_locked: ret.head.status = ADBStatus.BAN_SYS_USER elif is_banned: ret.head.status = ADBStatus.BAN_SYS elif is_locked: ret.head.status = ADBStatus.LOCK_USER - ret = ADBLookupResponse.from_req(req.head, user_id) self.logger.info( f"access_code {req.access_code} -> user_id {ret.user_id}" ) return ret - def handle_lookup_ex(self, data: bytes, resp_code: int) -> ADBBaseResponse: + async def handle_lookup_ex(self, data: bytes, resp_code: int) -> ADBBaseResponse: req = ADBLookupRequest(data) user_id = self.data.card.get_user_id_from_card(req.access_code) @@ -191,7 +223,7 @@ class AimedbProtocol(Protocol): return ret - def handle_felica_lookup(self, data: bytes, resp_code: int) -> bytes: + async def handle_felica_lookup(self, data: bytes, resp_code: int) -> bytes: """ On official, I think a card has to be registered for this to actually work, but I'm making the executive decision to not implement that and just kick back our @@ -207,7 +239,7 @@ class AimedbProtocol(Protocol): ) return ADBFelicaLookupResponse.from_req(req.head, ac) - def handle_felica_register(self, data: bytes, resp_code: int) -> bytes: + async def handle_felica_register(self, data: bytes, resp_code: int) -> bytes: """ I've never seen this used. """ @@ -239,7 +271,7 @@ class AimedbProtocol(Protocol): return ADBFelicaLookupResponse.from_req(req.head, ac) - def handle_felica_lookup_ex(self, data: bytes, resp_code: int) -> bytes: + async def handle_felica_lookup_ex(self, data: bytes, resp_code: int) -> bytes: req = ADBFelicaLookup2Request(data) access_code = self.data.card.to_access_code(req.idm) user_id = self.data.card.get_user_id_from_card(access_code=access_code) @@ -263,7 +295,7 @@ class AimedbProtocol(Protocol): return resp - def handle_campaign_clear(self, data: bytes, resp_code: int) -> ADBBaseResponse: + async def handle_campaign_clear(self, data: bytes, resp_code: int) -> ADBBaseResponse: req = ADBCampaignClearRequest(data) resp = ADBCampaignClearResponse.from_req(req.head) @@ -271,7 +303,7 @@ class AimedbProtocol(Protocol): # We don't support campaign stuff return resp - def handle_register(self, data: bytes, resp_code: int) -> bytes: + async def handle_register(self, data: bytes, resp_code: int) -> bytes: req = ADBLookupRequest(data) user_id = -1 @@ -305,17 +337,17 @@ class AimedbProtocol(Protocol): return resp # TODO: Save these in some capacity, as deemed relevant - def handle_status_log(self, data: bytes, resp_code: int) -> bytes: + async def handle_status_log(self, data: bytes, resp_code: int) -> bytes: req = ADBStatusLogRequest(data) self.logger.info(f"User {req.aime_id} logged {req.status.name} event") return ADBBaseResponse(resp_code, 0x20, 1, req.head.game_id, req.head.store_id, req.head.keychip_id, req.head.protocol_ver) - def handle_log(self, data: bytes, resp_code: int) -> bytes: + async def handle_log(self, data: bytes, resp_code: int) -> bytes: req = ADBLogRequest(data) self.logger.info(f"User {req.aime_id} logged {req.status.name} event, credit_ct: {req.credit_ct} bet_ct: {req.bet_ct} won_ct: {req.won_ct}") return ADBBaseResponse(resp_code, 0x20, 1, req.head.game_id, req.head.store_id, req.head.keychip_id, req.head.protocol_ver) - def handle_log_ex(self, data: bytes, resp_code: int) -> bytes: + async def handle_log_ex(self, data: bytes, resp_code: int) -> bytes: req = ADBLogExRequest(data) strs = [] self.logger.info(f"Recieved {req.num_logs} or {len(req.logs)} logs") @@ -324,43 +356,3 @@ class AimedbProtocol(Protocol): self.logger.debug(f"User {req.logs[x].aime_id} logged {req.logs[x].status.name} event, credit_ct: {req.logs[x].credit_ct} bet_ct: {req.logs[x].bet_ct} won_ct: {req.logs[x].won_ct}") return ADBLogExResponse.from_req(req.head) - def handle_goodbye(self, data: bytes, resp_code: int) -> None: - self.logger.info(f"goodbye from {self.transport.getPeer().host}") - self.transport.loseConnection() - return - -class AimedbFactory(Factory): - protocol = AimedbProtocol - - def __init__(self, cfg: CoreConfig) -> None: - self.config = cfg - log_fmt_str = "[%(asctime)s] Aimedb | %(levelname)s | %(message)s" - log_fmt = logging.Formatter(log_fmt_str) - self.logger = logging.getLogger("aimedb") - - fileHandler = TimedRotatingFileHandler( - "{0}/{1}.log".format(self.config.server.log_dir, "aimedb"), - when="d", - backupCount=10, - ) - fileHandler.setFormatter(log_fmt) - - consoleHandler = logging.StreamHandler() - consoleHandler.setFormatter(log_fmt) - - self.logger.addHandler(fileHandler) - self.logger.addHandler(consoleHandler) - - self.logger.setLevel(self.config.aimedb.loglevel) - coloredlogs.install( - level=cfg.aimedb.loglevel, logger=self.logger, fmt=log_fmt_str - ) - - if self.config.aimedb.key == "": - self.logger.error("Please set 'key' field in your config file.") - exit(1) - - self.logger.info(f"Ready on port {self.config.aimedb.port}") - - def buildProtocol(self, addr): - return AimedbProtocol(self.config) diff --git a/core/allnet.py b/core/allnet.py index e83aae0..712b945 100644 --- a/core/allnet.py +++ b/core/allnet.py @@ -1,20 +1,24 @@ -from typing import Dict, List, Any, Optional, Tuple, Union, Final -import logging, coloredlogs -from logging.handlers import TimedRotatingFileHandler -from twisted.web.http import Request -from datetime import datetime import pytz import base64 import zlib import json +import yaml +import logging +import coloredlogs +import urllib.parse +import math +from typing import Dict, List, Any, Optional, Union, Final +from logging.handlers import TimedRotatingFileHandler +from starlette.requests import Request +from starlette.responses import PlainTextResponse +from starlette.applications import Starlette +from starlette.routing import Route +from datetime import datetime from enum import Enum from Crypto.PublicKey import RSA from Crypto.Hash import SHA from Crypto.Signature import PKCS1_v1_5 -from time import strptime -from os import path -import urllib.parse -import math +from os import path, environ, mkdir, access, W_OK from .config import CoreConfig from .utils import Utils @@ -91,7 +95,6 @@ class DLI_STATUS(Enum): class AllnetServlet: def __init__(self, core_cfg: CoreConfig, cfg_folder: str): - super().__init__() self.config = core_cfg self.config_folder = cfg_folder self.data = Data(core_cfg) @@ -126,19 +129,20 @@ class AllnetServlet: self.logger.error("No games detected!") self.logger.info( - f"Serving {len(TitleServlet.title_registry)} game codes port {core_cfg.allnet.port}" + f"Serving {len(TitleServlet.title_registry)} game codes" ) - def handle_poweron(self, request: Request, _: Dict): + async def handle_poweron(self, request: Request): request_ip = Utils.get_ip_addr(request) - pragma_header = request.getHeader('Pragma') + pragma_header = request.headers.get('Pragma', "") is_dfi = pragma_header is not None and pragma_header == "DFI" + data = await request.body() try: if is_dfi: - req_urlencode = self.from_dfi(request.content.getvalue()) + req_urlencode = self.from_dfi(data) else: - req_urlencode = request.content.getvalue().decode() + req_urlencode = data req_dict = self.allnet_req_to_dict(req_urlencode) if req_dict is None: @@ -155,7 +159,7 @@ class AllnetServlet: except AllnetRequestException as e: if e.message != "": self.logger.error(e) - return b"" + return PlainTextResponse() if req.format_ver == 3: resp = AllnetPowerOnResponse3(req.token) @@ -176,7 +180,7 @@ class AllnetServlet: resp.stat = ALLNET_STAT.bad_machine.value resp_dict = {k: v for k, v in vars(resp).items() if v is not None} - return (urllib.parse.unquote(urllib.parse.urlencode(resp_dict)) + "\n").encode("utf-8") + return PlainTextResponse(urllib.parse.unquote(urllib.parse.urlencode(resp_dict)) + "\n") if machine is not None: arcade = self.data.arcade.get_arcade(machine["arcade"]) @@ -190,7 +194,7 @@ class AllnetServlet: resp.stat = ALLNET_STAT.bad_shop.value resp_dict = {k: v for k, v in vars(resp).items() if v is not None} - return (urllib.parse.unquote(urllib.parse.urlencode(resp_dict)) + "\n").encode("utf-8") + return PlainTextResponse(urllib.parse.unquote(urllib.parse.urlencode(resp_dict)) + "\n") elif (not arcade["ip"] or arcade["ip"] is None) and self.config.server.strict_ip_checking: msg = f"Serial {req.serial} attempted allnet auth from bad IP {req.ip}, but arcade {arcade['id']} has no IP set! (strict checking enabled)." @@ -201,7 +205,7 @@ class AllnetServlet: resp.stat = ALLNET_STAT.bad_shop.value resp_dict = {k: v for k, v in vars(resp).items() if v is not None} - return (urllib.parse.unquote(urllib.parse.urlencode(resp_dict)) + "\n").encode("utf-8") + return PlainTextResponse(urllib.parse.unquote(urllib.parse.urlencode(resp_dict)) + "\n") country = ( @@ -245,20 +249,20 @@ class AllnetServlet: resp.stat = ALLNET_STAT.bad_game.value resp_dict = {k: v for k, v in vars(resp).items() if v is not None} - return (urllib.parse.unquote(urllib.parse.urlencode(resp_dict)) + "\n").encode("utf-8") + return PlainTextResponse(urllib.parse.unquote(urllib.parse.urlencode(resp_dict)) + "\n") else: self.logger.info( f"Allowed unknown game {req.game_id} v{req.ver} to authenticate from {request_ip} due to 'is_develop' being enabled. S/N: {req.serial}" ) - resp.uri = f"http://{self.config.title.hostname}:{self.config.title.port}/{req.game_id}/{req.ver.replace('.', '')}/" - resp.host = f"{self.config.title.hostname}:{self.config.title.port}" + resp.uri = f"http://{self.config.server.hostname}:{self.config.server.port}/{req.game_id}/{req.ver.replace('.', '')}/" + resp.host = f"{self.config.server.hostname}:{self.config.server.port}" resp_dict = {k: v for k, v in vars(resp).items() if v is not None} resp_str = urllib.parse.unquote(urllib.parse.urlencode(resp_dict)) self.logger.debug(f"Allnet response: {resp_str}") - return (resp_str + "\n").encode("utf-8") + return PlainTextResponse(resp_str + "\n") int_ver = req.ver.replace(".", "") @@ -277,18 +281,19 @@ class AllnetServlet: request.responseHeaders.addRawHeader('Pragma', 'DFI') return self.to_dfi(resp_str)""" - return resp_str.encode("utf-8") + return PlainTextResponse(resp_str) - def handle_dlorder(self, request: Request, _: Dict): + async def handle_dlorder(self, request: Request): request_ip = Utils.get_ip_addr(request) - pragma_header = request.getHeader('Pragma') + pragma_header = request.headers.get('Pragma', "") is_dfi = pragma_header is not None and pragma_header == "DFI" + data = await request.body() try: if is_dfi: - req_urlencode = self.from_dfi(request.content.getvalue()) + req_urlencode = self.from_dfi(data) else: - req_urlencode = request.content.getvalue().decode() + req_urlencode = data.decode() req_dict = self.allnet_req_to_dict(req_urlencode) if req_dict is None: @@ -305,7 +310,7 @@ class AllnetServlet: except AllnetRequestException as e: if e.message != "": self.logger.error(e) - return b"" + return PlainTextResponse() self.logger.info( f"DownloadOrder from {request_ip} -> {req.game_id} v{req.ver} serial {req.serial}" @@ -316,18 +321,18 @@ class AllnetServlet: not self.config.allnet.allow_online_updates or not self.config.allnet.update_cfg_folder ): - return urllib.parse.unquote(urllib.parse.urlencode(vars(resp))) + "\n" + return PlainTextResponse(urllib.parse.unquote(urllib.parse.urlencode(vars(resp))) + "\n") else: # TODO: Keychip check if path.exists( f"{self.config.allnet.update_cfg_folder}/{req.game_id}-{req.ver.replace('.', '')}-app.ini" ): - resp.uri = f"http://{self.config.title.hostname}:{self.config.title.port}/dl/ini/{req.game_id}-{req.ver.replace('.', '')}-app.ini" + resp.uri = f"http://{self.config.server.hostname}:{self.config.server.port}/dl/ini/{req.game_id}-{req.ver.replace('.', '')}-app.ini" if path.exists( f"{self.config.allnet.update_cfg_folder}/{req.game_id}-{req.ver.replace('.', '')}-opt.ini" ): - resp.uri += f"|http://{self.config.title.hostname}:{self.config.title.port}/dl/ini/{req.game_id}-{req.ver.replace('.', '')}-opt.ini" + resp.uri += f"|http://{self.config.server.hostname}:{self.config.server.port}/dl/ini/{req.game_id}-{req.ver.replace('.', '')}-opt.ini" self.logger.debug(f"Sending download uri {resp.uri}") self.data.base.log_event("allnet", "DLORDER_REQ_SUCCESS", logging.INFO, f"{Utils.get_ip_addr(request)} requested DL Order for {req.serial} {req.game_id} v{req.ver}") @@ -337,33 +342,33 @@ class AllnetServlet: request.responseHeaders.addRawHeader('Pragma', 'DFI') return self.to_dfi(res_str)""" - return res_str + return PlainTextResponse(res_str) - def handle_dlorder_ini(self, request: Request, match: Dict) -> bytes: - if "file" not in match: - return b"" + async def handle_dlorder_ini(self, request: Request) -> bytes: + req_file = request.path_params.get("file", "").replace("%0A", "") - req_file = match["file"].replace("%0A", "") + if not req_file: + return PlainTextResponse(status_code=404) if path.exists(f"{self.config.allnet.update_cfg_folder}/{req_file}"): self.logger.info(f"Request for DL INI file {req_file} from {Utils.get_ip_addr(request)} successful") self.data.base.log_event("allnet", "DLORDER_INI_SENT", logging.INFO, f"{Utils.get_ip_addr(request)} successfully recieved {req_file}") - return open( - f"{self.config.allnet.update_cfg_folder}/{req_file}", "rb" - ).read() + return PlainTextResponse(open( + f"{self.config.allnet.update_cfg_folder}/{req_file}", "r" + ).read()) self.logger.info(f"DL INI File {req_file} not found") - return b"" + return PlainTextResponse() - def handle_dlorder_report(self, request: Request, match: Dict) -> bytes: - req_raw = request.content.getvalue() + async def handle_dlorder_report(self, request: Request) -> bytes: + req_raw = await request.body() client_ip = Utils.get_ip_addr(request) try: req_dict: Dict = json.loads(req_raw) except Exception as e: self.logger.warning(f"Failed to parse DL Report: {e}") - return "NG" + return PlainTextResponse("NG") dl_data_type = DLIMG_TYPE.app dl_data = req_dict.get("appimage", {}) @@ -374,13 +379,13 @@ class AllnetServlet: if dl_data is None or not dl_data: self.logger.warning(f"Failed to parse DL Report: Invalid format - contains neither appimage nor optimage") - return "NG" + return PlainTextResponse("NG") rep = DLReport(dl_data, dl_data_type) if not rep.validate(): self.logger.warning(f"Failed to parse DL Report: Invalid format - {rep.err}") - return "NG" + return PlainTextResponse("NG") msg = f"{rep.serial} @ {client_ip} reported {rep.rep_type.name} download state {rep.rf_state.name} for {rep.gd} v{rep.dav}:"\ f" {rep.tdsc}/{rep.tsc} segments downloaded for working files {rep.wfl} with {rep.dfl if rep.dfl else 'none'} complete." @@ -388,10 +393,10 @@ class AllnetServlet: self.data.base.log_event("allnet", "DL_REPORT", logging.INFO, msg, dl_data) self.logger.info(msg) - return "OK" + return PlainTextResponse("OK") - def handle_loaderstaterecorder(self, request: Request, match: Dict) -> bytes: - req_data = request.content.getvalue() + async def handle_loaderstaterecorder(self, request: Request) -> bytes: + req_data = await request.body() sections = req_data.decode("utf-8").split("\r\n") req_dict = dict(urllib.parse.parse_qsl(sections[0])) @@ -403,18 +408,94 @@ class AllnetServlet: ip = Utils.get_ip_addr(request) if serial is None or num_files_dld is None or num_files_to_dl is None or dl_state is None: - return "NG".encode() + return PlainTextResponse("NG") self.logger.info(f"LoaderStateRecorder Request from {ip} {serial}: {num_files_dld}/{num_files_to_dl} Files download (State: {dl_state})") - return "OK".encode() + return PlainTextResponse("OK") - def handle_alive(self, request: Request, match: Dict) -> bytes: - return "OK".encode() + async def handle_alive(self, request: Request) -> bytes: + return PlainTextResponse("OK") - def handle_billing_request(self, request: Request, _: Dict): - req_raw = request.content.getvalue() + async def handle_naomitest(self, request: Request) -> bytes: + self.logger.info(f"Ping from {Utils.get_ip_addr(request)}") + return PlainTextResponse("naomi ok") + + def allnet_req_to_dict(self, data: str) -> Optional[List[Dict[str, Any]]]: + """ + Parses an allnet request string into a python dictionary + """ + try: + sections = data.split("\r\n") + + ret = [] + for x in sections: + ret.append(dict(urllib.parse.parse_qsl(x))) + return ret + + except Exception as e: + self.logger.error(f"allnet_req_to_dict: {e} while parsing {data}") + return None + + def from_dfi(self, data: bytes) -> str: + zipped = base64.b64decode(data) + unzipped = zlib.decompress(zipped) + return unzipped.decode("utf-8") + + def to_dfi(self, data: str) -> bytes: + unzipped = data.encode('utf-8') + zipped = zlib.compress(unzipped) + return base64.b64encode(zipped) + +class BillingServlet: + def __init__(self, core_cfg: CoreConfig, cfg_folder: str) -> None: + self.config = core_cfg + self.config_folder = cfg_folder + self.data = Data(core_cfg) + + self.logger = logging.getLogger("billing") + if not hasattr(self.logger, "initialized"): + log_fmt_str = "[%(asctime)s] Billing | %(levelname)s | %(message)s" + log_fmt = logging.Formatter(log_fmt_str) + + fileHandler = TimedRotatingFileHandler( + "{0}/{1}.log".format(self.config.server.log_dir, "billing"), + when="d", + backupCount=10, + ) + fileHandler.setFormatter(log_fmt) + + consoleHandler = logging.StreamHandler() + consoleHandler.setFormatter(log_fmt) + + self.logger.addHandler(fileHandler) + self.logger.addHandler(consoleHandler) + + self.logger.setLevel(core_cfg.allnet.loglevel) + coloredlogs.install( + level=core_cfg.billing.loglevel, logger=self.logger, fmt=log_fmt_str + ) + self.logger.initialized = True + + def billing_req_to_dict(self, data: bytes): + """ + Parses an billing request string into a python dictionary + """ + try: + sections = data.decode("ascii").split("\r\n") + + ret = [] + for x in sections: + ret.append(dict(urllib.parse.parse_qsl(x))) + return ret + + except Exception as e: + self.logger.error(f"billing_req_to_dict: {e} while parsing {data}") + return None + + async def handle_billing_request(self, request: Request): + req_raw = await request.body() - if request.getHeader('Content-Type') == "application/octet-stream": + if request.headers.get('Content-Type', '') == "application/octet-stream": req_unzip = zlib.decompressobj(-zlib.MAX_WBITS).decompress(req_raw) else: req_unzip = req_raw @@ -423,8 +504,8 @@ class AllnetServlet: request_ip = Utils.get_ip_addr(request) if req_dict is None: - self.logger.error(f"Failed to parse request {request.content.getvalue()}") - return b"" + self.logger.error(f"Failed to parse request {req_raw}") + return PlainTextResponse() self.logger.debug(f"request {req_dict}") @@ -436,7 +517,7 @@ class AllnetServlet: req = BillingInfo(req_dict[0]) except KeyError as e: self.logger.error(f"Billing request failed to parse: {e}") - return f"result=5&linelimit=&message=field is missing or formatting is incorrect\r\n".encode() + return PlainTextResponse("result=5&linelimit=&message=field is missing or formatting is incorrect\r\n") for x in range(1, len(req_dict)): if not req_dict[x]: @@ -467,7 +548,7 @@ class AllnetServlet: ) self.logger.warning(msg) - return f"result=1&requestno={req.requestno}&message=Keychip Serial bad\r\n".encode() + return PlainTextResponse(f"result=1&requestno={req.requestno}&message=Keychip Serial bad\r\n") msg = ( f"Billing checkin from {request_ip}: game {req.gameid} ver {req.gamever} keychip {req.keychipid} playcount " @@ -496,7 +577,6 @@ class AllnetServlet: # TODO: playhistory - #resp = BillingResponse(playlimit, playlimit_sig, nearfull, nearfull_sig) resp = BillingResponse(playlimit, playlimit_sig, nearfull, nearfull_sig, req.requestno, req.protocolver) resp_str = urllib.parse.unquote(urllib.parse.urlencode(vars(resp))) + "\r\n" @@ -504,56 +584,9 @@ class AllnetServlet: self.logger.debug(f"response {vars(resp)}") if req.traceleft > 0: self.logger.info(f"Requesting 20 more of {req.traceleft} unsent tracelogs") - return f"result=6&waittime=0&linelimit=20\r\n".encode() - - return resp_str.encode("utf-8") - - def handle_naomitest(self, request: Request, _: Dict) -> bytes: - self.logger.info(f"Ping from {Utils.get_ip_addr(request)}") - return b"naomi ok" - - def billing_req_to_dict(self, data: bytes): - """ - Parses an billing request string into a python dictionary - """ - try: - sections = data.decode("ascii").split("\r\n") - - ret = [] - for x in sections: - ret.append(dict(urllib.parse.parse_qsl(x))) - return ret - - except Exception as e: - self.logger.error(f"billing_req_to_dict: {e} while parsing {data}") - return None - - def allnet_req_to_dict(self, data: str) -> Optional[List[Dict[str, Any]]]: - """ - Parses an allnet request string into a python dictionary - """ - try: - sections = data.split("\r\n") - - ret = [] - for x in sections: - ret.append(dict(urllib.parse.parse_qsl(x))) - return ret - - except Exception as e: - self.logger.error(f"allnet_req_to_dict: {e} while parsing {data}") - return None - - def from_dfi(self, data: bytes) -> str: - zipped = base64.b64decode(data) - unzipped = zlib.decompress(zipped) - return unzipped.decode("utf-8") - - def to_dfi(self, data: str) -> bytes: - unzipped = data.encode('utf-8') - zipped = zlib.compress(unzipped) - return base64.b64encode(zipped) - + return PlainTextResponse("result=6&waittime=0&linelimit=20\r\n") + + return PlainTextResponse(resp_str) class AllnetPowerOnRequest: def __init__(self, req: Dict) -> None: @@ -613,7 +646,6 @@ class AllnetPowerOnResponse3(AllnetPowerOnResponse): self.minute = None self.second = None - class AllnetPowerOnResponse2(AllnetPowerOnResponse): def __init__(self) -> None: super().__init__() @@ -623,7 +655,6 @@ class AllnetPowerOnResponse2(AllnetPowerOnResponse): self.timezone = "+09:00" self.res_class = "PowerOnResponseV2" - class AllnetDownloadOrderRequest: def __init__(self, req: Dict) -> None: self.game_id = req.get("game_id", "") @@ -631,7 +662,6 @@ class AllnetDownloadOrderRequest: self.serial = req.get("serial", "") self.encode = req.get("encode", "") - class AllnetDownloadOrderResponse: def __init__(self, stat: int = 1, serial: str = "", uri: str = "") -> None: self.stat = stat @@ -781,7 +811,6 @@ class BillingResponse: # playhistory -> YYYYMM/C:... # YYYY -> 4 digit year, MM -> 2 digit month, C -> Playcount during that period - class AllnetRequestException(Exception): def __init__(self, message="") -> None: self.message = message @@ -849,3 +878,26 @@ class DLReport: return False return True + +cfg_dir = environ.get("DIANA_CFG_DIR", "config") +cfg: CoreConfig = CoreConfig() +if path.exists(f"{cfg_dir}/core.yaml"): + cfg.update(yaml.safe_load(open(f"{cfg_dir}/core.yaml"))) + +if not path.exists(cfg.server.log_dir): + mkdir(cfg.server.log_dir) + +if not access(cfg.server.log_dir, W_OK): + print( + f"Log directory {cfg.server.log_dir} NOT writable, please check permissions" + ) + exit(1) + +billing = BillingServlet(cfg, cfg_dir) +app = Starlette( + cfg.server.is_develop, + [ + Route("/request", billing.handle_billing_request, methods=["POST"]), + Route("/request/", billing.handle_billing_request, methods=["POST"]), + ] +) diff --git a/core/app.py b/core/app.py new file mode 100644 index 0000000..f20c041 --- /dev/null +++ b/core/app.py @@ -0,0 +1,89 @@ +import yaml +import logging +import coloredlogs +from logging.handlers import TimedRotatingFileHandler +from starlette.routing import Route +from starlette.requests import Request +from starlette.applications import Starlette +from starlette.responses import PlainTextResponse +from os import environ, path, mkdir, W_OK, access +from typing import List + +from core import CoreConfig, TitleServlet, MuchaServlet, AllnetServlet, BillingServlet, AimedbServlette +from core.frontend import FrontendServlet + +async def dummy_rt(request: Request): + return PlainTextResponse("Service OK") + +cfg_dir = environ.get("ARTEMIS_CFG_DIR", "config") +cfg: CoreConfig = CoreConfig() +if path.exists(f"{cfg_dir}/core.yaml"): + cfg.update(yaml.safe_load(open(f"{cfg_dir}/core.yaml"))) + +if not path.exists(cfg.server.log_dir): + mkdir(cfg.server.log_dir) + +if not access(cfg.server.log_dir, W_OK): + print( + f"Log directory {cfg.server.log_dir} NOT writable, please check permissions" + ) + exit(1) + +logger = logging.getLogger("core") +log_fmt_str = "[%(asctime)s] Core | %(levelname)s | %(message)s" +log_fmt = logging.Formatter(log_fmt_str) + +fileHandler = TimedRotatingFileHandler( + "{0}/{1}.log".format(cfg.server.log_dir, "core"), when="d", backupCount=10 +) +fileHandler.setFormatter(log_fmt) + +consoleHandler = logging.StreamHandler() +consoleHandler.setFormatter(log_fmt) + +logger.addHandler(fileHandler) +logger.addHandler(consoleHandler) + +log_lv = logging.DEBUG if cfg.server.is_develop else logging.INFO +logger.setLevel(log_lv) +coloredlogs.install(level=log_lv, logger=logger, fmt=log_fmt_str) + +logger.info(f"Artemis starting in {'develop' if cfg.server.is_develop else 'production'} mode") + +title = TitleServlet(cfg, cfg_dir) # This has to be loaded first to load plugins +mucha = MuchaServlet(cfg, cfg_dir) +allnet = AllnetServlet(cfg, cfg_dir) + +route_lst: List[Route] = [ + # Allnet + Route("/sys/servlet/PowerOn", allnet.handle_poweron, methods=["GET", "POST"]), + Route("/sys/servlet/DownloadOrder", allnet.handle_dlorder, methods=["GET", "POST"]), + Route("/sys/servlet/LoaderStateRecorder", allnet.handle_loaderstaterecorder, methods=["GET", "POST"]), + Route("/sys/servlet/Alive", allnet.handle_alive, methods=["GET", "POST"]), + Route("/report-api/Report", allnet.handle_dlorder_report, methods=["POST"]), + Route("/dl/ini/{file:str}", allnet.handle_dlorder_ini), + Route("/naomitest.html", allnet.handle_naomitest), + # Mucha + Route("/mucha_front/boardauth.do", mucha.handle_boardauth, methods=["POST"]), + Route("/mucha_front/updatacheck.do", mucha.handle_updatecheck, methods=["POST"]), + Route("/mucha_front/downloadstate.do", mucha.handle_dlstate, methods=["POST"]), +] + +if not cfg.billing.standalone: + billing = BillingServlet(cfg, cfg_dir) + route_lst += [ + Route("/request", billing.handle_billing_request, methods=["POST"]), + Route("/request/", billing.handle_billing_request, methods=["POST"]), + ] + +if not cfg.frontend.standalone: + frontend = FrontendServlet(cfg, cfg_dir) + route_lst += frontend.get_routes() +else: + route_lst.append(Route("/", dummy_rt)) + route_lst.append(Route("/robots.txt", FrontendServlet.robots)) + +for code, game in title.title_registry.items(): + route_lst += game.get_routes() + +app = Starlette(cfg.server.is_develop, route_lst) diff --git a/core/config.py b/core/config.py index 68db052..0a95742 100644 --- a/core/config.py +++ b/core/config.py @@ -1,16 +1,48 @@ import logging, os from typing import Any - class ServerConfig: def __init__(self, parent_config: "CoreConfig") -> None: self.__config = parent_config @property def listen_address(self) -> str: + """ + Address Artemis will bind to and listen on + """ return CoreConfig.get_config_field( self.__config, "core", "server", "listen_address", default="127.0.0.1" ) + + @property + def hostname(self) -> str: + """ + Hostname sent to games + """ + return CoreConfig.get_config_field( + self.__config, "core", "server", "hostname", default="localhost" + ) + + @property + def port(self) -> int: + """ + Port the game will listen on + """ + return CoreConfig.get_config_field( + self.__config, "core", "server", "port", default=8080 + ) + + @property + def ssl_key(self) -> str: + return CoreConfig.get_config_field( + self.__config, "core", "server", "ssl_key", default="cert/title.key" + ) + + @property + def ssl_cert(self) -> str: + return CoreConfig.get_config_field( + self.__config, "core", "title", "ssl_cert", default="cert/title.pem" + ) @property def allow_user_registration(self) -> bool: @@ -43,9 +75,13 @@ class ServerConfig: ) @property - def threading(self) -> bool: + def proxy_port(self) -> int: + """ + What port the proxy is listening on. This will be sent instead of 'port' if + is_using_proxy is True and this value is non-zero + """ return CoreConfig.get_config_field( - self.__config, "core", "server", "threading", default=False + self.__config, "core", "title", "proxy_port", default=0 ) @property @@ -66,7 +102,6 @@ class ServerConfig: self.__config, "core", "server", "strict_ip_checking", default=False ) - class TitleConfig: def __init__(self, parent_config: "CoreConfig") -> None: self.__config = parent_config @@ -79,49 +114,18 @@ class TitleConfig: ) ) - @property - def hostname(self) -> str: - return CoreConfig.get_config_field( - self.__config, "core", "title", "hostname", default="localhost" - ) - - @property - def port(self) -> int: - return CoreConfig.get_config_field( - self.__config, "core", "title", "port", default=8080 - ) - - @property - def port_ssl(self) -> int: - return CoreConfig.get_config_field( - self.__config, "core", "title", "port_ssl", default=0 - ) - - @property - def ssl_key(self) -> str: - return CoreConfig.get_config_field( - self.__config, "core", "title", "ssl_key", default="cert/title.key" - ) - - @property - def ssl_cert(self) -> str: - return CoreConfig.get_config_field( - self.__config, "core", "title", "ssl_cert", default="cert/title.pem" - ) - @property def reboot_start_time(self) -> str: return CoreConfig.get_config_field( - self.__config, "core", "title", "reboot_start_time", default="" + self.__config, "core", "title", "reboot_start_time", default="04:00" ) @property def reboot_end_time(self) -> str: return CoreConfig.get_config_field( - self.__config, "core", "title", "reboot_end_time", default="" + self.__config, "core", "title", "reboot_end_time", default="05:00" ) - class DatabaseConfig: def __init__(self, parent_config: "CoreConfig") -> None: self.__config = parent_config @@ -176,16 +180,6 @@ class DatabaseConfig: ) ) - @property - def user_table_autoincrement_start(self) -> int: - return CoreConfig.get_config_field( - self.__config, - "core", - "database", - "user_table_autoincrement_start", - default=10000, - ) - @property def enable_memcached(self) -> bool: return CoreConfig.get_config_field( @@ -198,21 +192,14 @@ class DatabaseConfig: self.__config, "core", "database", "memcached_host", default="localhost" ) - class FrontendConfig: def __init__(self, parent_config: "CoreConfig") -> None: self.__config = parent_config @property - def enable(self) -> int: + def standalone(self) -> int: return CoreConfig.get_config_field( - self.__config, "core", "frontend", "enable", default=False - ) - - @property - def port(self) -> int: - return CoreConfig.get_config_field( - self.__config, "core", "frontend", "port", default=8090 + self.__config, "core", "frontend", "standalone", default=True ) @property @@ -222,7 +209,12 @@ class FrontendConfig: self.__config, "core", "frontend", "loglevel", default="info" ) ) - + + @property + def secret(self) -> str: + CoreConfig.get_config_field( + self.__config, "core", "frontend", "secret", default="" + ) class AllnetConfig: def __init__(self, parent_config: "CoreConfig") -> None: @@ -236,18 +228,6 @@ class AllnetConfig: ) ) - @property - def port(self) -> int: - return CoreConfig.get_config_field( - self.__config, "core", "allnet", "port", default=80 - ) - - @property - def ip_check(self) -> bool: - return CoreConfig.get_config_field( - self.__config, "core", "allnet", "ip_check", default=False - ) - @property def allow_online_updates(self) -> int: return CoreConfig.get_config_field( @@ -260,10 +240,23 @@ class AllnetConfig: self.__config, "core", "allnet", "update_cfg_folder", default="" ) - class BillingConfig: def __init__(self, parent_config: "CoreConfig") -> None: self.__config = parent_config + + @property + def standalone(self) -> bool: + return CoreConfig.get_config_field( + self.__config, "core", "billing", "standalone", default=True + ) + + @property + def loglevel(self) -> int: + return CoreConfig.str_to_loglevel( + CoreConfig.get_config_field( + self.__config, "core", "billing", "loglevel", default="info" + ) + ) @property def port(self) -> int: @@ -289,7 +282,6 @@ class BillingConfig: self.__config, "core", "billing", "signing_key", default="cert/billing.key" ) - class AimedbConfig: def __init__(self, parent_config: "CoreConfig") -> None: self.__config = parent_config @@ -326,17 +318,10 @@ class AimedbConfig: self.__config, "core", "aimedb", "id_lifetime_seconds", default=86400 ) - class MuchaConfig: def __init__(self, parent_config: "CoreConfig") -> None: self.__config = parent_config - @property - def enable(self) -> int: - return CoreConfig.get_config_field( - self.__config, "core", "mucha", "enable", default=False - ) - @property def loglevel(self) -> int: return CoreConfig.str_to_loglevel( @@ -345,13 +330,6 @@ class MuchaConfig: ) ) - @property - def hostname(self) -> str: - return CoreConfig.get_config_field( - self.__config, "core", "mucha", "hostname", default="localhost" - ) - - class CoreConfig(dict): def __init__(self) -> None: self.server = ServerConfig(self) diff --git a/core/data/alembic/README b/core/data/alembic/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/core/data/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/core/data/alembic/alembic.ini b/core/data/alembic/alembic.ini new file mode 100644 index 0000000..299f51f --- /dev/null +++ b/core/data/alembic/alembic.ini @@ -0,0 +1,89 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = . + +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date +# within the migration file as well as the filename. +# string value is passed to dateutil.tz.gettz() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the +# "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; this defaults +# to ./versions. When using multiple version +# directories, initial revisions must be specified with --version-path +# version_locations = %(here)s/bar %(here)s/bat ./versions + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = driver://user:pass@localhost/dbname + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks=black +# black.type=console_scripts +# black.entrypoint=black +# black.options=-l 79 + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/core/data/alembic/env.py b/core/data/alembic/env.py new file mode 100644 index 0000000..70518a2 --- /dev/null +++ b/core/data/alembic/env.py @@ -0,0 +1,77 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = None + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/core/data/alembic/script.py.mako b/core/data/alembic/script.py.mako new file mode 100644 index 0000000..2c01563 --- /dev/null +++ b/core/data/alembic/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/core/frontend.py b/core/frontend.py index 0ee2211..054092c 100644 --- a/core/frontend.py +++ b/core/frontend.py @@ -2,7 +2,9 @@ import logging, coloredlogs from typing import Any, Dict, List from twisted.web import resource from twisted.web.util import redirectTo -from twisted.web.http import Request +from starlette.requests import Request +from starlette.routing import Route +from starlette.responses import Response, PlainTextResponse from logging.handlers import TimedRotatingFileHandler from twisted.web.server import Session from zope.interface import Interface, Attribute, implementer @@ -98,8 +100,15 @@ class FrontendServlet(resource.Resource): self.putChild(b"game", fe_game) self.logger.info( - f"Ready on port {self.config.frontend.port} serving {len(fe_game.children)} games" + f"Ready on port {self.config.server.port} serving {len(fe_game.children)} games" ) + + def get_routes(self) -> List[Route]: + return [] + + @classmethod + async def robots(cls, request: Request) -> PlainTextResponse: + return PlainTextResponse("User-agent: *\nDisallow: /\n\nUser-agent: AdsBot-Google\nDisallow: /") def render_GET(self, request): self.logger.debug(f"{Utils.get_ip_addr(request)} -> {request.uri.decode()}") diff --git a/core/mucha.py b/core/mucha.py index 7c6f0ab..64f20aa 100644 --- a/core/mucha.py +++ b/core/mucha.py @@ -2,7 +2,7 @@ from typing import Dict, Any, Optional, List import logging, coloredlogs from logging.handlers import TimedRotatingFileHandler from twisted.web import resource -from twisted.web.http import Request +from starlette.requests import Request from datetime import datetime from Crypto.Cipher import Blowfish import pytz @@ -12,7 +12,7 @@ from .utils import Utils from .title import TitleServlet class MuchaServlet: - mucha_registry: List[str] = [] + mucha_registry: Dict[str, str] = {} def __init__(self, cfg: CoreConfig, cfg_dir: str) -> None: self.config = cfg self.config_dir = cfg_dir @@ -39,11 +39,12 @@ class MuchaServlet: for _, mod in TitleServlet.title_registry.items(): if hasattr(mod, "get_mucha_info"): - enabled, game_cd = mod.get_mucha_info( + enabled, game_cds, netid_prefixes = mod.get_mucha_info( self.config, self.config_dir ) if enabled: - self.mucha_registry.append(game_cd) + for x in range(len(game_cds)): + self.mucha_registry[game_cds[x]] = netid_prefixes[x] self.logger.info(f"Serving {len(self.mucha_registry)} games") @@ -75,7 +76,7 @@ class MuchaServlet: self.logger.debug(f"Decrypt SN to {sn_decrypt.hex()}") resp = MuchaAuthResponse( - f"{self.config.mucha.hostname}{':' + str(self.config.allnet.port) if self.config.server.is_develop else ''}" + f"{self.config.server.hostname}{':' + str(self.config.server.port) if self.config.server.is_develop else ''}" ) self.logger.debug(f"Mucha response {vars(resp)}") @@ -100,7 +101,7 @@ class MuchaServlet: self.logger.warning(f"Unknown gameCd {req.gameCd}") return b"RESULTS=000" - resp = MuchaUpdateResponse(req.gameVer, f"{self.config.mucha.hostname}{':' + str(self.config.allnet.port) if self.config.server.is_develop else ''}") + resp = MuchaUpdateResponse(req.gameVer, f"{self.config.server.hostname}{':' + str(self.config.server.port) if self.config.server.is_develop else ''}") self.logger.debug(f"Mucha response {vars(resp)}") diff --git a/core/title.py b/core/title.py index 3fdb30c..8bead43 100644 --- a/core/title.py +++ b/core/title.py @@ -1,7 +1,9 @@ from typing import Dict, List, Tuple import logging, coloredlogs from logging.handlers import TimedRotatingFileHandler -from twisted.web.http import Request +from starlette.requests import Request +from starlette.responses import Response +from starlette.routing import Route from core.config import CoreConfig from core.data import Data @@ -28,18 +30,16 @@ class BaseServlet: """ return False - def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]: + def get_routes(self) -> List[Route]: """Called during boot to get all matcher endpoints this title servlet handles Returns: - Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]: A 2-length tuple where offset 0 is GET and offset 1 is POST, - containing a list of 3-length tuples where offset 0 is the name of the function in the handler that should be called, offset 1 - is the matching string, and offset 2 is a dict containing rules for the matcher. + List[Route]: A list of Routes, WebSocketRoutes, or similar classes """ - return ( - [("render_GET", "/{game}/{version}/{endpoint}", {'game': R'S...'})], - [("render_POST", "/{game}/{version}/{endpoint}", {'game': R'S...'})] - ) + return [ + Route("/{game}/{version}/{endpoint}", self.render_POST, methods=["POST"]), + Route("/{game}/{version}/{endpoint}", self.render_GET, methods=["GET"]), + ] def setup(self) -> None: """Called once during boot, should contain any additional setup the handler must do, such as starting any sub-services @@ -58,11 +58,11 @@ class BaseServlet: Tuple[str, str]: A tuple where offset 0 is the allnet uri field, and offset 1 is the allnet host field """ if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80: - return (f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_code}/{game_ver}/", "") + return (f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_code}/{game_ver}/", "") - return (f"http://{self.core_cfg.title.hostname}/{game_code}/{game_ver}/", "") + return (f"http://{self.core_cfg.server.hostname}/{game_code}/{game_ver}/", "") - def get_mucha_info(self, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, str]: + def get_mucha_info(self, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, List[str], List[str]]: """Called once during boot to check if this game is a mucha game Args: @@ -72,15 +72,15 @@ class BaseServlet: Returns: Tuple[bool, str]: Tuple where offset 0 is true if the game is enabled, false otherwise, and offset 1 is the game CD """ - return (False, "") + return (False, [], []) - def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes: - self.logger.warn(f"{game_code} Does not dispatch POST") - return None + async def render_POST(self, request: Request) -> bytes: + self.logger.warn(f"Game Does not dispatch POST") + return Response() - def render_GET(self, request: Request, game_code: str, matchers: Dict) -> bytes: - self.logger.warn(f"{game_code} Does not dispatch GET") - return None + async def render_GET(self, request: Request) -> bytes: + self.logger.warn(f"Game Does not dispatch GET") + return Response() class TitleServlet: title_registry: Dict[str, BaseServlet] = {} @@ -136,7 +136,7 @@ class TitleServlet: self.logger.error(f"{folder} missing game_code or index in __init__.py, or is_game_enabled in index") self.logger.info( - f"Serving {len(self.title_registry)} game codes {'on port ' + str(core_cfg.title.port) if core_cfg.title.port > 0 else ''}" + f"Serving {len(self.title_registry)} game codes {'on port ' + str(core_cfg.server.port) if core_cfg.server.port > 0 else ''}" ) def render_GET(self, request: Request, endpoints: dict) -> bytes: diff --git a/core/utils.py b/core/utils.py index 8264213..4dfb4dc 100644 --- a/core/utils.py +++ b/core/utils.py @@ -1,6 +1,6 @@ from typing import Dict, Any, Optional from types import ModuleType -from twisted.web.http import Request +from starlette.requests import Request import logging import importlib from os import walk @@ -34,36 +34,16 @@ class Utils: @classmethod def get_ip_addr(cls, req: Request) -> str: - return ( - req.getAllHeaders()[b"x-forwarded-for"].decode() - if b"x-forwarded-for" in req.getAllHeaders() - else req.getClientAddress().host - ) + return req.headers.get("x-forwarded-for", req.client.host) @classmethod def get_title_port(cls, cfg: CoreConfig): if cls.real_title_port is not None: return cls.real_title_port - if cfg.title.port == 0: - cls.real_title_port = cfg.allnet.port - - else: - cls.real_title_port = cfg.title.port + cls.real_title_port = cfg.server.proxy_port if cfg.server.is_using_proxy else cfg.server.port return cls.real_title_port - @classmethod - def get_title_port_ssl(cls, cfg: CoreConfig): - if cls.real_title_port_ssl is not None: return cls.real_title_port_ssl - - if cfg.title.port_ssl == 0: - cls.real_title_port_ssl = 443 - - else: - cls.real_title_port_ssl = cfg.title.port_ssl - - return cls.real_title_port_ssl - def create_sega_auth_key(aime_id: int, game: str, place_id: int, keychip_id: str, b64_secret: str, exp_seconds: int = 86400, err_logger: str = 'aimedb') -> Optional[str]: logger = logging.getLogger(err_logger) try: diff --git a/dbutils.py b/dbutils.py index 85b18a0..f5e57be 100644 --- a/dbutils.py +++ b/dbutils.py @@ -1,9 +1,11 @@ -import yaml +#!/usr/bin/env python3 import argparse import logging -from core.config import CoreConfig -from core.data import Data -from os import path, mkdir, access, W_OK +from os import mkdir, path, access, W_OK +import yaml +import asyncio + +from core import Data, CoreConfig if __name__ == "__main__": parser = argparse.ArgumentParser(description="Database utilities") @@ -16,19 +18,9 @@ if __name__ == "__main__": type=str, help="Version of the database to upgrade/rollback to", ) - parser.add_argument( - "--game", - "-g", - type=str, - help="Game code of the game who's schema will be updated/rolled back. Ex. SDFE", - ) parser.add_argument("--email", "-e", type=str, help="Email for the new user") - parser.add_argument("--old_ac", "-o", type=str, help="Access code to transfer from") - parser.add_argument("--new_ac", "-n", type=str, help="Access code to transfer to") - parser.add_argument("--force", "-f", type=bool, help="Force the action to happen") - parser.add_argument( - "action", type=str, help="DB Action, create, recreate, upgrade, or rollback" - ) + parser.add_argument("--access_code", "-a", type=str, help="Access code for new/transfer user", default="00000000000000000000") + parser.add_argument("action", type=str, help="create, upgrade, create-owner") args = parser.parse_args() cfg = CoreConfig() @@ -50,42 +42,13 @@ if __name__ == "__main__": if args.action == "create": data.create_database() - - elif args.action == "recreate": - data.recreate_database() - - elif args.action == "upgrade" or args.action == "rollback": - if args.version is None: - data.logger.warning("No version set, upgrading to latest") - - if args.game is None: - data.logger.warning("No game set, upgrading core schema") - data.migrate_database( - "CORE", - int(args.version) if args.version is not None else None, - args.action, - ) - - else: - data.migrate_database( - args.game, - int(args.version) if args.version is not None else None, - args.action, - ) - - elif args.action == "autoupgrade": - data.autoupgrade() + + elif args.action == "upgrade": + data.schema_upgrade(args.version) elif args.action == "create-owner": - data.create_owner(args.email) + loop = asyncio.get_event_loop() + loop.run_until_complete(data.create_owner(args.email, args.access_code)) - elif args.action == "migrate-card": - data.migrate_card(args.old_ac, args.new_ac, args.force) - - elif args.action == "cleanup": - data.delete_hanging_users() - - elif args.action == "version": - data.show_versions() - - data.logger.info("Done") + else: + logging.getLogger("database").info(f"Unknown action {args.action}") diff --git a/example_config/core.yaml b/example_config/core.yaml index 21b1a9d..f5b11f4 100644 --- a/example_config/core.yaml +++ b/example_config/core.yaml @@ -1,26 +1,24 @@ server: - listen_address: "127.0.0.1" + listen_address: "127.0.0.1" + hostname: "localhost" + port: 8080 + ssl_key: "cert/title.key" + ssl_cert: "cert/title.crt" allow_user_registration: True allow_unregistered_serials: True name: "ARTEMiS" is_develop: True is_using_proxy: False - threading: False + proxy_port: 0 log_dir: "logs" check_arcade_ip: False strict_ip_checking: False title: loglevel: "info" - hostname: "localhost" - port: 8080 - port_ssl: 0 - ssl_cert: "cert/title.crt" - ssl_key: "cert/title.key" reboot_start_time: "04:00" reboot_end_time: "05:00" - - + ssl_key: "cert/title.key" database: host: "localhost" username: "aime" @@ -30,23 +28,22 @@ database: protocol: "mysql" sha2_password: False loglevel: "warn" - user_table_autoincrement_start: 10000 enable_memcached: True memcached_host: "localhost" frontend: - enable: False - port: 8090 + standalone: True loglevel: "info" + secret: "" allnet: loglevel: "info" - port: 80 - ip_check: False allow_online_updates: False update_cfg_folder: "" billing: + standalone: True + loglevel: "info" port: 8443 ssl_key: "cert/server.key" ssl_cert: "cert/server.pem" @@ -60,6 +57,4 @@ aimedb: id_lifetime_seconds: 86400 mucha: - enable: False - hostname: "localhost" loglevel: "info" diff --git a/index.py b/index.py index 798519c..c820f22 100644 --- a/index.py +++ b/index.py @@ -1,335 +1,92 @@ #!/usr/bin/env python3 import argparse -import logging, coloredlogs -from logging.handlers import TimedRotatingFileHandler -from typing import Dict import yaml -from os import path, mkdir, access, W_OK -from core import * +from os import path, mkdir, access, W_OK, environ +import uvicorn +import logging +import asyncio -from twisted.web import server, resource -from twisted.internet import reactor, endpoints -from twisted.web.http import Request -from routes import Mapper -from threading import Thread +from core import CoreConfig, AimedbServlette -class HttpDispatcher(resource.Resource): - def __init__(self, cfg: CoreConfig, config_dir: str): - super().__init__() - self.config = cfg - self.isLeaf = True - self.map_get = Mapper() - self.map_post = Mapper() - self.logger = logging.getLogger("core") - - self.title = TitleServlet(cfg, config_dir) - self.allnet = AllnetServlet(cfg, config_dir) - self.mucha = MuchaServlet(cfg, config_dir) - - self.map_get.connect( - "allnet_downloadorder_ini", - "/dl/ini/{file}", - controller="allnet", - action="handle_dlorder_ini", - conditions=dict(method=["GET"]), +async def launch_main(cfg: CoreConfig, ssl: bool) -> None: + if ssl: + server_cfg = uvicorn.Config( + "core.app:app", + host=cfg.server.listen_address, + port=cfg.server.port if args.port == 0 else args.port, + reload=cfg.server.is_develop, + log_level="info" if cfg.server.is_develop else "critical", + ssl_version=3, + ssl_certfile=cfg.server.ssl_cert, + ssl_keyfile=cfg.server.ssl_key + ) + else: + server_cfg = uvicorn.Config( + "core.app:app", + host=cfg.server.listen_address, + port=cfg.server.port if args.port == 0 else args.port, + reload=cfg.server.is_develop, + log_level="info" if cfg.server.is_develop else "critical" ) + server = uvicorn.Server(server_cfg) + await server.serve() - self.map_post.connect( - "allnet_downloadorder_report", - "/report-api/Report", - controller="allnet", - action="handle_dlorder_report", - conditions=dict(method=["POST"]), +async def launch_billing(cfg: CoreConfig) -> None: + server_cfg = uvicorn.Config( + "core.allnet:app", + host=cfg.server.listen_address, + port=cfg.billing.port, + reload=cfg.server.is_develop, + log_level="info" if cfg.server.is_develop else "critical", + ssl_version=3, + ssl_certfile=cfg.billing.ssl_cert, + ssl_keyfile=cfg.billing.ssl_key ) + server = uvicorn.Server(server_cfg) + if cfg.billing.standalone: + await server.serve() + else: + while True: + pass - self.map_get.connect( - "allnet_ping", - "/naomitest.html", - controller="allnet", - action="handle_naomitest", - conditions=dict(method=["GET"]), - ) - self.map_post.connect( - "allnet_poweron", - "/sys/servlet/PowerOn", - controller="allnet", - action="handle_poweron", - conditions=dict(method=["POST"]), - ) - self.map_post.connect( - "allnet_downloadorder", - "/sys/servlet/DownloadOrder", - controller="allnet", - action="handle_dlorder", - conditions=dict(method=["POST"]), - ) - self.map_post.connect( - "allnet_loaderstaterecorder", - "/sys/servlet/LoaderStateRecorder", - controller="allnet", - action="handle_loaderstaterecorder", - conditions=dict(method=["POST"]), - ) - self.map_post.connect( - "allnet_alive", - "/sys/servlet/Alive", - controller="allnet", - action="handle_alive", - conditions=dict(method=["POST"]), - ) - self.map_get.connect( - "allnet_alive", - "/sys/servlet/Alive", - controller="allnet", - action="handle_alive", - conditions=dict(method=["GET"]), - ) - self.map_post.connect( - "allnet_billing", - "/request", - controller="allnet", - action="handle_billing_request", - conditions=dict(method=["POST"]), - ) - self.map_post.connect( - "allnet_billing", - "/request/", - controller="allnet", - action="handle_billing_request", - conditions=dict(method=["POST"]), - ) - # Maintain compatability - self.map_post.connect( - "mucha_boardauth", - "/mucha/boardauth.do", - controller="mucha", - action="handle_boardauth", - conditions=dict(method=["POST"]), - ) - self.map_post.connect( - "mucha_updatacheck", - "/mucha/updatacheck.do", - controller="mucha", - action="handle_updatecheck", - conditions=dict(method=["POST"]), - ) - self.map_post.connect( - "mucha_dlstate", - "/mucha/downloadstate.do", - controller="mucha", - action="handle_dlstate", - conditions=dict(method=["POST"]), - ) - - self.map_post.connect( - "mucha_boardauth", - "/mucha_front/boardauth.do", - controller="mucha", - action="handle_boardauth", - conditions=dict(method=["POST"]), - ) - self.map_post.connect( - "mucha_updatacheck", - "/mucha_front/updatacheck.do", - controller="mucha", - action="handle_updatecheck", - conditions=dict(method=["POST"]), - ) - self.map_post.connect( - "mucha_dlstate", - "/mucha_front/downloadstate.do", - controller="mucha", - action="handle_dlstate", - conditions=dict(method=["POST"]), - ) - - for code, game in self.title.title_registry.items(): - get_matchers, post_matchers = game.get_endpoint_matchers() - - for m in get_matchers: - self.map_get.connect( - "title_get", - m[1], - controller="title", - action="render_GET", - title=code, - subaction=m[0], - conditions=dict(method=["GET"]), - requirements=m[2], - ) - - for m in post_matchers: - self.map_post.connect( - "title_post", - m[1], - controller="title", - action="render_POST", - title=code, - subaction=m[0], - conditions=dict(method=["POST"]), - requirements=m[2], - ) - - def render_GET(self, request: Request) -> bytes: - test = self.map_get.match(request.uri.decode()) - client_ip = Utils.get_ip_addr(request) - - if test is None: - self.logger.debug( - f"Unknown GET endpoint {request.uri.decode()} from {client_ip} to port {request.getHost().port}" - ) - request.setResponseCode(404) - return b"Endpoint not found." - - return self.dispatch(test, request) - - def render_POST(self, request: Request) -> bytes: - test = self.map_post.match(request.uri.decode()) - client_ip = Utils.get_ip_addr(request) - - if test is None: - self.logger.debug( - f"Unknown POST endpoint {request.uri.decode()} from {client_ip} to port {request.getHost().port}" - ) - request.setResponseCode(404) - return b"Endpoint not found." - - return self.dispatch(test, request) - - def dispatch(self, matcher: Dict, request: Request) -> bytes: - controller = getattr(self, matcher["controller"], None) - if controller is None: - self.logger.error( - f"Controller {matcher['controller']} not found via endpoint {request.uri.decode()}" - ) - request.setResponseCode(404) - return b"Endpoint not found." - - handler = getattr(controller, matcher["action"], None) - if handler is None: - self.logger.error( - f"Action {matcher['action']} not found in controller {matcher['controller']} via endpoint {request.uri.decode()}" - ) - request.setResponseCode(404) - return b"Endpoint not found." - - url_vars = matcher - url_vars.pop("controller") - url_vars.pop("action") - ret = handler(request, url_vars) - - if type(ret) == str: - return ret.encode() - - elif type(ret) == bytes or type(ret) == tuple: # allow for bytes or tuple (data, response code) responses - return ret - - elif ret is None: - self.logger.warning(f"None returned by controller for {request.uri.decode()} endpoint") - return b"" - - else: - self.logger.warning(f"Unknown data type returned by controller for {request.uri.decode()} endpoint") - return b"" +async def launcher(cfg: CoreConfig, ssl: bool) -> None: + AimedbServlette(cfg).start() + done, pending = await asyncio.wait( + [ + asyncio.create_task(launch_main(cfg, ssl)), + asyncio.create_task(launch_billing(cfg)), + ], + return_when=asyncio.FIRST_COMPLETED, + ) + + logging.getLogger("core").info("Shutdown") + for pending_task in pending: + pending_task.cancel("Another service died, server is shutting down") if __name__ == "__main__": - parser = argparse.ArgumentParser(description="ARTEMiS main entry point") + parser = argparse.ArgumentParser(description="Artemis main entry point") parser.add_argument( "--config", "-c", type=str, default="config", help="Configuration folder" ) + parser.add_argument( + "--port", "-p", type=int, default=0, help="Port override" + ) + parser.add_argument( + "--ssl", "-s", type=bool, help="Launch with SSL" + ) args = parser.parse_args() if not path.exists(f"{args.config}/core.yaml"): print( - f"The config folder you specified ({args.config}) does not exist or does not contain core.yaml.\nDid you copy the example folder?" + f"The config folder you specified ({args.config}) does not exist or does not contain core.yaml. Defaults will be used.\nDid you copy the example folder?" ) - exit(1) - + cfg: CoreConfig = CoreConfig() if path.exists(f"{args.config}/core.yaml"): cfg.update(yaml.safe_load(open(f"{args.config}/core.yaml"))) - if not path.exists(cfg.server.log_dir): - mkdir(cfg.server.log_dir) + environ["ARTEMIS_CFG_DIR"] = args.config - if not access(cfg.server.log_dir, W_OK): - print( - f"Log directory {cfg.server.log_dir} NOT writable, please check permissions" - ) - exit(1) - - logger = logging.getLogger("core") - log_fmt_str = "[%(asctime)s] Core | %(levelname)s | %(message)s" - log_fmt = logging.Formatter(log_fmt_str) - - fileHandler = TimedRotatingFileHandler( - "{0}/{1}.log".format(cfg.server.log_dir, "core"), when="d", backupCount=10 - ) - fileHandler.setFormatter(log_fmt) - - consoleHandler = logging.StreamHandler() - consoleHandler.setFormatter(log_fmt) - - logger.addHandler(fileHandler) - logger.addHandler(consoleHandler) - - log_lv = logging.DEBUG if cfg.server.is_develop else logging.INFO - logger.setLevel(log_lv) - coloredlogs.install(level=log_lv, logger=logger, fmt=log_fmt_str) - - if not cfg.aimedb.key: - logger.error("!!AIMEDB KEY BLANK, SET KEY IN CORE.YAML!!") - exit(1) - - logger.info( - f"ARTEMiS starting in {'develop' if cfg.server.is_develop else 'production'} mode" - ) - - allnet_server_str = f"tcp:{cfg.allnet.port}:interface={cfg.server.listen_address}" - title_server_str = f"tcp:{cfg.title.port}:interface={cfg.server.listen_address}" - title_https_server_str = f"ssl:{cfg.title.port_ssl}:interface={cfg.server.listen_address}:privateKey={cfg.title.ssl_key}:certKey={cfg.title.ssl_cert}" - adb_server_str = f"tcp:{cfg.aimedb.port}:interface={cfg.server.listen_address}" - frontend_server_str = ( - f"tcp:{cfg.frontend.port}:interface={cfg.server.listen_address}" - ) - - billing_server_str = f"tcp:{cfg.billing.port}:interface={cfg.server.listen_address}" - if cfg.server.is_develop: - billing_server_str = ( - f"ssl:{cfg.billing.port}:interface={cfg.server.listen_address}" - f":privateKey={cfg.billing.ssl_key}:certKey={cfg.billing.ssl_cert}" - ) - - dispatcher = HttpDispatcher(cfg, args.config) - - endpoints.serverFromString(reactor, allnet_server_str).listen( - server.Site(dispatcher) - ) - endpoints.serverFromString(reactor, adb_server_str).listen(AimedbFactory(cfg)) - - if cfg.frontend.enable: - endpoints.serverFromString(reactor, frontend_server_str).listen( - server.Site(FrontendServlet(cfg, args.config)) - ) - - if cfg.billing.port > 0: - endpoints.serverFromString(reactor, billing_server_str).listen( - server.Site(dispatcher) - ) - - if cfg.title.port > 0: - endpoints.serverFromString(reactor, title_server_str).listen( - server.Site(dispatcher) - ) - - if cfg.title.port_ssl > 0: - endpoints.serverFromString(reactor, title_https_server_str).listen( - server.Site(dispatcher) - ) - - if cfg.server.threading: - Thread(target=reactor.run, args=(False,)).start() - else: - reactor.run() + asyncio.run(launcher(cfg, args.ssl)) diff --git a/read.py b/read.py index fa34314..05bbd1d 100644 --- a/read.py +++ b/read.py @@ -1,4 +1,4 @@ -# vim: set fileencoding=utf-8 +#!/usr/bin/env python3 import argparse import re import os diff --git a/requirements.txt b/requirements.txt index c399e1f..fb27cb0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ mypy wheel -twisted pytz pyyaml sqlalchemy==1.4.46 @@ -12,10 +11,11 @@ inflection coloredlogs pylibmc; platform_system != "Windows" wacky -Routes bcrypt jinja2 protobuf -autobahn pillow -pyjwt +pyjwt==2.8.0 +websockets +starlette +asyncio diff --git a/titles/chuni/air.py b/titles/chuni/air.py index b9bc1d3..094340c 100644 --- a/titles/chuni/air.py +++ b/titles/chuni/air.py @@ -11,7 +11,7 @@ class ChuniAir(ChuniBase): super().__init__(core_cfg, game_cfg) self.version = ChuniConstants.VER_CHUNITHM_AIR - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: - ret = super().handle_get_game_setting_api_request(data) + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + ret = await super().handle_get_game_setting_api_request(data) ret["gameSetting"]["dataVersion"] = "1.10.00" return ret diff --git a/titles/chuni/airplus.py b/titles/chuni/airplus.py index f0d8224..9b35c69 100644 --- a/titles/chuni/airplus.py +++ b/titles/chuni/airplus.py @@ -11,7 +11,7 @@ class ChuniAirPlus(ChuniBase): super().__init__(core_cfg, game_cfg) self.version = ChuniConstants.VER_CHUNITHM_AIR_PLUS - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: - ret = super().handle_get_game_setting_api_request(data) + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + ret = await super().handle_get_game_setting_api_request(data) ret["gameSetting"]["dataVersion"] = "1.15.00" return ret diff --git a/titles/chuni/amazon.py b/titles/chuni/amazon.py index b765c2f..84f5a12 100644 --- a/titles/chuni/amazon.py +++ b/titles/chuni/amazon.py @@ -13,7 +13,7 @@ class ChuniAmazon(ChuniBase): super().__init__(core_cfg, game_cfg) self.version = ChuniConstants.VER_CHUNITHM_AMAZON - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: - ret = super().handle_get_game_setting_api_request(data) + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + ret = await super().handle_get_game_setting_api_request(data) ret["gameSetting"]["dataVersion"] = "1.30.00" return ret diff --git a/titles/chuni/amazonplus.py b/titles/chuni/amazonplus.py index ea8d704..9ce13cf 100644 --- a/titles/chuni/amazonplus.py +++ b/titles/chuni/amazonplus.py @@ -13,7 +13,7 @@ class ChuniAmazonPlus(ChuniBase): super().__init__(core_cfg, game_cfg) self.version = ChuniConstants.VER_CHUNITHM_AMAZON_PLUS - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: - ret = super().handle_get_game_setting_api_request(data) + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + ret = await super().handle_get_game_setting_api_request(data) ret["gameSetting"]["dataVersion"] = "1.35.00" return ret diff --git a/titles/chuni/base.py b/titles/chuni/base.py index 8d22488..cffcb75 100644 --- a/titles/chuni/base.py +++ b/titles/chuni/base.py @@ -22,7 +22,7 @@ class ChuniBase: self.game = ChuniConstants.GAME_CODE self.version = ChuniConstants.VER_CHUNITHM - def handle_game_login_api_request(self, data: Dict) -> Dict: + async def handle_game_login_api_request(self, data: Dict) -> Dict: """ Handles the login bonus logic, required for the game because getUserLoginBonus gets called after getUserItem and therefore the @@ -119,11 +119,11 @@ class ChuniBase: return {"returnCode": 1} - def handle_game_logout_api_request(self, data: Dict) -> Dict: + async def handle_game_logout_api_request(self, data: Dict) -> Dict: # self.data.base.log_event("chuni", "logout", logging.INFO, {"version": self.version, "user": data["userId"]}) return {"returnCode": 1} - def handle_get_game_charge_api_request(self, data: Dict) -> Dict: + async def handle_get_game_charge_api_request(self, data: Dict) -> Dict: game_charge_list = self.data.static.get_enabled_charges(self.version) if game_charge_list is None or len(game_charge_list) == 0: @@ -145,7 +145,7 @@ class ChuniBase: ) return {"length": len(charges), "gameChargeList": charges} - def handle_get_game_event_api_request(self, data: Dict) -> Dict: + async def handle_get_game_event_api_request(self, data: Dict) -> Dict: game_events = self.data.static.get_enabled_events(self.version) if game_events is None or len(game_events) == 0: @@ -177,10 +177,10 @@ class ChuniBase: "gameEventList": event_list, } - def handle_get_game_idlist_api_request(self, data: Dict) -> Dict: + async def handle_get_game_idlist_api_request(self, data: Dict) -> Dict: return {"type": data["type"], "length": 0, "gameIdlistList": []} - def handle_get_game_message_api_request(self, data: Dict) -> Dict: + async def handle_get_game_message_api_request(self, data: Dict) -> Dict: return { "type": data["type"], "length": 1, @@ -193,14 +193,14 @@ class ChuniBase: }] } - def handle_get_game_ranking_api_request(self, data: Dict) -> Dict: + async def handle_get_game_ranking_api_request(self, data: Dict) -> Dict: rankings = self.data.score.get_rankings(self.version) return {"type": data["type"], "gameRankingList": rankings} - def handle_get_game_sale_api_request(self, data: Dict) -> Dict: + async def handle_get_game_sale_api_request(self, data: Dict) -> Dict: return {"type": data["type"], "length": 0, "gameSaleList": []} - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: # if reboot start/end time is not defined use the default behavior of being a few hours ago if self.core_cfg.title.reboot_start_time == "" or self.core_cfg.title.reboot_end_time == "": reboot_start = datetime.strftime( @@ -240,7 +240,7 @@ class ChuniBase: "isDumpUpload": "false", "isAou": "false", } - def handle_get_user_activity_api_request(self, data: Dict) -> Dict: + async def handle_get_user_activity_api_request(self, data: Dict) -> Dict: user_activity_list = self.data.profile.get_profile_activity( data["userId"], data["kind"] ) @@ -261,7 +261,7 @@ class ChuniBase: "userActivityList": activity_list, } - def handle_get_user_character_api_request(self, data: Dict) -> Dict: + async def handle_get_user_character_api_request(self, data: Dict) -> Dict: characters = self.data.item.get_characters(data["userId"]) if characters is None: return { @@ -296,7 +296,7 @@ class ChuniBase: "userCharacterList": character_list, } - def handle_get_user_charge_api_request(self, data: Dict) -> Dict: + async def handle_get_user_charge_api_request(self, data: Dict) -> Dict: user_charge_list = self.data.profile.get_profile_charge(data["userId"]) charge_list = [] @@ -312,14 +312,14 @@ class ChuniBase: "userChargeList": charge_list, } - def handle_get_user_recent_player_api_request(self, data: Dict) -> Dict: + async def handle_get_user_recent_player_api_request(self, data: Dict) -> Dict: return { "userId": data["userId"], "length": 0, "userRecentPlayerList": [], # playUserId, playUserName, playDate, friendPoint } - def handle_get_user_course_api_request(self, data: Dict) -> Dict: + async def handle_get_user_course_api_request(self, data: Dict) -> Dict: user_course_list = self.data.score.get_courses(data["userId"]) if user_course_list is None: return { @@ -354,7 +354,7 @@ class ChuniBase: "userCourseList": course_list, } - def handle_get_user_data_api_request(self, data: Dict) -> Dict: + async def handle_get_user_data_api_request(self, data: Dict) -> Dict: p = self.data.profile.get_profile_data(data["userId"], self.version) if p is None: return {} @@ -366,7 +366,7 @@ class ChuniBase: return {"userId": data["userId"], "userData": profile} - def handle_get_user_data_ex_api_request(self, data: Dict) -> Dict: + async def handle_get_user_data_ex_api_request(self, data: Dict) -> Dict: p = self.data.profile.get_profile_data_ex(data["userId"], self.version) if p is None: return {} @@ -378,7 +378,7 @@ class ChuniBase: return {"userId": data["userId"], "userDataEx": profile} - def handle_get_user_duel_api_request(self, data: Dict) -> Dict: + async def handle_get_user_duel_api_request(self, data: Dict) -> Dict: user_duel_list = self.data.item.get_duels(data["userId"]) if user_duel_list is None: return {} @@ -396,7 +396,7 @@ class ChuniBase: "userDuelList": duel_list, } - def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict: + async def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict: p = self.data.profile.get_rival(data["rivalId"]) if p is None: return {} @@ -409,7 +409,7 @@ class ChuniBase: "userRivalData": userRivalData } - def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict: + async def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict: rival_id = data["rivalId"] next_index = int(data["nextIndex"]) max_count = int(data["maxCount"]) @@ -462,7 +462,7 @@ class ChuniBase: return result - def handle_get_user_favorite_item_api_request(self, data: Dict) -> Dict: + async def handle_get_user_favorite_item_api_request(self, data: Dict) -> Dict: user_fav_item_list = [] # still needs to be implemented on WebUI @@ -482,14 +482,14 @@ class ChuniBase: "userFavoriteItemList": user_fav_item_list, } - def handle_get_user_favorite_music_api_request(self, data: Dict) -> Dict: + async def handle_get_user_favorite_music_api_request(self, data: Dict) -> Dict: """ This is handled via the webui, which we don't have right now """ return {"userId": data["userId"], "length": 0, "userFavoriteMusicList": []} - def handle_get_user_item_api_request(self, data: Dict) -> Dict: + async def handle_get_user_item_api_request(self, data: Dict) -> Dict: kind = int(int(data["nextIndex"]) / 10000000000) next_idx = int(int(data["nextIndex"]) % 10000000000) user_item_list = self.data.item.get_items(data["userId"], kind) @@ -526,7 +526,7 @@ class ChuniBase: "userItemList": items, } - def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict: + async def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict: user_id = data["userId"] user_login_bonus = self.data.item.get_all_login_bonus(user_id, self.version) # ignore the loginBonus request if its disabled in config @@ -552,7 +552,7 @@ class ChuniBase: "userLoginBonusList": user_login_list, } - def handle_get_user_map_api_request(self, data: Dict) -> Dict: + async def handle_get_user_map_api_request(self, data: Dict) -> Dict: user_map_list = self.data.item.get_maps(data["userId"]) if user_map_list is None: return {} @@ -570,7 +570,7 @@ class ChuniBase: "userMapList": map_list, } - def handle_get_user_music_api_request(self, data: Dict) -> Dict: + async def handle_get_user_music_api_request(self, data: Dict) -> Dict: music_detail = self.data.score.get_scores(data["userId"]) if music_detail is None: return { @@ -629,7 +629,7 @@ class ChuniBase: "userMusicList": song_list, # 240 } - def handle_get_user_option_api_request(self, data: Dict) -> Dict: + async def handle_get_user_option_api_request(self, data: Dict) -> Dict: p = self.data.profile.get_profile_option(data["userId"]) option = p._asdict() @@ -638,7 +638,7 @@ class ChuniBase: return {"userId": data["userId"], "userGameOption": option} - def handle_get_user_option_ex_api_request(self, data: Dict) -> Dict: + async def handle_get_user_option_ex_api_request(self, data: Dict) -> Dict: p = self.data.profile.get_profile_option_ex(data["userId"]) option = p._asdict() @@ -650,7 +650,7 @@ class ChuniBase: def read_wtf8(self, src): return bytes([ord(c) for c in src]).decode("utf-8") - def handle_get_user_preview_api_request(self, data: Dict) -> Dict: + async def handle_get_user_preview_api_request(self, data: Dict) -> Dict: profile = self.data.profile.get_profile_preview(data["userId"], self.version) if profile is None: return None @@ -692,7 +692,7 @@ class ChuniBase: "userNameEx": profile["userName"], } - def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict: + async def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict: recent_rating_list = self.data.profile.get_profile_recent_rating(data["userId"]) if recent_rating_list is None: return { @@ -707,7 +707,7 @@ class ChuniBase: "userRecentRatingList": recent_rating_list["recentRating"], } - def handle_get_user_region_api_request(self, data: Dict) -> Dict: + async def handle_get_user_region_api_request(self, data: Dict) -> Dict: # TODO: Region return { "userId": data["userId"], @@ -715,7 +715,7 @@ class ChuniBase: "userRegionList": [], } - def handle_get_user_team_api_request(self, data: Dict) -> Dict: + async def handle_get_user_team_api_request(self, data: Dict) -> Dict: # Default values team_id = 65535 team_name = self.game_cfg.team.team_name @@ -750,7 +750,7 @@ class ChuniBase: }, } - def handle_get_team_course_setting_api_request(self, data: Dict) -> Dict: + async def handle_get_team_course_setting_api_request(self, data: Dict) -> Dict: return { "userId": data["userId"], "length": 0, @@ -758,7 +758,7 @@ class ChuniBase: "teamCourseSettingList": [], } - def handle_get_team_course_setting_api_request_proto(self, data: Dict) -> Dict: + async def handle_get_team_course_setting_api_request_proto(self, data: Dict) -> Dict: return { "userId": data["userId"], "length": 1, @@ -782,7 +782,7 @@ class ChuniBase: ], } - def handle_get_team_course_rule_api_request(self, data: Dict) -> Dict: + async def handle_get_team_course_rule_api_request(self, data: Dict) -> Dict: return { "userId": data["userId"], "length": 0, @@ -790,7 +790,7 @@ class ChuniBase: "teamCourseRuleList": [] } - def handle_get_team_course_rule_api_request_proto(self, data: Dict) -> Dict: + async def handle_get_team_course_rule_api_request_proto(self, data: Dict) -> Dict: return { "userId": data["userId"], "length": 1, @@ -807,7 +807,7 @@ class ChuniBase: ], } - def handle_upsert_user_all_api_request(self, data: Dict) -> Dict: + async def handle_upsert_user_all_api_request(self, data: Dict) -> Dict: upsert = data["upsertUserAll"] user_id = data["userId"] @@ -927,28 +927,28 @@ class ChuniBase: return {"returnCode": "1"} - def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict: + async def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict: # add tickets after they got bought, this makes sure the tickets are # still valid after an unsuccessful logout self.data.profile.put_profile_charge(data["userId"], data["userCharge"]) return {"returnCode": "1"} - def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict: + async def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict: return {"returnCode": "1"} - def handle_upsert_client_develop_api_request(self, data: Dict) -> Dict: + async def handle_upsert_client_develop_api_request(self, data: Dict) -> Dict: return {"returnCode": "1"} - def handle_upsert_client_error_api_request(self, data: Dict) -> Dict: + async def handle_upsert_client_error_api_request(self, data: Dict) -> Dict: return {"returnCode": "1"} - def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict: + async def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict: return {"returnCode": "1"} - def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict: + async def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict: return {"returnCode": "1"} - def handle_get_user_net_battle_data_api_request(self, data: Dict) -> Dict: + async def handle_get_user_net_battle_data_api_request(self, data: Dict) -> Dict: return { "userId": data["userId"], "userNetBattleData": {"recentNBSelectMusicList": []}, diff --git a/titles/chuni/crystal.py b/titles/chuni/crystal.py index a727ac3..9c08fd7 100644 --- a/titles/chuni/crystal.py +++ b/titles/chuni/crystal.py @@ -13,7 +13,7 @@ class ChuniCrystal(ChuniBase): super().__init__(core_cfg, game_cfg) self.version = ChuniConstants.VER_CHUNITHM_CRYSTAL - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: - ret = super().handle_get_game_setting_api_request(data) + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + ret = await super().handle_get_game_setting_api_request(data) ret["gameSetting"]["dataVersion"] = "1.40.00" return ret diff --git a/titles/chuni/crystalplus.py b/titles/chuni/crystalplus.py index fbb3969..90a0479 100644 --- a/titles/chuni/crystalplus.py +++ b/titles/chuni/crystalplus.py @@ -13,7 +13,7 @@ class ChuniCrystalPlus(ChuniBase): super().__init__(core_cfg, game_cfg) self.version = ChuniConstants.VER_CHUNITHM_CRYSTAL_PLUS - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: - ret = super().handle_get_game_setting_api_request(data) + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + ret = await super().handle_get_game_setting_api_request(data) ret["gameSetting"]["dataVersion"] = "1.45.00" return ret diff --git a/titles/chuni/index.py b/titles/chuni/index.py index fa8a394..b102d5e 100644 --- a/titles/chuni/index.py +++ b/titles/chuni/index.py @@ -1,4 +1,6 @@ -from twisted.web.http import Request +from starlette.requests import Request +from starlette.routing import Route +from starlette.responses import Response import logging, coloredlogs from logging.handlers import TimedRotatingFileHandler import zlib @@ -33,7 +35,6 @@ from .newplus import ChuniNewPlus from .sun import ChuniSun from .sunplus import ChuniSunPlus - class ChuniServlet(BaseServlet): def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None: super().__init__(core_cfg, cfg_dir) @@ -124,15 +125,6 @@ class ChuniServlet(BaseServlet): f"Hashed v{version} method {method_fixed} with {bytes.fromhex(keys[2])} to get {hash.hex()}" ) - def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]: - return ( - [], - [ - ("render_POST", "/{game}/{version}/ChuniServlet/{endpoint}", {}), - ("render_POST", "/{game}/{version}/ChuniServlet/MatchingServer/{endpoint}", {}) - ] - ) - @classmethod def is_game_enabled( cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str @@ -150,19 +142,25 @@ class ChuniServlet(BaseServlet): def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]: if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80: - return (f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_code}/{game_ver}/", self.core_cfg.title.hostname) + return (f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_code}/{game_ver}/", self.core_cfg.server.hostname) - return (f"http://{self.core_cfg.title.hostname}/{game_code}/{game_ver}/", self.core_cfg.title.hostname) + return (f"http://{self.core_cfg.server.hostname}/{game_code}/{game_ver}/", self.core_cfg.server.hostname) - def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes: - endpoint = matchers['endpoint'] - version = int(matchers['version']) - game_code = matchers['game'] + def get_routes(self) -> List[Route]: + return [ + Route("/{game:str}/{version:int}/ChuniServlet/{endpoint:str}", self.render_POST, methods=['POST']), + Route("/{game:str}/{version:int}/ChuniServlet/MatchingServer/{endpoint:str}", self.render_POST, methods=['POST']), + ] + + async def render_POST(self, request: Request) -> bytes: + endpoint: str = request.path_params.get('endpoint') + version: int = request.path_params.get('version') + game_code: str = request.path_params.get('game') if endpoint.lower() == "ping": - return zlib.compress(b'{"returnCode": "1"}') + return Response(zlib.compress(b'{"returnCode": "1"}')) - req_raw = request.content.getvalue() + req_raw = await request.body() encrtped = False internal_ver = 0 @@ -201,7 +199,7 @@ class ChuniServlet(BaseServlet): internal_ver = ChuniConstants.VER_CHUNITHM_SUN_PLUS elif game_code == "SDGS": # Int if version < 110: # SUPERSTAR - internal_ver = ChuniConstants.PARADISE + internal_ver = ChuniConstants.VER_CHUNITHM_PARADISE # FIXME: Not sure what was intended to go here? was just "PARADISE" elif version >= 110 and version < 115: # NEW internal_ver = ChuniConstants.VER_CHUNITHM_NEW elif version >= 115 and version < 120: # NEW PLUS!! @@ -216,20 +214,20 @@ class ChuniServlet(BaseServlet): # doing encrypted. The likelyhood of false positives is low but # technically not 0 if internal_ver < ChuniConstants.VER_CHUNITHM_NEW: - endpoint = request.getHeader("User-Agent").split("#")[0] + endpoint = request.headers.get("User-Agent").split("#")[0] else: if internal_ver not in self.hash_table: self.logger.error( f"v{version} does not support encryption or no keys entered" ) - return zlib.compress(b'{"stat": "0"}') + return Response(zlib.compress(b'{"stat": "0"}')) elif endpoint.lower() not in self.hash_table[internal_ver]: self.logger.error( f"No hash found for v{version} endpoint {endpoint}" ) - return zlib.compress(b'{"stat": "0"}') + return Response(zlib.compress(b'{"stat": "0"}')) endpoint = self.hash_table[internal_ver][endpoint.lower()] @@ -246,7 +244,7 @@ class ChuniServlet(BaseServlet): self.logger.error( f"Failed to decrypt v{version} request to {endpoint} -> {e}" ) - return zlib.compress(b'{"stat": "0"}') + return Response(zlib.compress(b'{"stat": "0"}')) encrtped = True @@ -258,7 +256,7 @@ class ChuniServlet(BaseServlet): self.logger.error( f"Unencrypted v{version} {endpoint} request, but config is set to encrypted only: {req_raw}" ) - return zlib.compress(b'{"stat": "0"}') + return Response(zlib.compress(b'{"stat": "0"}')) try: unzip = zlib.decompress(req_raw) @@ -267,7 +265,7 @@ class ChuniServlet(BaseServlet): self.logger.error( f"Failed to decompress v{version} {endpoint} request -> {e}" ) - return b"" + return Response(zlib.compress(b'{"stat": "0"}')) req_data = json.loads(unzip) @@ -285,11 +283,11 @@ class ChuniServlet(BaseServlet): else: try: handler = getattr(handler_cls, func_to_find) - resp = handler(req_data) + resp = await handler(req_data) except Exception as e: self.logger.error(f"Error handling v{version} method {endpoint} - {e}") - return zlib.compress(b'{"stat": "0"}') + return Response(zlib.compress(b'{"stat": "0"}')) if resp == None: resp = {"returnCode": 1} @@ -299,7 +297,7 @@ class ChuniServlet(BaseServlet): zipped = zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8")) if not encrtped: - return zipped + return Response(zipped) padded = pad(zipped, 16) @@ -309,4 +307,4 @@ class ChuniServlet(BaseServlet): bytes.fromhex(self.game_cfg.crypto.keys[internal_ver][1]), ) - return crypt.encrypt(padded) \ No newline at end of file + return Response(crypt.encrypt(padded)) \ No newline at end of file diff --git a/titles/chuni/new.py b/titles/chuni/new.py index d8a71b1..9902906 100644 --- a/titles/chuni/new.py +++ b/titles/chuni/new.py @@ -33,7 +33,7 @@ class ChuniNew(ChuniBase): if self.version == ChuniConstants.VER_CHUNITHM_SUN_PLUS: return "215" - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: # use UTC time and convert it to JST time by adding +9 # matching therefore starts one hour before and lasts for 8 hours match_start = datetime.strftime( @@ -82,26 +82,26 @@ class ChuniNew(ChuniBase): "matchErrorLimit": self.game_cfg.matching.match_error_limit, "romVersion": self.game_cfg.version.version(self.version)["rom"], "dataVersion": self.game_cfg.version.version(self.version)["data"], - "matchingUri": f"http://{self.core_cfg.title.hostname}:{t_port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/", - "matchingUriX": f"http://{self.core_cfg.title.hostname}:{t_port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/", + "matchingUri": f"http://{self.core_cfg.server.hostname}:{t_port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/", + "matchingUriX": f"http://{self.core_cfg.server.hostname}:{t_port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/", # might be really important for online battle to connect the cabs via UDP port 50201 - "udpHolePunchUri": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/", - "reflectorUri": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/", + "udpHolePunchUri": f"http://{self.core_cfg.server.hostname}:{self.core_cfg.server.port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/", + "reflectorUri": f"http://{self.core_cfg.server.hostname}:{self.core_cfg.server.port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/", }, "isDumpUpload": False, "isAou": False, } - def handle_remove_token_api_request(self, data: Dict) -> Dict: + async def handle_remove_token_api_request(self, data: Dict) -> Dict: return {"returnCode": "1"} - def handle_delete_token_api_request(self, data: Dict) -> Dict: + async def handle_delete_token_api_request(self, data: Dict) -> Dict: return {"returnCode": "1"} - def handle_create_token_api_request(self, data: Dict) -> Dict: + async def handle_create_token_api_request(self, data: Dict) -> Dict: return {"returnCode": "1"} - def handle_get_user_map_area_api_request(self, data: Dict) -> Dict: + async def handle_get_user_map_area_api_request(self, data: Dict) -> Dict: user_map_areas = self.data.item.get_map_areas(data["userId"]) map_areas = [] @@ -113,10 +113,10 @@ class ChuniNew(ChuniBase): return {"userId": data["userId"], "userMapAreaList": map_areas} - def handle_get_user_symbol_chat_setting_api_request(self, data: Dict) -> Dict: + async def handle_get_user_symbol_chat_setting_api_request(self, data: Dict) -> Dict: return {"userId": data["userId"], "symbolCharInfoList": []} - def handle_get_user_preview_api_request(self, data: Dict) -> Dict: + async def handle_get_user_preview_api_request(self, data: Dict) -> Dict: profile = self.data.profile.get_profile_preview(data["userId"], self.version) if profile is None: return None @@ -164,7 +164,7 @@ class ChuniNew(ChuniBase): } return data1 - def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: + async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: p = self.data.profile.get_profile_data(data["userId"], self.version) if p is None: return {} @@ -177,13 +177,13 @@ class ChuniNew(ChuniBase): "isLogin": False, } - def handle_printer_login_api_request(self, data: Dict) -> Dict: + async def handle_printer_login_api_request(self, data: Dict) -> Dict: return {"returnCode": 1} - def handle_printer_logout_api_request(self, data: Dict) -> Dict: + async def handle_printer_logout_api_request(self, data: Dict) -> Dict: return {"returnCode": 1} - def handle_get_game_gacha_api_request(self, data: Dict) -> Dict: + async def handle_get_game_gacha_api_request(self, data: Dict) -> Dict: """ returns all current active banners (gachas) """ @@ -213,7 +213,7 @@ class ChuniNew(ChuniBase): "registIdList": [], } - def handle_get_game_gacha_card_by_id_api_request(self, data: Dict) -> Dict: + async def handle_get_game_gacha_card_by_id_api_request(self, data: Dict) -> Dict: """ returns all valid cards for a given gachaId """ @@ -237,7 +237,7 @@ class ChuniNew(ChuniBase): "ssrBookCalcList": [], } - def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict: + async def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict: p = self.data.profile.get_profile_data(data["userId"], self.version) if p is None: return {} @@ -262,7 +262,7 @@ class ChuniNew(ChuniBase): ], } - def handle_get_user_gacha_api_request(self, data: Dict) -> Dict: + async def handle_get_user_gacha_api_request(self, data: Dict) -> Dict: user_gachas = self.data.item.get_user_gachas(data["userId"]) if user_gachas is None: return {"userId": data["userId"], "length": 0, "userGachaList": []} @@ -281,7 +281,7 @@ class ChuniNew(ChuniBase): "userGachaList": user_gacha_list, } - def handle_get_user_printed_card_api_request(self, data: Dict) -> Dict: + async def handle_get_user_printed_card_api_request(self, data: Dict) -> Dict: user_print_list = self.data.item.get_user_print_states( data["userId"], has_completed=True ) @@ -316,7 +316,7 @@ class ChuniNew(ChuniBase): "userPrintedCardList": print_list, } - def handle_get_user_card_print_error_api_request(self, data: Dict) -> Dict: + async def handle_get_user_card_print_error_api_request(self, data: Dict) -> Dict: user_id = data["userId"] user_print_states = self.data.item.get_user_print_states( @@ -338,13 +338,13 @@ class ChuniNew(ChuniBase): "userCardPrintStateList": card_print_state_list, } - def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict: + async def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict: return super().handle_get_user_character_api_request(data) - def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict: + async def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict: return super().handle_get_user_item_api_request(data) - def handle_roll_gacha_api_request(self, data: Dict) -> Dict: + async def handle_roll_gacha_api_request(self, data: Dict) -> Dict: """ Handle a gacha roll API request, with: gachaId: the gachaId where the cards should be pulled from @@ -386,7 +386,7 @@ class ChuniNew(ChuniBase): return {"length": len(rolled_cards), "gameGachaCardList": rolled_cards} - def handle_cm_upsert_user_gacha_api_request(self, data: Dict) -> Dict: + async def handle_cm_upsert_user_gacha_api_request(self, data: Dict) -> Dict: upsert = data["cmUpsertUserGacha"] user_id = data["userId"] place_id = data["placeId"] @@ -441,7 +441,7 @@ class ChuniNew(ChuniBase): "userCardPrintStateList": card_print_state_list, } - def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict: + async def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict: return { "returnCode": 1, "orderId": 0, @@ -449,7 +449,7 @@ class ChuniNew(ChuniBase): "apiName": "CMUpsertUserPrintlogApi", } - def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict: + async def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict: user_print_detail = data["userPrintDetail"] user_id = data["userId"] @@ -474,7 +474,7 @@ class ChuniNew(ChuniBase): "apiName": "CMUpsertUserPrintApi", } - def handle_cm_upsert_user_print_subtract_api_request(self, data: Dict) -> Dict: + async def handle_cm_upsert_user_print_subtract_api_request(self, data: Dict) -> Dict: upsert = data["userCardPrintState"] user_id = data["userId"] place_id = data["placeId"] @@ -491,7 +491,7 @@ class ChuniNew(ChuniBase): return {"returnCode": "1", "apiName": "CMUpsertUserPrintSubtractApi"} - def handle_cm_upsert_user_print_cancel_api_request(self, data: Dict) -> Dict: + async def handle_cm_upsert_user_print_cancel_api_request(self, data: Dict) -> Dict: order_ids = data["orderIdList"] user_id = data["userId"] @@ -501,11 +501,11 @@ class ChuniNew(ChuniBase): return {"returnCode": "1", "apiName": "CMUpsertUserPrintCancelApi"} - def handle_ping_request(self, data: Dict) -> Dict: + async def handle_ping_request(self, data: Dict) -> Dict: # matchmaking ping request return {"returnCode": "1"} - def handle_begin_matching_api_request(self, data: Dict) -> Dict: + async def handle_begin_matching_api_request(self, data: Dict) -> Dict: room_id = 1 # check if there is a free matching room matching_room = self.data.item.get_oldest_free_matching(self.version) @@ -554,7 +554,7 @@ class ChuniNew(ChuniBase): return {"roomId": 1, "matchingWaitState": matching_wait} - def handle_end_matching_api_request(self, data: Dict) -> Dict: + async def handle_end_matching_api_request(self, data: Dict) -> Dict: matching_room = self.data.item.get_matching(self.version, data["roomId"]) members = matching_room["matchingMemberInfoList"] @@ -579,10 +579,10 @@ class ChuniNew(ChuniBase): # no idea, maybe to differentiate between CPUs and real players? "matchingMemberRoleList": role_list, # TCP/UDP connection? - "reflectorUri": f"{self.core_cfg.title.hostname}", + "reflectorUri": f"{self.core_cfg.server.hostname}", } - def handle_remove_matching_member_api_request(self, data: Dict) -> Dict: + async def handle_remove_matching_member_api_request(self, data: Dict) -> Dict: # get all matching rooms, because Chuni only returns the userId # not the actual roomId matching_rooms = self.data.item.get_all_matchings(self.version) @@ -612,7 +612,7 @@ class ChuniNew(ChuniBase): return {"returnCode": "1"} - def handle_get_matching_state_api_request(self, data: Dict) -> Dict: + async def handle_get_matching_state_api_request(self, data: Dict) -> Dict: polling_interval = 1 # get the current active room matching_room = self.data.item.get_matching(self.version, data["roomId"]) diff --git a/titles/chuni/newplus.py b/titles/chuni/newplus.py index fa642d3..84467fb 100644 --- a/titles/chuni/newplus.py +++ b/titles/chuni/newplus.py @@ -11,8 +11,8 @@ class ChuniNewPlus(ChuniNew): super().__init__(core_cfg, game_cfg) self.version = ChuniConstants.VER_CHUNITHM_NEW_PLUS - def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: - user_data = super().handle_cm_get_user_preview_api_request(data) + async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: + user_data = await super().handle_cm_get_user_preview_api_request(data) # hardcode lastDataVersion for CardMaker 1.35 A028 user_data["lastDataVersion"] = "2.05.00" diff --git a/titles/chuni/paradise.py b/titles/chuni/paradise.py index 19155d6..88ed91c 100644 --- a/titles/chuni/paradise.py +++ b/titles/chuni/paradise.py @@ -13,7 +13,7 @@ class ChuniParadise(ChuniBase): super().__init__(core_cfg, game_cfg) self.version = ChuniConstants.VER_CHUNITHM_PARADISE - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: - ret = super().handle_get_game_setting_api_request(data) + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + ret = await super().handle_get_game_setting_api_request(data) ret["gameSetting"]["dataVersion"] = "1.50.00" return ret diff --git a/titles/chuni/plus.py b/titles/chuni/plus.py index 62d9e0d..094be06 100644 --- a/titles/chuni/plus.py +++ b/titles/chuni/plus.py @@ -11,7 +11,7 @@ class ChuniPlus(ChuniBase): super().__init__(core_cfg, game_cfg) self.version = ChuniConstants.VER_CHUNITHM_PLUS - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: - ret = super().handle_get_game_setting_api_request(data) + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + ret = await super().handle_get_game_setting_api_request(data) ret["gameSetting"]["dataVersion"] = "1.05.00" return ret diff --git a/titles/chuni/star.py b/titles/chuni/star.py index 4c071e8..247934f 100644 --- a/titles/chuni/star.py +++ b/titles/chuni/star.py @@ -11,7 +11,7 @@ class ChuniStar(ChuniBase): super().__init__(core_cfg, game_cfg) self.version = ChuniConstants.VER_CHUNITHM_STAR - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: - ret = super().handle_get_game_setting_api_request(data) + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + ret = await super().handle_get_game_setting_api_request(data) ret["gameSetting"]["dataVersion"] = "1.20.00" return ret diff --git a/titles/chuni/starplus.py b/titles/chuni/starplus.py index 8c24cc8..616c3c6 100644 --- a/titles/chuni/starplus.py +++ b/titles/chuni/starplus.py @@ -11,7 +11,7 @@ class ChuniStarPlus(ChuniBase): super().__init__(core_cfg, game_cfg) self.version = ChuniConstants.VER_CHUNITHM_STAR_PLUS - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: - ret = super().handle_get_game_setting_api_request(data) + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + ret = await super().handle_get_game_setting_api_request(data) ret["gameSetting"]["dataVersion"] = "1.25.00" return ret diff --git a/titles/chuni/sun.py b/titles/chuni/sun.py index bfefd97..94dc1eb 100644 --- a/titles/chuni/sun.py +++ b/titles/chuni/sun.py @@ -11,8 +11,8 @@ class ChuniSun(ChuniNewPlus): super().__init__(core_cfg, game_cfg) self.version = ChuniConstants.VER_CHUNITHM_SUN - def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: - user_data = super().handle_cm_get_user_preview_api_request(data) + async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: + user_data = await super().handle_cm_get_user_preview_api_request(data) # hardcode lastDataVersion for CardMaker 1.35 A032 user_data["lastDataVersion"] = "2.10.00" diff --git a/titles/chuni/sunplus.py b/titles/chuni/sunplus.py index fc86314..1f3f271 100644 --- a/titles/chuni/sunplus.py +++ b/titles/chuni/sunplus.py @@ -11,8 +11,8 @@ class ChuniSunPlus(ChuniSun): super().__init__(core_cfg, game_cfg) self.version = ChuniConstants.VER_CHUNITHM_SUN_PLUS - def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: - user_data = super().handle_cm_get_user_preview_api_request(data) + async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: + user_data = await super().handle_cm_get_user_preview_api_request(data) # I don't know if lastDataVersion is going to matter, I don't think CardMaker 1.35 works this far up user_data["lastDataVersion"] = "2.15.00" diff --git a/titles/cm/base.py b/titles/cm/base.py index b911983..e4fd1cb 100644 --- a/titles/cm/base.py +++ b/titles/cm/base.py @@ -29,11 +29,11 @@ class CardMakerBase: def _parse_int_ver(version: str) -> str: return version.replace(".", "")[:3] - def handle_get_game_connect_api_request(self, data: Dict) -> Dict: + async def handle_get_game_connect_api_request(self, data: Dict) -> Dict: if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80: - uri = f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}" + uri = f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}" else: - uri = f"http://{self.core_cfg.title.hostname}" + uri = f"http://{self.core_cfg.server.hostname}" # grab the dict with all games version numbers from user config games_ver = self.game_cfg.version.version(self.version) @@ -62,7 +62,7 @@ class CardMakerBase: ], } - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: # if reboot start/end time is not defined use the default behavior of being a few hours ago if self.core_cfg.title.reboot_start_time == "" or self.core_cfg.title.reboot_end_time == "": reboot_start = datetime.strftime( @@ -110,11 +110,11 @@ class CardMakerBase: "isAou": False, } - def handle_get_client_bookkeeping_api_request(self, data: Dict) -> Dict: + async def handle_get_client_bookkeeping_api_request(self, data: Dict) -> Dict: return {"placeId": data["placeId"], "length": 0, "clientBookkeepingList": []} - def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict: + async def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict: return {"returnCode": 1, "apiName": "UpsertClientSettingApi"} - def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict: + async def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict: return {"returnCode": 1, "apiName": "UpsertClientBookkeepingApi"} diff --git a/titles/cm/cm135.py b/titles/cm/cm135.py index e134974..5bc5460 100644 --- a/titles/cm/cm135.py +++ b/titles/cm/cm135.py @@ -12,7 +12,7 @@ class CardMaker135(CardMakerBase): super().__init__(core_cfg, game_cfg) self.version = CardMakerConstants.VER_CARD_MAKER_135 - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: - ret = super().handle_get_game_setting_api_request(data) + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + ret = await super().handle_get_game_setting_api_request(data) ret["gameSetting"]["dataVersion"] = "1.35.00" return ret diff --git a/titles/cm/index.py b/titles/cm/index.py index 489b846..20d1e94 100644 --- a/titles/cm/index.py +++ b/titles/cm/index.py @@ -5,10 +5,11 @@ import string import logging import coloredlogs import zlib - +from starlette.routing import Route +from starlette.responses import Response +from starlette.requests import Request from os import path -from typing import Tuple, List, Dict -from twisted.web.http import Request +from typing import List from logging.handlers import TimedRotatingFileHandler from core.config import CoreConfig @@ -19,7 +20,6 @@ from .const import CardMakerConstants from .base import CardMakerBase from .cm135 import CardMaker135 - class CardMakerServlet(BaseServlet): def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None: super().__init__(core_cfg, cfg_dir) @@ -72,16 +72,15 @@ class CardMakerServlet(BaseServlet): return True - def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]: - return ( - [], - [("render_POST", "/SDED/{version}/{endpoint}", {})] - ) - - def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes: - version = int(matchers['version']) - endpoint = matchers['endpoint'] - req_raw = request.content.getvalue() + def get_routes(self) -> List[Route]: + return [ + Route("/SDED/{version:int}/{endpoint:str}", self.render_POST) + ] + + async def render_POST(self, request: Request) -> bytes: + version: int = request.path_params.get('version') + endpoint: str = request.path_params.get('endpoint') + req_raw = await request.body() internal_ver = 0 client_ip = Utils.get_ip_addr(request) @@ -103,7 +102,7 @@ class CardMakerServlet(BaseServlet): self.logger.error( f"Failed to decompress v{version} {endpoint} request -> {e}" ) - return zlib.compress(b'{"stat": "0"}') + return Response(zlib.compress(b'{"stat": "0"}')) req_data = json.loads(unzip) @@ -114,7 +113,7 @@ class CardMakerServlet(BaseServlet): if not hasattr(self.versions[internal_ver], func_to_find): self.logger.warning(f"Unhandled v{version} request {endpoint}") - return zlib.compress(b'{"returnCode": 1}') + return Response(zlib.compress(b'{"returnCode": 1}')) try: handler = getattr(self.versions[internal_ver], func_to_find) @@ -123,11 +122,11 @@ class CardMakerServlet(BaseServlet): except Exception as e: self.logger.error(f"Error handling v{version} method {endpoint} - {e}") raise - return zlib.compress(b'{"stat": "0"}') + return Response(zlib.compress(b'{"stat": "0"}')) if resp is None: resp = {"returnCode": 1} self.logger.debug(f"Response {resp}") - return zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8")) + return Response(zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))) diff --git a/titles/cxb/base.py b/titles/cxb/base.py index fe583e6..691897d 100644 --- a/titles/cxb/base.py +++ b/titles/cxb/base.py @@ -28,13 +28,13 @@ class CxbBase: return [] - def handle_action_rpreq_request(self, data: Dict) -> Dict: + async def handle_action_rpreq_request(self, data: Dict) -> Dict: return {} - def handle_action_hitreq_request(self, data: Dict) -> Dict: + async def handle_action_hitreq_request(self, data: Dict) -> Dict: return {"data": []} - def handle_auth_usercheck_request(self, data: Dict) -> Dict: + async def handle_auth_usercheck_request(self, data: Dict) -> Dict: profile = self.data.profile.get_profile_index( 0, data["usercheck"]["authid"], self.version ) @@ -45,11 +45,11 @@ class CxbBase: self.logger.info(f"No profile for aime id {data['usercheck']['authid']}") return {"exist": "false", "logout": "true"} - def handle_auth_entry_request(self, data: Dict) -> Dict: + async def handle_auth_entry_request(self, data: Dict) -> Dict: self.logger.info(f"New profile for {data['entry']['authid']}") return {"token": data["entry"]["authid"], "uid": data["entry"]["authid"]} - def handle_auth_login_request(self, data: Dict) -> Dict: + async def handle_auth_login_request(self, data: Dict) -> Dict: profile = self.data.profile.get_profile_index( 0, data["login"]["authid"], self.version ) @@ -198,7 +198,7 @@ class CxbBase: ).decode("utf-8") ) - def handle_action_loadrange_request(self, data: Dict) -> Dict: + async def handle_action_loadrange_request(self, data: Dict) -> Dict: range_start = data["loadrange"]["range"][0] range_end = data["loadrange"]["range"][1] uid = data["loadrange"]["uid"] @@ -282,7 +282,7 @@ class CxbBase: return {"index": index, "data": data1, "version": versionindex} - def handle_action_saveindex_request(self, data: Dict) -> Dict: + async def handle_action_saveindex_request(self, data: Dict) -> Dict: save_data = data["saveindex"] try: @@ -443,7 +443,7 @@ class CxbBase: i += 1 return {} - def handle_action_sprankreq_request(self, data: Dict) -> Dict: + async def handle_action_sprankreq_request(self, data: Dict) -> Dict: uid = data["sprankreq"]["uid"] self.logger.info(f"Get best rankings for {uid}") p = self.data.score.get_best_rankings(uid) @@ -475,16 +475,16 @@ class CxbBase: "rankx": [1, 1, 1], } - def handle_action_getadv_request(self, data: Dict) -> Dict: + async def handle_action_getadv_request(self, data: Dict) -> Dict: return {"data": [{"r": "1", "i": "100300", "c": "20"}]} - def handle_action_getmsg_request(self, data: Dict) -> Dict: + async def handle_action_getmsg_request(self, data: Dict) -> Dict: return {"msgs": []} - def handle_auth_logout_request(self, data: Dict) -> Dict: + async def handle_auth_logout_request(self, data: Dict) -> Dict: return {"auth": True} - def handle_action_rankreg_request(self, data: Dict) -> Dict: + async def handle_action_rankreg_request(self, data: Dict) -> Dict: uid = data["rankreg"]["uid"] self.logger.info(f"Put {len(data['rankreg']['data'])} rankings for {uid}") @@ -527,7 +527,7 @@ class CxbBase: ) return {} - def handle_action_addenergy_request(self, data: Dict) -> Dict: + async def handle_action_addenergy_request(self, data: Dict) -> Dict: uid = data["addenergy"]["uid"] self.logger.info(f"Add energy to user {uid}") profile = self.data.profile.get_profile_index(0, uid, self.version) @@ -570,10 +570,10 @@ class CxbBase: ) return array[0] - def handle_action_eventreq_request(self, data: Dict) -> Dict: + async def handle_action_eventreq_request(self, data: Dict) -> Dict: self.logger.info(data) return {"eventreq": ""} - def handle_action_stampreq_request(self, data: Dict) -> Dict: + async def handle_action_stampreq_request(self, data: Dict) -> Dict: self.logger.info(data) return {"stampreq": ""} \ No newline at end of file diff --git a/titles/cxb/index.py b/titles/cxb/index.py index 04bbc92..3f719ff 100644 --- a/titles/cxb/index.py +++ b/titles/cxb/index.py @@ -1,4 +1,6 @@ -from twisted.web.http import Request +from starlette.requests import Request +from starlette.routing import Route +from starlette.responses import Response, PlainTextResponse, JSONResponse import traceback import sys import yaml @@ -62,6 +64,14 @@ class CxbServlet(BaseServlet): CxbRevSunriseS2(core_cfg, self.game_cfg), ] + def get_routes(self) -> List[Route]: + return [ + Route("/data", self.handle_data, methods=['POST']), + Route("/action", self.handle_action, methods=['POST']), + Route("/v2/action", self.handle_action, methods=['POST']), + Route("/auth", self.handle_auth, methods=['POST']), + ] + @classmethod def is_game_enabled(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> bool: game_cfg = CxbConfig() @@ -88,26 +98,12 @@ class CxbServlet(BaseServlet): t_port = f":{title_port_int}" if title_port_int and not self.core_cfg.server.is_using_proxy else "" return ( - f"{proto}://{self.core_cfg.title.hostname}{t_port}", + f"{proto}://{self.core_cfg.server.hostname}{t_port}", "", ) - def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]: - return ( - [], - [ - ("handle_data", "/data", {}), - ("handle_action", "/action", {}), - ("handle_action", "/v2/action", {}), - ("handle_auth", "/auth", {}), - ] - ) - - def preprocess(self, req: Request) -> Dict: - try: - req_bytes = req.content.getvalue() - except: - req_bytes = req.content.read() # Can we just use this one? + async def preprocess(self, req: Request) -> Dict: + req_bytes = await req.body() try: req_json: Dict = json.loads(req_bytes) @@ -126,8 +122,8 @@ class CxbServlet(BaseServlet): return req_json - def handle_data(self, request: Request, game_code: str, matchers: Dict) -> bytes: - req_json = self.preprocess(request) + async def handle_data(self, request: Request) -> bytes: + req_json = await self.preprocess(request) func_to_find = "handle_data_" version_string = "Base" internal_ver = 0 @@ -135,7 +131,7 @@ class CxbServlet(BaseServlet): if req_json == {}: self.logger.warning(f"Empty json request to /data") - return b"" + return Response() subcmd = list(req_json.keys())[0] if subcmd == "dldate": @@ -145,14 +141,14 @@ class CxbServlet(BaseServlet): or "filetype" not in req_json["dldate"] ): self.logger.warning(f"Malformed dldate request: {req_json}") - return b"" + return Response() filetype = req_json["dldate"]["filetype"] filetype_split = filetype.split("/") if len(filetype_split) < 2 or not filetype_split[0].isnumeric(): self.logger.warning(f"Malformed dldate request: {req_json}") - return b"" + return Response() version = int(filetype_split[0]) filename = filetype_split[len(filetype_split) - 1] @@ -184,7 +180,7 @@ class CxbServlet(BaseServlet): if not hasattr(self.versions[internal_ver], func_to_find): self.logger.warn(f"{version_string} has no handler for filetype {filetype} / {func_to_find}") - return({"data":""}) + return JSONResponse({"data":""}) self.logger.info(f"{version_string} request for filetype {filetype}") self.logger.debug(req_json) @@ -192,7 +188,7 @@ class CxbServlet(BaseServlet): handler = getattr(self.versions[internal_ver], func_to_find) try: - resp = handler(req_json) + resp = await handler(req_json) except Exception as e: self.logger.error(f"Error handling request for file {filetype} - {e}") @@ -201,19 +197,19 @@ class CxbServlet(BaseServlet): traceback.print_exception(tp, val, tb, limit=1) with open("{0}/{1}.log".format(self.core_cfg.server.log_dir, "cxb"), "a") as f: traceback.print_exception(tp, val, tb, limit=1, file=f) - return "" + return Response() self.logger.debug(f"{version_string} Response {resp}") - return json.dumps(resp, ensure_ascii=False).encode("utf-8") + return JSONResponse(resp, ensure_ascii=False) - def handle_action(self, request: Request, game_code: str, matchers: Dict) -> bytes: - req_json = self.preprocess(request) + async def handle_action(self, request: Request) -> bytes: + req_json = await self.preprocess(request) subcmd = list(req_json.keys())[0] func_to_find = f"handle_action_{subcmd}_request" if not hasattr(self.versions[0], func_to_find): self.logger.warn(f"No handler for action {subcmd} request") - return "" + return Response() self.logger.info(f"Action {subcmd} Request") self.logger.debug(req_json) @@ -221,7 +217,7 @@ class CxbServlet(BaseServlet): handler = getattr(self.versions[0], func_to_find) try: - resp = handler(req_json) + resp = await handler(req_json) except Exception as e: self.logger.error(f"Error handling action {subcmd} request - {e}") @@ -230,19 +226,19 @@ class CxbServlet(BaseServlet): traceback.print_exception(tp, val, tb, limit=1) with open("{0}/{1}.log".format(self.core_cfg.server.log_dir, "cxb"), "a") as f: traceback.print_exception(tp, val, tb, limit=1, file=f) - return "" + return Response() self.logger.debug(f"Response {resp}") - return json.dumps(resp, ensure_ascii=False).encode("utf-8") + return JSONResponse(resp) - def handle_auth(self, request: Request, game_code: str, matchers: Dict) -> bytes: - req_json = self.preprocess(request) + async def handle_auth(self, request: Request) -> bytes: + req_json = await self.preprocess(request) subcmd = list(req_json.keys())[0] func_to_find = f"handle_auth_{subcmd}_request" if not hasattr(self.versions[0], func_to_find): self.logger.warn(f"No handler for auth {subcmd} request") - return "" + return Response() self.logger.info(f"Action {subcmd} Request") self.logger.debug(req_json) @@ -250,7 +246,7 @@ class CxbServlet(BaseServlet): handler = getattr(self.versions[0], func_to_find) try: - resp = handler(req_json) + resp = await handler(req_json) except Exception as e: self.logger.error(f"Error handling auth {subcmd} request - {e}") @@ -259,7 +255,7 @@ class CxbServlet(BaseServlet): traceback.print_exception(tp, val, tb, limit=1) with open("{0}/{1}.log".format(self.core_cfg.server.log_dir, "cxb"), "a") as f: traceback.print_exception(tp, val, tb, limit=1, file=f) - return "" + return Response() self.logger.debug(f"Response {resp}") - return json.dumps(resp, ensure_ascii=False).encode("utf-8") + return JSONResponse(resp) diff --git a/titles/cxb/rev.py b/titles/cxb/rev.py index e311a1e..1a7c5ac 100644 --- a/titles/cxb/rev.py +++ b/titles/cxb/rev.py @@ -17,10 +17,10 @@ class CxbRev(CxbBase): super().__init__(cfg, game_cfg) self.version = CxbConstants.VER_CROSSBEATS_REV - def handle_data_path_list_request(self, data: Dict) -> Dict: + async def handle_data_path_list_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_putlog_request(self, data: Dict) -> Dict: + async def handle_data_putlog_request(self, data: Dict) -> Dict: if data["putlog"]["type"] == "ResultLog": score_data = json.loads(data["putlog"]["data"]) userid = score_data["usid"] @@ -45,7 +45,7 @@ class CxbRev(CxbBase): return {"data": True} @cached(lifetime=86400) - def handle_data_music_list_request(self, data: Dict) -> Dict: + async def handle_data_music_list_request(self, data: Dict) -> Dict: ret_str = "" with open(r"titles/cxb/data/rss/MusicArchiveList.csv") as music: lines = music.readlines() @@ -56,7 +56,7 @@ class CxbRev(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_item_list_icon_request(self, data: Dict) -> Dict: + async def handle_data_item_list_icon_request(self, data: Dict) -> Dict: ret_str = "\r\n#ItemListIcon\r\n" with open( r"titles/cxb/data/rss/Item/ItemArchiveList_Icon.csv", encoding="utf-8" @@ -67,7 +67,7 @@ class CxbRev(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_item_list_skin_notes_request(self, data: Dict) -> Dict: + async def handle_data_item_list_skin_notes_request(self, data: Dict) -> Dict: ret_str = "\r\n#ItemListSkinNotes\r\n" with open( r"titles/cxb/data/rss/Item/ItemArchiveList_SkinNotes.csv", encoding="utf-8" @@ -78,7 +78,7 @@ class CxbRev(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_item_list_skin_effect_request(self, data: Dict) -> Dict: + async def handle_data_item_list_skin_effect_request(self, data: Dict) -> Dict: ret_str = "\r\n#ItemListSkinEffect\r\n" with open( r"titles/cxb/data/rss/Item/ItemArchiveList_SkinEffect.csv", encoding="utf-8" @@ -89,7 +89,7 @@ class CxbRev(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_item_list_skin_bg_request(self, data: Dict) -> Dict: + async def handle_data_item_list_skin_bg_request(self, data: Dict) -> Dict: ret_str = "\r\n#ItemListSkinBg\r\n" with open( r"titles/cxb/data/rss/Item/ItemArchiveList_SkinBg.csv", encoding="utf-8" @@ -100,7 +100,7 @@ class CxbRev(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_item_list_title_request(self, data: Dict) -> Dict: + async def handle_data_item_list_title_request(self, data: Dict) -> Dict: ret_str = "\r\n#ItemListTitle\r\n" with open( r"titles/cxb/data/rss/Item/ItemList_Title.csv", encoding="shift-jis" @@ -111,7 +111,7 @@ class CxbRev(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_shop_list_music_request(self, data: Dict) -> Dict: + async def handle_data_shop_list_music_request(self, data: Dict) -> Dict: ret_str = "\r\n#ShopListMusic\r\n" with open( r"titles/cxb/data/rss/Shop/ShopList_Music.csv", encoding="shift-jis" @@ -122,7 +122,7 @@ class CxbRev(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_shop_list_icon_request(self, data: Dict) -> Dict: + async def handle_data_shop_list_icon_request(self, data: Dict) -> Dict: ret_str = "\r\n#ShopListIcon\r\n" with open( r"titles/cxb/data/rss/Shop/ShopList_Icon.csv", encoding="shift-jis" @@ -133,7 +133,7 @@ class CxbRev(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_shop_list_title_request(self, data: Dict) -> Dict: + async def handle_data_shop_list_title_request(self, data: Dict) -> Dict: ret_str = "\r\n#ShopListTitle\r\n" with open( r"titles/cxb/data/rss/Shop/ShopList_Title.csv", encoding="shift-jis" @@ -143,17 +143,17 @@ class CxbRev(CxbBase): ret_str += f"{line[:-1]}\r\n" return {"data": ret_str} - def handle_data_shop_list_skin_hud_request(self, data: Dict) -> Dict: + async def handle_data_shop_list_skin_hud_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_shop_list_skin_arrow_request(self, data: Dict) -> Dict: + async def handle_data_shop_list_skin_arrow_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_shop_list_skin_hit_request(self, data: Dict) -> Dict: + async def handle_data_shop_list_skin_hit_request(self, data: Dict) -> Dict: return {"data": ""} @cached(lifetime=86400) - def handle_data_shop_list_sale_request(self, data: Dict) -> Dict: + async def handle_data_shop_list_sale_request(self, data: Dict) -> Dict: ret_str = "\r\n#ShopListSale\r\n" with open( r"titles/cxb/data/rss/Shop/ShopList_Sale.csv", encoding="shift-jis" @@ -163,11 +163,11 @@ class CxbRev(CxbBase): ret_str += f"{line[:-1]}\r\n" return {"data": ret_str} - def handle_data_extra_stage_list_request(self, data: Dict) -> Dict: + async def handle_data_extra_stage_list_request(self, data: Dict) -> Dict: return {"data": ""} @cached(lifetime=86400) - def handle_data_exxxxx_request(self, data: Dict) -> Dict: + async def handle_data_exxxxx_request(self, data: Dict) -> Dict: extra_num = int(data["dldate"]["filetype"][-4:]) ret_str = "" with open( @@ -178,14 +178,14 @@ class CxbRev(CxbBase): ret_str += f"{line[:-1]}\r\n" return {"data": ret_str} - def handle_data_bonus_list10100_request(self, data: Dict) -> Dict: + async def handle_data_bonus_list10100_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_free_coupon_request(self, data: Dict) -> Dict: + async def handle_data_free_coupon_request(self, data: Dict) -> Dict: return {"data": ""} @cached(lifetime=86400) - def handle_data_news_list_request(self, data: Dict) -> Dict: + async def handle_data_news_list_request(self, data: Dict) -> Dict: ret_str = "" with open(r"titles/cxb/data/rss/NewsList.csv", encoding="UTF-8") as news: lines = news.readlines() @@ -193,11 +193,11 @@ class CxbRev(CxbBase): ret_str += f"{line[:-1]}\r\n" return {"data": ret_str} - def handle_data_tips_request(self, data: Dict) -> Dict: + async def handle_data_tips_request(self, data: Dict) -> Dict: return {"data": ""} @cached(lifetime=86400) - def handle_data_license_request(self, data: Dict) -> Dict: + async def handle_data_license_request(self, data: Dict) -> Dict: ret_str = "" with open(r"titles/cxb/data/rss/License_Offline.csv", encoding="UTF-8") as lic: lines = lic.readlines() @@ -206,7 +206,7 @@ class CxbRev(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_course_list_request(self, data: Dict) -> Dict: + async def handle_data_course_list_request(self, data: Dict) -> Dict: ret_str = "" with open( r"titles/cxb/data/rss/Course/CourseList.csv", encoding="UTF-8" @@ -217,7 +217,7 @@ class CxbRev(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_csxxxx_request(self, data: Dict) -> Dict: + async def handle_data_csxxxx_request(self, data: Dict) -> Dict: # Removed the CSVs since the format isnt quite right extra_num = int(data["dldate"]["filetype"][-4:]) ret_str = "" @@ -230,7 +230,7 @@ class CxbRev(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_mission_list_request(self, data: Dict) -> Dict: + async def handle_data_mission_list_request(self, data: Dict) -> Dict: ret_str = "" with open( r"titles/cxb/data/rss/MissionList.csv", encoding="shift-jis" @@ -240,14 +240,14 @@ class CxbRev(CxbBase): ret_str += f"{line[:-1]}\r\n" return {"data": ret_str} - def handle_data_mission_bonus_request(self, data: Dict) -> Dict: + async def handle_data_mission_bonus_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_unlimited_mission_request(self, data: Dict) -> Dict: + async def handle_data_unlimited_mission_request(self, data: Dict) -> Dict: return {"data": ""} @cached(lifetime=86400) - def handle_data_event_list_request(self, data: Dict) -> Dict: + async def handle_data_event_list_request(self, data: Dict) -> Dict: ret_str = "" with open( r"titles/cxb/data/rss/Event/EventArchiveList.csv", encoding="shift-jis" @@ -257,39 +257,39 @@ class CxbRev(CxbBase): ret_str += f"{line[:-1]}\r\n" return {"data": ret_str} - def handle_data_event_music_list_request(self, data: Dict) -> Dict: + async def handle_data_event_music_list_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_event_mission_list_request(self, data: Dict) -> Dict: + async def handle_data_event_mission_list_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_event_achievement_single_high_score_list_request( + async def handle_data_event_achievement_single_high_score_list_request( self, data: Dict ) -> Dict: return {"data": ""} - def handle_data_event_achievement_single_accumulation_request( + async def handle_data_event_achievement_single_accumulation_request( self, data: Dict ) -> Dict: return {"data": ""} - def handle_data_event_ranking_high_score_list_request(self, data: Dict) -> Dict: + async def handle_data_event_ranking_high_score_list_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_event_ranking_accumulation_list_request(self, data: Dict) -> Dict: + async def handle_data_event_ranking_accumulation_list_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_event_ranking_stamp_list_request(self, data: Dict) -> Dict: + async def handle_data_event_ranking_stamp_list_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_event_ranking_store_list_request(self, data: Dict) -> Dict: + async def handle_data_event_ranking_store_list_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_event_ranking_area_list_request(self, data: Dict) -> Dict: + async def handle_data_event_ranking_area_list_request(self, data: Dict) -> Dict: return {"data": ""} @cached(lifetime=86400) - def handle_data_event_stamp_list_request(self, data: Dict) -> Dict: + async def handle_data_event_stamp_list_request(self, data: Dict) -> Dict: ret_str = "" with open( r"titles/cxb/data/rss/Event/EventStampList.csv", encoding="shift-jis" @@ -299,8 +299,8 @@ class CxbRev(CxbBase): ret_str += f"{line[:-1]}\r\n" return {"data": ret_str} - def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict: + async def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict: return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"} - def handle_data_server_state_request(self, data: Dict) -> Dict: + async def handle_data_server_state_request(self, data: Dict) -> Dict: return {"data": True} diff --git a/titles/cxb/rss1.py b/titles/cxb/rss1.py index fe43e42..cce0315 100644 --- a/titles/cxb/rss1.py +++ b/titles/cxb/rss1.py @@ -17,11 +17,11 @@ class CxbRevSunriseS1(CxbBase): super().__init__(cfg, game_cfg) self.version = CxbConstants.VER_CROSSBEATS_REV_SUNRISE_S1 - def handle_data_path_list_request(self, data: Dict) -> Dict: + async def handle_data_path_list_request(self, data: Dict) -> Dict: return {"data": ""} @cached(lifetime=86400) - def handle_data_music_list_request(self, data: Dict) -> Dict: + async def handle_data_music_list_request(self, data: Dict) -> Dict: ret_str = "" with open(r"titles/cxb/data/rss1/MusicArchiveList.csv") as music: lines = music.readlines() @@ -32,7 +32,7 @@ class CxbRevSunriseS1(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_item_list_detail_request(self, data: Dict) -> Dict: + async def handle_data_item_list_detail_request(self, data: Dict) -> Dict: # ItemListIcon load ret_str = "#ItemListIcon\r\n" with open( @@ -54,7 +54,7 @@ class CxbRevSunriseS1(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_shop_list_detail_request(self, data: Dict) -> Dict: + async def handle_data_shop_list_detail_request(self, data: Dict) -> Dict: # ShopListIcon load ret_str = "#ShopListIcon\r\n" with open( @@ -119,26 +119,26 @@ class CxbRevSunriseS1(CxbBase): ret_str += f"{line[:-1]}\r\n" return {"data": ret_str} - def handle_data_extra_stage_list_request(self, data: Dict) -> Dict: + async def handle_data_extra_stage_list_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_exxxxx_request(self, data: Dict) -> Dict: + async def handle_data_exxxxx_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_one_more_extra_list_request(self, data: Dict) -> Dict: + async def handle_data_one_more_extra_list_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_bonus_list10100_request(self, data: Dict) -> Dict: + async def handle_data_bonus_list10100_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_oexxxx_request(self, data: Dict) -> Dict: + async def handle_data_oexxxx_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_free_coupon_request(self, data: Dict) -> Dict: + async def handle_data_free_coupon_request(self, data: Dict) -> Dict: return {"data": ""} @cached(lifetime=86400) - def handle_data_news_list_request(self, data: Dict) -> Dict: + async def handle_data_news_list_request(self, data: Dict) -> Dict: ret_str = "" with open(r"titles/cxb/data/rss1/NewsList.csv", encoding="UTF-8") as news: lines = news.readlines() @@ -146,14 +146,14 @@ class CxbRevSunriseS1(CxbBase): ret_str += f"{line[:-1]}\r\n" return {"data": ret_str} - def handle_data_tips_request(self, data: Dict) -> Dict: + async def handle_data_tips_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_release_info_list_request(self, data: Dict) -> Dict: + async def handle_data_release_info_list_request(self, data: Dict) -> Dict: return {"data": ""} @cached(lifetime=86400) - def handle_data_random_music_list_request(self, data: Dict) -> Dict: + async def handle_data_random_music_list_request(self, data: Dict) -> Dict: ret_str = "" with open(r"titles/cxb/data/rss1/MusicArchiveList.csv") as music: lines = music.readlines() @@ -167,7 +167,7 @@ class CxbRevSunriseS1(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_license_request(self, data: Dict) -> Dict: + async def handle_data_license_request(self, data: Dict) -> Dict: ret_str = "" with open(r"titles/cxb/data/rss1/License.csv", encoding="UTF-8") as licenses: lines = licenses.readlines() @@ -176,7 +176,7 @@ class CxbRevSunriseS1(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_course_list_request(self, data: Dict) -> Dict: + async def handle_data_course_list_request(self, data: Dict) -> Dict: ret_str = "" with open( r"titles/cxb/data/rss1/Course/CourseList.csv", encoding="UTF-8" @@ -187,7 +187,7 @@ class CxbRevSunriseS1(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_csxxxx_request(self, data: Dict) -> Dict: + async def handle_data_csxxxx_request(self, data: Dict) -> Dict: extra_num = int(data["dldate"]["filetype"][-4:]) ret_str = "" with open( @@ -198,16 +198,16 @@ class CxbRevSunriseS1(CxbBase): ret_str += f"{line[:-1]}\r\n" return {"data": ret_str} - def handle_data_mission_list_request(self, data: Dict) -> Dict: + async def handle_data_mission_list_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_mission_bonus_request(self, data: Dict) -> Dict: + async def handle_data_mission_bonus_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_unlimited_mission_request(self, data: Dict) -> Dict: + async def handle_data_unlimited_mission_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_partner_list_request(self, data: Dict) -> Dict: + async def handle_data_partner_list_request(self, data: Dict) -> Dict: ret_str = "" # Lord forgive me for the sins I am about to commit for i in range(0, 10): @@ -226,7 +226,7 @@ class CxbRevSunriseS1(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_partnerxxxx_request(self, data: Dict) -> Dict: + async def handle_data_partnerxxxx_request(self, data: Dict) -> Dict: partner_num = int(data["dldate"]["filetype"][-4:]) ret_str = f"{partner_num},,{partner_num},1,10000,\r\n" with open(r"titles/cxb/data/rss1/Partner0000.csv") as partner: @@ -235,13 +235,13 @@ class CxbRevSunriseS1(CxbBase): ret_str += f"{line[:-1]}\r\n" return {"data": ret_str} - def handle_data_server_state_request(self, data: Dict) -> Dict: + async def handle_data_server_state_request(self, data: Dict) -> Dict: return {"data": True} - def handle_data_settings_request(self, data: Dict) -> Dict: + async def handle_data_settings_request(self, data: Dict) -> Dict: return {"data": "2,\r\n"} - def handle_data_story_list_request(self, data: Dict) -> Dict: + async def handle_data_story_list_request(self, data: Dict) -> Dict: # story id, story name, game version, start time, end time, course arc, unlock flag, song mcode for menu ret_str = "\r\n" ret_str += ( @@ -253,23 +253,23 @@ class CxbRevSunriseS1(CxbBase): ret_str += f"st0002,REMNANT,10104,1502127790,4096483201,Cs1000,-1,overcl,\r\n" return {"data": ret_str} - def handle_data_stxxxx_request(self, data: Dict) -> Dict: + async def handle_data_stxxxx_request(self, data: Dict) -> Dict: story_num = int(data["dldate"]["filetype"][-4:]) ret_str = "" for i in range(1, 11): ret_str += f"{i},st000{story_num}_{i-1},,,,,,,,,,,,,,,,1,,-1,1,\r\n" return {"data": ret_str} - def handle_data_event_stamp_list_request(self, data: Dict) -> Dict: + async def handle_data_event_stamp_list_request(self, data: Dict) -> Dict: return {"data": "Cs1032,1,1,1,1,1,1,1,1,1,1,\r\n"} - def handle_data_premium_list_request(self, data: Dict) -> Dict: + async def handle_data_premium_list_request(self, data: Dict) -> Dict: return {"data": "1,,,,10,,,,,99,,,,,,,,,100,,\r\n"} - def handle_data_event_list_request(self, data: Dict) -> Dict: + async def handle_data_event_list_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_event_detail_list_request(self, data: Dict) -> Dict: + async def handle_data_event_detail_list_request(self, data: Dict) -> Dict: event_id = data["dldate"]["filetype"].split("/")[2] if "EventStampMapListCs1002" in event_id: return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"} @@ -278,7 +278,7 @@ class CxbRevSunriseS1(CxbBase): else: return {"data": ""} - def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict: + async def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict: event_id = data["dldate"]["filetype"].split("/")[2] if "EventStampMapListCs1002" in event_id: return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"} diff --git a/titles/cxb/rss2.py b/titles/cxb/rss2.py index b15deda..070d88b 100644 --- a/titles/cxb/rss2.py +++ b/titles/cxb/rss2.py @@ -17,11 +17,11 @@ class CxbRevSunriseS2(CxbBase): super().__init__(cfg, game_cfg) self.version = CxbConstants.VER_CROSSBEATS_REV_SUNRISE_S2_OMNI - def handle_data_path_list_request(self, data: Dict) -> Dict: + async def handle_data_path_list_request(self, data: Dict) -> Dict: return {"data": ""} @cached(lifetime=86400) - def handle_data_music_list_request(self, data: Dict) -> Dict: + async def handle_data_music_list_request(self, data: Dict) -> Dict: version = data["dldate"]["filetype"].split("/")[0] ret_str = "" @@ -41,7 +41,7 @@ class CxbRevSunriseS2(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_item_list_detail_request(self, data: Dict) -> Dict: + async def handle_data_item_list_detail_request(self, data: Dict) -> Dict: # ItemListIcon load ret_str = "#ItemListIcon\r\n" with open( @@ -63,7 +63,7 @@ class CxbRevSunriseS2(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_shop_list_detail_request(self, data: Dict) -> Dict: + async def handle_data_shop_list_detail_request(self, data: Dict) -> Dict: # ShopListIcon load ret_str = "#ShopListIcon\r\n" with open( @@ -128,7 +128,7 @@ class CxbRevSunriseS2(CxbBase): ret_str += f"{line[:-1]}\r\n" return {"data": ret_str} - def handle_data_extra_stage_list_request(self, data: Dict) -> Dict: + async def handle_data_extra_stage_list_request(self, data: Dict) -> Dict: ret_str="" with open(r"titles/cxb/data/rss2/ExtraStageList.csv") as extra: lines = extra.readlines() @@ -136,19 +136,19 @@ class CxbRevSunriseS2(CxbBase): ret_str += f"{line[:-1]}\r\n" return({"data":ret_str}) - def handle_data_exxxxx_request(self, data: Dict) -> Dict: + async def handle_data_exxxxx_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_one_more_extra_list_request(self, data: Dict) -> Dict: + async def handle_data_one_more_extra_list_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_bonus_list10100_request(self, data: Dict) -> Dict: + async def handle_data_bonus_list10100_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_oexxxx_request(self, data: Dict) -> Dict: + async def handle_data_oexxxx_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_free_coupon_request(self, data: Dict) -> Dict: + async def handle_data_free_coupon_request(self, data: Dict) -> Dict: ret_str="" with open(r"titles/cxb/data/rss2/FreeCoupon.csv") as coupon: lines = coupon.readlines() @@ -157,7 +157,7 @@ class CxbRevSunriseS2(CxbBase): return({"data":ret_str}) @cached(lifetime=86400) - def handle_data_news_list_request(self, data: Dict) -> Dict: + async def handle_data_news_list_request(self, data: Dict) -> Dict: ret_str = "" with open(r"titles/cxb/data/rss2/NewsList.csv", encoding="UTF-8") as news: lines = news.readlines() @@ -165,14 +165,14 @@ class CxbRevSunriseS2(CxbBase): ret_str += f"{line[:-1]}\r\n" return {"data": ret_str} - def handle_data_tips_request(self, data: Dict) -> Dict: + async def handle_data_tips_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_release_info_list_request(self, data: Dict) -> Dict: + async def handle_data_release_info_list_request(self, data: Dict) -> Dict: return {"data": ""} @cached(lifetime=86400) - def handle_data_random_music_list_request(self, data: Dict) -> Dict: + async def handle_data_random_music_list_request(self, data: Dict) -> Dict: ret_str = "" with open(r"titles/cxb/data/rss2/MusicArchiveList.csv") as music: lines = music.readlines() @@ -186,7 +186,7 @@ class CxbRevSunriseS2(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_license_request(self, data: Dict) -> Dict: + async def handle_data_license_request(self, data: Dict) -> Dict: ret_str = "" with open(r"titles/cxb/data/rss2/License.csv", encoding="UTF-8") as licenses: lines = licenses.readlines() @@ -195,7 +195,7 @@ class CxbRevSunriseS2(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_course_list_request(self, data: Dict) -> Dict: + async def handle_data_course_list_request(self, data: Dict) -> Dict: ret_str = "" with open( r"titles/cxb/data/rss2/Course/CourseList.csv", encoding="UTF-8" @@ -206,7 +206,7 @@ class CxbRevSunriseS2(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_csxxxx_request(self, data: Dict) -> Dict: + async def handle_data_csxxxx_request(self, data: Dict) -> Dict: extra_num = int(data["dldate"]["filetype"][-4:]) ret_str = "" with open( @@ -217,16 +217,16 @@ class CxbRevSunriseS2(CxbBase): ret_str += f"{line[:-1]}\r\n" return {"data": ret_str} - def handle_data_mission_list_request(self, data: Dict) -> Dict: + async def handle_data_mission_list_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_mission_bonus_request(self, data: Dict) -> Dict: + async def handle_data_mission_bonus_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_unlimited_mission_request(self, data: Dict) -> Dict: + async def handle_data_unlimited_mission_request(self, data: Dict) -> Dict: return {"data": ""} - def handle_data_partner_list_request(self, data: Dict) -> Dict: + async def handle_data_partner_list_request(self, data: Dict) -> Dict: ret_str = "" # Lord forgive me for the sins I am about to commit for i in range(0, 10): @@ -245,7 +245,7 @@ class CxbRevSunriseS2(CxbBase): return {"data": ret_str} @cached(lifetime=86400) - def handle_data_partnerxxxx_request(self, data: Dict) -> Dict: + async def handle_data_partnerxxxx_request(self, data: Dict) -> Dict: partner_num = int(data["dldate"]["filetype"][-4:]) ret_str = f"{partner_num},,{partner_num},1,10000,\r\n" with open(r"titles/cxb/data/rss2/Partner0000.csv") as partner: @@ -254,13 +254,13 @@ class CxbRevSunriseS2(CxbBase): ret_str += f"{line[:-1]}\r\n" return {"data": ret_str} - def handle_data_server_state_request(self, data: Dict) -> Dict: + async def handle_data_server_state_request(self, data: Dict) -> Dict: return {"data": True} - def handle_data_settings_request(self, data: Dict) -> Dict: + async def handle_data_settings_request(self, data: Dict) -> Dict: return {"data": "2,\r\n"} - def handle_data_story_list_request(self, data: Dict) -> Dict: + async def handle_data_story_list_request(self, data: Dict) -> Dict: # story id, story name, game version, start time, end time, course arc, unlock flag, song mcode for menu ret_str = "\r\n" ret_str += ( @@ -272,7 +272,7 @@ class CxbRevSunriseS2(CxbBase): ret_str += f"st0002,REMNANT,10104,1502127790,4096483201,Cs1000,-1,overcl,\r\n" return {"data": ret_str} - def handle_data_stxxxx_request(self, data: Dict) -> Dict: + async def handle_data_stxxxx_request(self, data: Dict) -> Dict: story_num = int(data["dldate"]["filetype"][-4:]) ret_str = "" # Each stories appears to have 10 pieces based on the wiki but as on how they are set.... no clue @@ -280,18 +280,18 @@ class CxbRevSunriseS2(CxbBase): ret_str += f"{i},st000{story_num}_{i-1},,,,,,,,,,,,,,,,1,,-1,1,\r\n" return {"data": ret_str} - def handle_data_event_stamp_list_request(self, data: Dict) -> Dict: + async def handle_data_event_stamp_list_request(self, data: Dict) -> Dict: return {"data": "Cs1002,1,1,1,1,1,1,1,1,1,1,\r\n"} - def handle_data_premium_list_request(self, data: Dict) -> Dict: + async def handle_data_premium_list_request(self, data: Dict) -> Dict: return {"data": "1,,,,10,,,,,99,,,,,,,,,100,,\r\n"} - def handle_data_event_list_request(self, data: Dict) -> Dict: + async def handle_data_event_list_request(self, data: Dict) -> Dict: return { "data": "Cs4001,0,10000,1601510400,1604188799,1,nv2006,1,\r\nCs4005,0,10000,1609459200,1615766399,1,nv2006,1,\r\n" } - def handle_data_event_detail_list_request(self, data: Dict) -> Dict: + async def handle_data_event_detail_list_request(self, data: Dict) -> Dict: event_id = data["dldate"]["filetype"].split("/")[2] if "Cs4001" in event_id: return { @@ -308,7 +308,7 @@ class CxbRevSunriseS2(CxbBase): else: return {"data": ""} - def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict: + async def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict: event_id = data["dldate"]["filetype"].split("/")[2] if "EventStampMapListCs1002" in event_id: return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"} diff --git a/titles/diva/base.py b/titles/diva/base.py index 6db0dbc..eb7d11e 100644 --- a/titles/diva/base.py +++ b/titles/diva/base.py @@ -1,8 +1,7 @@ import datetime -from typing import Any, List, Dict +from typing import Dict import logging -import json -import urllib +import urllib.parse from threading import Thread from core.config import CoreConfig @@ -24,13 +23,13 @@ class DivaBase: dt = datetime.datetime.now() self.time_lut = urllib.parse.quote(dt.strftime("%Y-%m-%d %H:%M:%S:16.0")) - def handle_test_request(self, data: Dict) -> Dict: + async def handle_test_request(self, data: Dict) -> Dict: return "" - def handle_game_init_request(self, data: Dict) -> Dict: + async def handle_game_init_request(self, data: Dict) -> Dict: return f"" - def handle_attend_request(self, data: Dict) -> Dict: + async def handle_attend_request(self, data: Dict) -> Dict: encoded = "&" params = { "atnd_prm1": "0,1,1,0,0,0,1,0,100,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1", @@ -44,7 +43,7 @@ class DivaBase: return encoded - def handle_ping_request(self, data: Dict) -> Dict: + async def handle_ping_request(self, data: Dict) -> Dict: encoded = "&" params = { "ping_b_msg": f"Welcome to {self.core_cfg.server.name} network!", @@ -89,7 +88,7 @@ class DivaBase: return encoded - def handle_pv_list_request(self, data: Dict) -> Dict: + async def handle_pv_list_request(self, data: Dict) -> Dict: pvlist = "" with open(r"titles/diva/data/PvList0.dat", encoding="utf-8") as shop: lines = shop.readlines() @@ -126,7 +125,7 @@ class DivaBase: return response - def handle_shop_catalog_request(self, data: Dict) -> Dict: + async def handle_shop_catalog_request(self, data: Dict) -> Dict: catalog = "" shopList = self.data.static.get_enabled_shops(self.version) @@ -164,7 +163,7 @@ class DivaBase: return response - def handle_buy_module_request(self, data: Dict) -> Dict: + async def handle_buy_module_request(self, data: Dict) -> Dict: profile = self.data.profile.get_profile(data["pd_id"], self.version) module = self.data.static.get_enabled_shop(self.version, int(data["mdl_id"])) @@ -191,7 +190,7 @@ class DivaBase: return response - def handle_cstmz_itm_ctlg_request(self, data: Dict) -> Dict: + async def handle_cstmz_itm_ctlg_request(self, data: Dict) -> Dict: catalog = "" itemList = self.data.static.get_enabled_items(self.version) @@ -229,7 +228,7 @@ class DivaBase: return response - def handle_buy_cstmz_itm_request(self, data: Dict) -> Dict: + async def handle_buy_cstmz_itm_request(self, data: Dict) -> Dict: profile = self.data.profile.get_profile(data["pd_id"], self.version) item = self.data.static.get_enabled_item( self.version, int(data["cstmz_itm_id"]) @@ -264,7 +263,7 @@ class DivaBase: return response - def handle_festa_info_request(self, data: Dict) -> Dict: + async def handle_festa_info_request(self, data: Dict) -> Dict: encoded = "&" params = { "fi_id": "1,2", @@ -287,7 +286,7 @@ class DivaBase: return encoded - def handle_contest_info_request(self, data: Dict) -> Dict: + async def handle_contest_info_request(self, data: Dict) -> Dict: response = "" response += f"&ci_lut={self.time_lut}" @@ -295,7 +294,7 @@ class DivaBase: return response - def handle_qst_inf_request(self, data: Dict) -> Dict: + async def handle_qst_inf_request(self, data: Dict) -> Dict: quest = "" questList = self.data.static.get_enabled_quests(self.version) @@ -345,43 +344,43 @@ class DivaBase: return response - def handle_nv_ranking_request(self, data: Dict) -> Dict: + async def handle_nv_ranking_request(self, data: Dict) -> Dict: return f"" - def handle_ps_ranking_request(self, data: Dict) -> Dict: + async def handle_ps_ranking_request(self, data: Dict) -> Dict: return f"" - def handle_ng_word_request(self, data: Dict) -> Dict: + async def handle_ng_word_request(self, data: Dict) -> Dict: return f"" - def handle_rmt_wp_list_request(self, data: Dict) -> Dict: + async def handle_rmt_wp_list_request(self, data: Dict) -> Dict: return f"" - def handle_pv_def_chr_list_request(self, data: Dict) -> Dict: + async def handle_pv_def_chr_list_request(self, data: Dict) -> Dict: return f"" - def handle_pv_ng_mdl_list_request(self, data: Dict) -> Dict: + async def handle_pv_ng_mdl_list_request(self, data: Dict) -> Dict: return f"" - def handle_cstmz_itm_ng_mdl_lst_request(self, data: Dict) -> Dict: + async def handle_cstmz_itm_ng_mdl_lst_request(self, data: Dict) -> Dict: return f"" - def handle_banner_info_request(self, data: Dict) -> Dict: + async def handle_banner_info_request(self, data: Dict) -> Dict: return f"" - def handle_banner_data_request(self, data: Dict) -> Dict: + async def handle_banner_data_request(self, data: Dict) -> Dict: return f"" - def handle_cm_ply_info_request(self, data: Dict) -> Dict: + async def handle_cm_ply_info_request(self, data: Dict) -> Dict: return f"" - def handle_pstd_h_ctrl_request(self, data: Dict) -> Dict: + async def handle_pstd_h_ctrl_request(self, data: Dict) -> Dict: return f"" - def handle_pstd_item_ng_lst_request(self, data: Dict) -> Dict: + async def handle_pstd_item_ng_lst_request(self, data: Dict) -> Dict: return f"" - def handle_pre_start_request(self, data: Dict) -> str: + async def handle_pre_start_request(self, data: Dict) -> str: profile = self.data.profile.get_profile(data["aime_id"], self.version) profile_shop = self.data.item.get_shop(data["aime_id"], self.version) @@ -422,13 +421,13 @@ class DivaBase: return response - def handle_registration_request(self, data: Dict) -> Dict: + async def handle_registration_request(self, data: Dict) -> Dict: self.data.profile.create_profile( self.version, data["aime_id"], data["player_name"] ) return f"&cd_adm_result=1&pd_id={data['aime_id']}" - def handle_start_request(self, data: Dict) -> Dict: + async def handle_start_request(self, data: Dict) -> Dict: profile = self.data.profile.get_profile(data["pd_id"], self.version) profile_shop = self.data.item.get_shop(data["pd_id"], self.version) if profile is None: @@ -583,10 +582,10 @@ class DivaBase: return response - def handle_pd_unlock_request(self, data: Dict) -> Dict: + async def handle_pd_unlock_request(self, data: Dict) -> Dict: return f"" - def handle_spend_credit_request(self, data: Dict) -> Dict: + async def handle_spend_credit_request(self, data: Dict) -> Dict: profile = self.data.profile.get_profile(data["pd_id"], self.version) if profile is None: return @@ -705,7 +704,7 @@ class DivaBase: pd_by_pv_id.append(urllib.parse.quote(f"{song}***")) pd_by_pv_id.append(",") - def handle_get_pv_pd_request(self, data: Dict) -> Dict: + async def handle_get_pv_pd_request(self, data: Dict) -> Dict: song_id = data["pd_pv_id_lst"].split(",") pv = "" @@ -732,10 +731,10 @@ class DivaBase: return response - def handle_stage_start_request(self, data: Dict) -> Dict: + async def handle_stage_start_request(self, data: Dict) -> Dict: return f"" - def handle_stage_result_request(self, data: Dict) -> Dict: + async def handle_stage_result_request(self, data: Dict) -> Dict: profile = self.data.profile.get_profile(data["pd_id"], self.version) pd_song_list = data["stg_ply_pv_id"].split(",") @@ -914,7 +913,7 @@ class DivaBase: return response - def handle_end_request(self, data: Dict) -> Dict: + async def handle_end_request(self, data: Dict) -> Dict: profile = self.data.profile.get_profile(data["pd_id"], self.version) self.data.profile.update_profile( @@ -922,7 +921,7 @@ class DivaBase: ) return f"" - def handle_shop_exit_request(self, data: Dict) -> Dict: + async def handle_shop_exit_request(self, data: Dict) -> Dict: self.data.item.put_shop( data["pd_id"], self.version, @@ -952,7 +951,7 @@ class DivaBase: response = "&shp_rslt=1" return response - def handle_card_procedure_request(self, data: Dict) -> str: + async def handle_card_procedure_request(self, data: Dict) -> str: profile = self.data.profile.get_profile(data["aime_id"], self.version) if profile is None: return "&cd_adm_result=0" @@ -972,7 +971,7 @@ class DivaBase: return response - def handle_change_name_request(self, data: Dict) -> str: + async def handle_change_name_request(self, data: Dict) -> str: profile = self.data.profile.get_profile(data["pd_id"], self.version) # make sure user has enough Vocaloid Points @@ -992,7 +991,7 @@ class DivaBase: return response - def handle_change_passwd_request(self, data: Dict) -> str: + async def handle_change_passwd_request(self, data: Dict) -> str: profile = self.data.profile.get_profile(data["pd_id"], self.version) # TODO: return correct error number instead of 0 diff --git a/titles/diva/index.py b/titles/diva/index.py index ac4114e..f414ab2 100644 --- a/titles/diva/index.py +++ b/titles/diva/index.py @@ -1,4 +1,6 @@ -from twisted.web.http import Request +from starlette.requests import Request +from starlette.responses import PlainTextResponse +from starlette.routing import Route import yaml import logging, coloredlogs from logging.handlers import TimedRotatingFileHandler @@ -51,17 +53,16 @@ class DivaServlet(BaseServlet): level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str ) - def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]: - return ( - [], - [("render_POST", "/DivaServlet/", {})] - ) + def get_routes(self) -> List[Route]: + return [ + Route("/DivaServlet/", self.render_POST, methods=['POST']) + ] def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]: if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80: - return (f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/DivaServlet/", self.core_cfg.title.hostname) + return (f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}/DivaServlet/", self.core_cfg.server.hostname) - return (f"http://{self.core_cfg.title.hostname}/DivaServlet/", self.core_cfg.title.hostname) + return (f"http://{self.core_cfg.server.hostname}/DivaServlet/", self.core_cfg.server.hostname) @classmethod def is_game_enabled( @@ -78,9 +79,9 @@ class DivaServlet(BaseServlet): return True - def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes: - req_raw = request.content.getvalue() - url_header = request.getAllHeaders() + async def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes: + req_raw = await request.body() + url_header = request.headers # Ping Dispatch if "THIS_STRING_SEPARATES" in str(url_header): @@ -103,9 +104,7 @@ class DivaServlet(BaseServlet): self.logger.debug( f"Response cmd={bin_req_data['cmd']}&req_id={bin_req_data['req_id']}&stat=ok{resp}" ) - return f"cmd={bin_req_data['cmd']}&req_id={bin_req_data['req_id']}&stat=ok{resp}".encode( - "utf-8" - ) + return PlainTextResponse(f"cmd={bin_req_data['cmd']}&req_id={bin_req_data['req_id']}&stat=ok{resp}") # Main Dispatch json_string = json.dumps( @@ -122,7 +121,7 @@ class DivaServlet(BaseServlet): ) # Decompressing the gzip except zlib.error as e: self.logger.error(f"Failed to defalte! {e} -> {gz_string}") - return "stat=0" + return PlainTextResponse("stat=0") req_kvp = urllib.parse.unquote(url_data) req_data = {} @@ -141,27 +140,18 @@ class DivaServlet(BaseServlet): # Load the requests try: handler = getattr(self.base, func_to_find) - resp = handler(req_data) + resp = await handler(req_data) except AttributeError as e: self.logger.warning(f"Unhandled {req_data['cmd']} request {e}") - return f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok".encode( - "utf-8" - ) + return PlainTextResponse(f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok") except Exception as e: self.logger.error(f"Error handling method {func_to_find} {e}") - return f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok".encode( - "utf-8" - ) + return PlainTextResponse(f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok") - request.responseHeaders.addRawHeader(b"content-type", b"text/plain") self.logger.debug( f"Response cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok{resp}" ) - return ( - f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok{resp}".encode( - "utf-8" - ) - ) + return PlainTextResponse(f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok{resp}") diff --git a/titles/idac/frontend.py b/titles/idac/frontend.py index 78abae8..8845b35 100644 --- a/titles/idac/frontend.py +++ b/titles/idac/frontend.py @@ -3,7 +3,7 @@ import yaml import jinja2 from os import path from twisted.web.util import redirectTo -from twisted.web.http import Request +from starlette.requests import Request from twisted.web.server import Session from core.frontend import FE_Base, IUserSession diff --git a/titles/idac/index.py b/titles/idac/index.py index 7daedae..1aa8050 100644 --- a/titles/idac/index.py +++ b/titles/idac/index.py @@ -1,18 +1,18 @@ import json import traceback -import inflection +from starlette.routing import Route +from starlette.requests import Request +from starlette.responses import JSONResponse import yaml import logging import coloredlogs - from os import path from typing import Dict, List, Tuple from logging.handlers import TimedRotatingFileHandler -from twisted.web import server -from twisted.web.http import Request -from twisted.internet import reactor, endpoints + from core.config import CoreConfig +from core.title import BaseServlet from core.utils import Utils from titles.idac.base import IDACBase from titles.idac.season2 import IDACSeason2 @@ -22,7 +22,7 @@ from titles.idac.echo import IDACEchoUDP from titles.idac.matching import IDACMatching -class IDACServlet: +class IDACServlet(BaseServlet): def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None: self.core_cfg = core_cfg self.game_cfg = IDACConfig() @@ -72,12 +72,11 @@ class IDACServlet: return False return True - - def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]: - return ( - [], - [("render_POST", "/SDGT/{version}/initiald/{category}/{endpoint}", {})] - ) + + def get_routes(self) -> List[Route]: + return [ + Route("/{version:int}/initiald/{category:str}/{endpoint:str}", self.render_POST, methods=["POST"]) + ] def get_allnet_info( self, game_code: str, game_ver: int, keychip: str @@ -88,15 +87,15 @@ class IDACServlet: return ( f"", # requires http or else it defaults to https - f"http://{self.core_cfg.title.hostname}{t_port}/{game_code}/{game_ver}/", + f"http://{self.core_cfg.server.hostname}{t_port}/{game_code}/{game_ver}/", ) - def render_POST(self, request: Request, game_code: int, matchers: Dict) -> bytes: - req_raw = request.content.getvalue() + async def render_POST(self, request: Request) -> bytes: + req_raw = await request.body() internal_ver = 0 - version = int(matchers['version']) - category = matchers['category'] - endpoint = matchers['endpoint'] + version: int = request.path_params.get('version') + category: str = request.path_params.get('category') + endpoint: str = request.path_params.get('endpoint') client_ip = Utils.get_ip_addr(request) if version >= 100 and version < 140: # IDAC Season 1 @@ -104,7 +103,7 @@ class IDACServlet: elif version >= 140 and version < 171: # IDAC Season 2 internal_ver = IDACConstants.VER_IDAC_SEASON_2 - header_application = self.decode_header(request.getAllHeaders()) + header_application = self.decode_header(request.headers.get("application", "")) req_data = json.loads(req_raw) @@ -119,7 +118,7 @@ class IDACServlet: if not hasattr(self.versions[internal_ver], func_to_find): self.logger.warning(f"Unhandled v{version} request {endpoint}") - return '{"status_code": "0"}'.encode("utf-8") + return JSONResponse('{"status_code": "0"}') resp = None try: @@ -129,17 +128,16 @@ class IDACServlet: except Exception as e: traceback.print_exc() self.logger.error(f"Error handling v{version} method {endpoint} - {e}") - return '{"status_code": "0"}'.encode("utf-8") + return JSONResponse('{"status_code": "0"}') if resp is None: resp = {"status_code": "0"} self.logger.debug(f"Response {resp}") - return json.dumps(resp, ensure_ascii=False).encode("utf-8") + return JSONResponse(json.dumps(resp, ensure_ascii=False)) - def decode_header(self, data: Dict) -> Dict: - app: str = data[b"application"].decode() + def decode_header(self, app: str) -> Dict: ret = {} for x in app.split(", "): @@ -149,6 +147,8 @@ class IDACServlet: return ret def setup(self): + return + """ if self.game_cfg.server.enable: endpoints.serverFromString( reactor, @@ -165,3 +165,4 @@ class IDACServlet: ) self.logger.info(f"Matching listening on {self.game_cfg.server.matching} with echos on {self.game_cfg.server.echo1} and {self.game_cfg.server.echo2}") + """ diff --git a/titles/idac/season2.py b/titles/idac/season2.py index ca57392..fabcbcd 100644 --- a/titles/idac/season2.py +++ b/titles/idac/season2.py @@ -109,9 +109,9 @@ class IDACSeason2(IDACBase): ver_str = version.replace(".", "")[:3] if self.core_cfg.server.is_develop: - domain_api_game = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDGT/{ver_str}/" + domain_api_game = f"http://{self.core_cfg.server.hostname}:{self.core_cfg.server.port}/SDGT/{ver_str}/" else: - domain_api_game = f"http://{self.core_cfg.title.hostname}/SDGT/{ver_str}/" + domain_api_game = f"http://{self.core_cfg.server.hostname}/SDGT/{ver_str}/" return { "status_code": "0", @@ -136,10 +136,10 @@ class IDACSeason2(IDACBase): "server_maintenance_end_hour": 0, "server_maintenance_end_minutes": 0, "domain_api_game": domain_api_game, - "domain_matching": f"http://{self.core_cfg.title.hostname}:{self.game_config.server.matching}", - "domain_echo1": f"{self.core_cfg.title.hostname}:{self.game_config.server.echo1}", - "domain_echo2": f"{self.core_cfg.title.hostname}:{self.game_config.server.echo2}", - "domain_ping": f"{self.core_cfg.title.hostname}", + "domain_matching": f"http://{self.core_cfg.server.hostname}:{self.game_config.server.matching}", + "domain_echo1": f"{self.core_cfg.server.hostname}:{self.game_config.server.echo1}", + "domain_echo2": f"{self.core_cfg.server.hostname}:{self.game_config.server.echo2}", + "domain_ping": f"{self.core_cfg.server.hostname}", "battle_gift_event_master": [], "round_event": [ { diff --git a/titles/idz/handlers/load_server_info.py b/titles/idz/handlers/load_server_info.py index 4c60dd7..9eb63ab 100644 --- a/titles/idz/handlers/load_server_info.py +++ b/titles/idz/handlers/load_server_info.py @@ -24,10 +24,10 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase): t_port = Utils.get_title_port(self.core_config) - news_str = f"http://{self.core_config.title.hostname}:{t_port}/idz/news/news80**.txt" - err_str = f"http://{self.core_config.title.hostname}:{t_port}/idz/error" + news_str = f"http://{self.core_config.server.hostname}:{t_port}/idz/news/news80**.txt" + err_str = f"http://{self.core_config.server.hostname}:{t_port}/idz/error" - len_hostname = len(self.core_config.title.hostname) + len_hostname = len(self.core_config.server.hostname) len_news = len(news_str) len_error = len(err_str) @@ -36,7 +36,7 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase): f"{len_hostname}s", ret, 0x4 + offset, - self.core_config.title.hostname.encode(), + self.core_config.server.hostname.encode(), ) struct.pack_into(" Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]: - return[ - [("render_GET", "/idz/news/{endpoint:.*?}", {}), - ("render_GET", "/idz/error", {})], - [] + def get_routes(self) -> List[Route]: + return [ + Route("/idz/news/{endpoint:str}", self.render_GET), + Route("/idz/error", self.render_GET) ] def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]: hostname = ( - self.core_cfg.title.hostname + self.core_cfg.server.hostname if not self.game_cfg.server.hostname else self.game_cfg.server.hostname ) @@ -135,7 +134,8 @@ class IDZServlet(BaseServlet): except AttributeError as e: continue - + + """ endpoints.serverFromString( reactor, f"tcp:{self.game_cfg.ports.userdb}:interface={self.core_cfg.server.listen_address}", @@ -155,18 +155,15 @@ class IDZServlet(BaseServlet): reactor.listenUDP( self.game_cfg.ports.userdb + 1, IDZEcho(self.core_cfg, self.game_cfg) ) - + """ self.logger.info(f"UserDB Listening on port {self.game_cfg.ports.userdb}") - def render_GET(self, request: Request, game_code: str, matchers: Dict) -> bytes: - url_path = matchers['endpoint'] + async def render_GET(self, request: Request) -> bytes: + url_path = request.path_params.get('endpoint', '') + if not url_path: + return Response() + self.logger.info(f"IDZ GET request: {url_path}") - request.responseHeaders.setRawHeaders( - "Content-Type", [b"text/plain; charset=utf-8"] - ) - request.responseHeaders.setRawHeaders( - "Last-Modified", [b"Sun, 23 Apr 2023 05:33:20 GMT"] - ) news = ( self.game_cfg.server.news @@ -176,4 +173,4 @@ class IDZServlet(BaseServlet): news += "\r\n" news = "1979/01/01 00:00:00 2099/12/31 23:59:59 " + news - return news.encode() + return PlainTextResponse(news, media_type="text/plain; charset=utf-8", headers={"Last-Modified": "Sun, 23 Apr 2023 05:33:20 GMT"}) diff --git a/titles/idz/userdb.py b/titles/idz/userdb.py index fd555a1..b585bd2 100644 --- a/titles/idz/userdb.py +++ b/titles/idz/userdb.py @@ -5,7 +5,7 @@ import struct from typing import Dict, Optional, List, Type from twisted.web import server, resource from twisted.internet import reactor, endpoints -from twisted.web.http import Request +from starlette.requests import Request from routes import Mapper import random from os import walk diff --git a/titles/mai2/base.py b/titles/mai2/base.py index 2f40176..a9ddcab 100644 --- a/titles/mai2/base.py +++ b/titles/mai2/base.py @@ -26,12 +26,12 @@ class Mai2Base: self.date_time_format = "%Y-%m-%d %H:%M:%S" if not self.core_config.server.is_using_proxy and Utils.get_title_port(self.core_config) != 80: - self.old_server = f"http://{self.core_config.title.hostname}:{Utils.get_title_port(cfg)}/197/MaimaiServlet/" + self.old_server = f"http://{self.core_config.server.hostname}:{Utils.get_title_port(cfg)}/197/MaimaiServlet/" else: - self.old_server = f"http://{self.core_config.title.hostname}/197/MaimaiServlet/" + self.old_server = f"http://{self.core_config.server.hostname}/197/MaimaiServlet/" - def handle_get_game_setting_api_request(self, data: Dict): + async def handle_get_game_setting_api_request(self, data: Dict): # if reboot start/end time is not defined use the default behavior of being a few hours ago if self.core_config.title.reboot_start_time == "" or self.core_config.title.reboot_end_time == "": reboot_start = datetime.strftime( @@ -74,14 +74,14 @@ class Mai2Base: }, } - def handle_get_game_ranking_api_request(self, data: Dict) -> Dict: + async def handle_get_game_ranking_api_request(self, data: Dict) -> Dict: return {"length": 0, "gameRankingList": []} - def handle_get_game_tournament_info_api_request(self, data: Dict) -> Dict: + async def handle_get_game_tournament_info_api_request(self, data: Dict) -> Dict: # TODO: Tournament support return {"length": 0, "gameTournamentInfoList": []} - def handle_get_game_event_api_request(self, data: Dict) -> Dict: + async def handle_get_game_event_api_request(self, data: Dict) -> Dict: events = self.data.static.get_enabled_events(self.version) events_lst = [] if events is None or not events: @@ -108,10 +108,10 @@ class Mai2Base: "gameEventList": events_lst, } - def handle_get_game_ng_music_id_api_request(self, data: Dict) -> Dict: + async def handle_get_game_ng_music_id_api_request(self, data: Dict) -> Dict: return {"length": 0, "musicIdList": []} - def handle_get_game_charge_api_request(self, data: Dict) -> Dict: + async def handle_get_game_charge_api_request(self, data: Dict) -> Dict: game_charge_list = self.data.static.get_enabled_tickets(self.version, 1) if game_charge_list is None: return {"length": 0, "gameChargeList": []} @@ -130,19 +130,19 @@ class Mai2Base: return {"length": len(charge_list), "gameChargeList": charge_list} - def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict: + async def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict: return {"returnCode": 1, "apiName": "UpsertClientSettingApi"} - def handle_upsert_client_upload_api_request(self, data: Dict) -> Dict: + async def handle_upsert_client_upload_api_request(self, data: Dict) -> Dict: return {"returnCode": 1, "apiName": "UpsertClientUploadApi"} - def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict: + async def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict: return {"returnCode": 1, "apiName": "UpsertClientBookkeepingApi"} - def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict: + async def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict: return {"returnCode": 1, "apiName": "UpsertClientTestmodeApi"} - def handle_get_user_preview_api_request(self, data: Dict) -> Dict: + async def handle_get_user_preview_api_request(self, data: Dict) -> Dict: p = self.data.profile.get_profile_detail(data["userId"], self.version, False) w = self.data.profile.get_web_option(data["userId"], self.version) if p is None or w is None: @@ -169,7 +169,7 @@ class Mai2Base: "totalLv": profile["totalLv"], } - def handle_user_login_api_request(self, data: Dict) -> Dict: + async def handle_user_login_api_request(self, data: Dict) -> Dict: profile = self.data.profile.get_profile_detail(data["userId"], self.version) consec = self.data.profile.get_consec_login(data["userId"], self.version) @@ -210,7 +210,7 @@ class Mai2Base: "consecutiveLoginCount": consec_ct, # Number of consecutive days we've logged in. } - def handle_upload_user_playlog_api_request(self, data: Dict) -> Dict: + async def handle_upload_user_playlog_api_request(self, data: Dict) -> Dict: user_id = data["userId"] playlog = data["userPlaylog"] @@ -218,7 +218,7 @@ class Mai2Base: return {"returnCode": 1, "apiName": "UploadUserPlaylogApi"} - def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict: + async def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict: user_id = data["userId"] charge = data["userCharge"] @@ -234,7 +234,7 @@ class Mai2Base: return {"returnCode": 1, "apiName": "UpsertUserChargelogApi"} - def handle_upsert_user_all_api_request(self, data: Dict) -> Dict: + async def handle_upsert_user_all_api_request(self, data: Dict) -> Dict: user_id = data["userId"] upsert = data["upsertUserAll"] @@ -375,10 +375,10 @@ class Mai2Base: return {"returnCode": 1, "apiName": "UpsertUserAllApi"} - def handle_user_logout_api_request(self, data: Dict) -> Dict: + async def handle_user_logout_api_request(self, data: Dict) -> Dict: return {"returnCode": 1} - def handle_get_user_data_api_request(self, data: Dict) -> Dict: + async def handle_get_user_data_api_request(self, data: Dict) -> Dict: profile = self.data.profile.get_profile_detail(data["userId"], self.version, False) if profile is None: return @@ -390,7 +390,7 @@ class Mai2Base: return {"userId": data["userId"], "userData": profile_dict} - def handle_get_user_extend_api_request(self, data: Dict) -> Dict: + async def handle_get_user_extend_api_request(self, data: Dict) -> Dict: extend = self.data.profile.get_profile_extend(data["userId"], self.version) if extend is None: return @@ -402,7 +402,7 @@ class Mai2Base: return {"userId": data["userId"], "userExtend": extend_dict} - def handle_get_user_option_api_request(self, data: Dict) -> Dict: + async def handle_get_user_option_api_request(self, data: Dict) -> Dict: options = self.data.profile.get_profile_option(data["userId"], self.version, False) if options is None: return @@ -414,7 +414,7 @@ class Mai2Base: return {"userId": data["userId"], "userOption": options_dict} - def handle_get_user_card_api_request(self, data: Dict) -> Dict: + async def handle_get_user_card_api_request(self, data: Dict) -> Dict: user_cards = self.data.item.get_cards(data["userId"]) if user_cards is None: return {"userId": data["userId"], "nextIndex": 0, "userCardList": []} @@ -448,7 +448,7 @@ class Mai2Base: "userCardList": card_list[start_idx:end_idx], } - def handle_get_user_charge_api_request(self, data: Dict) -> Dict: + async def handle_get_user_charge_api_request(self, data: Dict) -> Dict: user_charges = self.data.item.get_charges(data["userId"]) if user_charges is None: return {"userId": data["userId"], "length": 0, "userChargeList": []} @@ -467,16 +467,16 @@ class Mai2Base: "userChargeList": user_charge_list, } - def handle_get_user_present_api_request(self, data: Dict) -> Dict: + async def handle_get_user_present_api_request(self, data: Dict) -> Dict: return { "userId": data.get("userId", 0), "length": 0, "userPresentList": []} - def handle_get_transfer_friend_api_request(self, data: Dict) -> Dict: + async def handle_get_transfer_friend_api_request(self, data: Dict) -> Dict: return {} - def handle_get_user_present_event_api_request(self, data: Dict) -> Dict: + async def handle_get_user_present_event_api_request(self, data: Dict) -> Dict: return { "userId": data.get("userId", 0), "length": 0, "userPresentEventList": []} - def handle_get_user_boss_api_request(self, data: Dict) -> Dict: + async def handle_get_user_boss_api_request(self, data: Dict) -> Dict: b = self.data.profile.get_boss_list(data["userId"]) if b is None: return { "userId": data.get("userId", 0), "userBossData": {}} @@ -486,7 +486,7 @@ class Mai2Base: return { "userId": data.get("userId", 0), "userBossData": boss_lst} - def handle_get_user_item_api_request(self, data: Dict) -> Dict: + async def handle_get_user_item_api_request(self, data: Dict) -> Dict: kind = int(data["nextIndex"] / 10000000000) next_idx = int(data["nextIndex"] % 10000000000) user_item_list = self.data.item.get_items(data["userId"], kind) @@ -514,7 +514,7 @@ class Mai2Base: "userItemList": items, } - def handle_get_user_character_api_request(self, data: Dict) -> Dict: + async def handle_get_user_character_api_request(self, data: Dict) -> Dict: characters = self.data.item.get_characters(data["userId"]) chara_list = [] @@ -528,7 +528,7 @@ class Mai2Base: return {"userId": data["userId"], "userCharacterList": chara_list} - def handle_get_user_favorite_api_request(self, data: Dict) -> Dict: + async def handle_get_user_favorite_api_request(self, data: Dict) -> Dict: favorites = self.data.item.get_favorites(data["userId"], data["itemKind"]) if favorites is None: return @@ -545,7 +545,7 @@ class Mai2Base: return {"userId": data["userId"], "userFavoriteData": userFavs} - def handle_get_user_ghost_api_request(self, data: Dict) -> Dict: + async def handle_get_user_ghost_api_request(self, data: Dict) -> Dict: ghost = self.data.profile.get_profile_ghost(data["userId"], self.version) if ghost is None: return @@ -557,7 +557,7 @@ class Mai2Base: return {"userId": data["userId"], "userGhost": ghost_dict} - def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict: + async def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict: rating = self.data.profile.get_recent_rating(data["userId"]) if rating is None: return @@ -567,7 +567,7 @@ class Mai2Base: return {"userId": data["userId"], "length": len(lst), "userRecentRatingList": lst} - def handle_get_user_rating_api_request(self, data: Dict) -> Dict: + async def handle_get_user_rating_api_request(self, data: Dict) -> Dict: rating = self.data.profile.get_profile_rating(data["userId"], self.version) if rating is None: return @@ -579,7 +579,7 @@ class Mai2Base: return {"userId": data["userId"], "userRating": rating_dict} - def handle_get_user_activity_api_request(self, data: Dict) -> Dict: + async def handle_get_user_activity_api_request(self, data: Dict) -> Dict: """ kind 1 is playlist, kind 2 is music list """ @@ -607,7 +607,7 @@ class Mai2Base: return {"userActivity": {"playList": plst, "musicList": mlst}} - def handle_get_user_course_api_request(self, data: Dict) -> Dict: + async def handle_get_user_course_api_request(self, data: Dict) -> Dict: user_courses = self.data.score.get_courses(data["userId"]) if user_courses is None: return {"userId": data["userId"], "nextIndex": 0, "userCourseList": []} @@ -621,11 +621,11 @@ class Mai2Base: return {"userId": data["userId"], "nextIndex": 0, "userCourseList": course_list} - def handle_get_user_portrait_api_request(self, data: Dict) -> Dict: + async def handle_get_user_portrait_api_request(self, data: Dict) -> Dict: # No support for custom pfps return {"length": 0, "userPortraitList": []} - def handle_get_user_friend_season_ranking_api_request(self, data: Dict) -> Dict: + async def handle_get_user_friend_season_ranking_api_request(self, data: Dict) -> Dict: friend_season_ranking = self.data.item.get_friend_season_ranking(data["userId"]) if friend_season_ranking is None: return { @@ -661,7 +661,7 @@ class Mai2Base: "userFriendSeasonRankingList": friend_season_ranking_list, } - def handle_get_user_map_api_request(self, data: Dict) -> Dict: + async def handle_get_user_map_api_request(self, data: Dict) -> Dict: maps = self.data.item.get_maps(data["userId"]) if maps is None: return { @@ -694,7 +694,7 @@ class Mai2Base: "userMapList": map_list, } - def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict: + async def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict: login_bonuses = self.data.item.get_login_bonuses(data["userId"]) if login_bonuses is None: return { @@ -727,10 +727,10 @@ class Mai2Base: "userLoginBonusList": login_bonus_list, } - def handle_get_user_region_api_request(self, data: Dict) -> Dict: + async def handle_get_user_region_api_request(self, data: Dict) -> Dict: return {"userId": data["userId"], "length": 0, "userRegionList": []} - def handle_get_user_web_option_api_request(self, data: Dict) -> Dict: + async def handle_get_user_web_option_api_request(self, data: Dict) -> Dict: w = self.data.profile.get_web_option(data["userId"], self.version) if w is None: return {"userId": data["userId"], "userWebOption": {}} @@ -742,10 +742,10 @@ class Mai2Base: return {"userId": data["userId"], "userWebOption": web_opt} - def handle_get_user_survival_api_request(self, data: Dict) -> Dict: + async def handle_get_user_survival_api_request(self, data: Dict) -> Dict: return {"userId": data["userId"], "length": 0, "userSurvivalList": []} - def handle_get_user_grade_api_request(self, data: Dict) -> Dict: + async def handle_get_user_grade_api_request(self, data: Dict) -> Dict: g = self.data.profile.get_grade_status(data["userId"]) if g is None: return {"userId": data["userId"], "userGradeStatus": {}, "length": 0, "userGradeList": []} @@ -755,7 +755,7 @@ class Mai2Base: return {"userId": data["userId"], "userGradeStatus": grade_stat, "length": 0, "userGradeList": []} - def handle_get_user_music_api_request(self, data: Dict) -> Dict: + async def handle_get_user_music_api_request(self, data: Dict) -> Dict: user_id = data.get("userId", 0) next_index = data.get("nextIndex", 0) max_ct = data.get("maxCount", 50) @@ -794,10 +794,10 @@ class Mai2Base: "userMusicList": [{"userMusicDetailList": music_detail_list}], } - def handle_upload_user_portrait_api_request(self, data: Dict) -> Dict: + async def handle_upload_user_portrait_api_request(self, data: Dict) -> Dict: self.logger.debug(data) - def handle_upload_user_photo_api_request(self, data: Dict) -> Dict: + async def handle_upload_user_photo_api_request(self, data: Dict) -> Dict: if not self.game_config.uploads.photos or not self.game_config.uploads.photos_dir: return {'returnCode': 0, 'apiName': 'UploadUserPhotoApi'} diff --git a/titles/mai2/dx.py b/titles/mai2/dx.py index 508cebb..80a3bc2 100644 --- a/titles/mai2/dx.py +++ b/titles/mai2/dx.py @@ -15,7 +15,7 @@ class Mai2DX(Mai2Base): super().__init__(cfg, game_cfg) self.version = Mai2Constants.VER_MAIMAI_DX - def handle_get_game_setting_api_request(self, data: Dict): + async def handle_get_game_setting_api_request(self, data: Dict): return { "gameSetting": { "isMaintenance": False, @@ -33,7 +33,7 @@ class Mai2DX(Mai2Base): "isAouAccession": False, } - def handle_get_user_preview_api_request(self, data: Dict) -> Dict: + async def handle_get_user_preview_api_request(self, data: Dict) -> Dict: p = self.data.profile.get_profile_detail(data["userId"], self.version) o = self.data.profile.get_profile_option(data["userId"], self.version) if p is None or o is None: @@ -69,7 +69,7 @@ class Mai2DX(Mai2Base): else 0, # New with uni+ } - def handle_upload_user_playlog_api_request(self, data: Dict) -> Dict: + async def handle_upload_user_playlog_api_request(self, data: Dict) -> Dict: user_id = data["userId"] playlog = data["userPlaylog"] @@ -77,7 +77,7 @@ class Mai2DX(Mai2Base): return {"returnCode": 1, "apiName": "UploadUserPlaylogApi"} - def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict: + async def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict: user_id = data["userId"] charge = data["userCharge"] @@ -93,7 +93,7 @@ class Mai2DX(Mai2Base): return {"returnCode": 1, "apiName": "UpsertUserChargelogApi"} - def handle_upsert_user_all_api_request(self, data: Dict) -> Dict: + async def handle_upsert_user_all_api_request(self, data: Dict) -> Dict: user_id = data["userId"] upsert = data["upsertUserAll"] @@ -215,7 +215,7 @@ class Mai2DX(Mai2Base): return {"returnCode": 1, "apiName": "UpsertUserAllApi"} - def handle_get_user_data_api_request(self, data: Dict) -> Dict: + async def handle_get_user_data_api_request(self, data: Dict) -> Dict: profile = self.data.profile.get_profile_detail(data["userId"], self.version) if profile is None: return @@ -227,7 +227,7 @@ class Mai2DX(Mai2Base): return {"userId": data["userId"], "userData": profile_dict} - def handle_get_user_extend_api_request(self, data: Dict) -> Dict: + async def handle_get_user_extend_api_request(self, data: Dict) -> Dict: extend = self.data.profile.get_profile_extend(data["userId"], self.version) if extend is None: return @@ -239,7 +239,7 @@ class Mai2DX(Mai2Base): return {"userId": data["userId"], "userExtend": extend_dict} - def handle_get_user_option_api_request(self, data: Dict) -> Dict: + async def handle_get_user_option_api_request(self, data: Dict) -> Dict: options = self.data.profile.get_profile_option(data["userId"], self.version) if options is None: return @@ -251,7 +251,7 @@ class Mai2DX(Mai2Base): return {"userId": data["userId"], "userOption": options_dict} - def handle_get_user_card_api_request(self, data: Dict) -> Dict: + async def handle_get_user_card_api_request(self, data: Dict) -> Dict: user_cards = self.data.item.get_cards(data["userId"]) if user_cards is None: return {"userId": data["userId"], "nextIndex": 0, "userCardList": []} @@ -285,7 +285,7 @@ class Mai2DX(Mai2Base): "userCardList": card_list[start_idx:end_idx], } - def handle_get_user_charge_api_request(self, data: Dict) -> Dict: + async def handle_get_user_charge_api_request(self, data: Dict) -> Dict: user_charges = self.data.item.get_charges(data["userId"]) if user_charges is None: return {"userId": data["userId"], "length": 0, "userChargeList": []} @@ -310,7 +310,7 @@ class Mai2DX(Mai2Base): "userChargeList": user_charge_list, } - def handle_get_user_item_api_request(self, data: Dict) -> Dict: + async def handle_get_user_item_api_request(self, data: Dict) -> Dict: kind = int(data["nextIndex"] / 10000000000) next_idx = int(data["nextIndex"] % 10000000000) user_item_list = self.data.item.get_items(data["userId"], kind) @@ -338,7 +338,7 @@ class Mai2DX(Mai2Base): "userItemList": items, } - def handle_get_user_character_api_request(self, data: Dict) -> Dict: + async def handle_get_user_character_api_request(self, data: Dict) -> Dict: characters = self.data.item.get_characters(data["userId"]) chara_list = [] @@ -350,7 +350,7 @@ class Mai2DX(Mai2Base): return {"userId": data["userId"], "userCharacterList": chara_list} - def handle_get_user_favorite_api_request(self, data: Dict) -> Dict: + async def handle_get_user_favorite_api_request(self, data: Dict) -> Dict: favorites = self.data.item.get_favorites(data["userId"], data["itemKind"]) if favorites is None: return @@ -367,7 +367,7 @@ class Mai2DX(Mai2Base): return {"userId": data["userId"], "userFavoriteData": userFavs} - def handle_get_user_ghost_api_request(self, data: Dict) -> Dict: + async def handle_get_user_ghost_api_request(self, data: Dict) -> Dict: ghost = self.data.profile.get_profile_ghost(data["userId"], self.version) if ghost is None: return @@ -379,7 +379,7 @@ class Mai2DX(Mai2Base): return {"userId": data["userId"], "userGhost": ghost_dict} - def handle_get_user_rating_api_request(self, data: Dict) -> Dict: + async def handle_get_user_rating_api_request(self, data: Dict) -> Dict: rating = self.data.profile.get_profile_rating(data["userId"], self.version) if rating is None: return @@ -391,7 +391,7 @@ class Mai2DX(Mai2Base): return {"userId": data["userId"], "userRating": rating_dict} - def handle_get_user_activity_api_request(self, data: Dict) -> Dict: + async def handle_get_user_activity_api_request(self, data: Dict) -> Dict: """ kind 1 is playlist, kind 2 is music list """ @@ -419,7 +419,7 @@ class Mai2DX(Mai2Base): return {"userActivity": {"playList": plst, "musicList": mlst}} - def handle_get_user_course_api_request(self, data: Dict) -> Dict: + async def handle_get_user_course_api_request(self, data: Dict) -> Dict: user_courses = self.data.score.get_courses(data["userId"]) if user_courses is None: return {"userId": data["userId"], "nextIndex": 0, "userCourseList": []} @@ -433,11 +433,11 @@ class Mai2DX(Mai2Base): return {"userId": data["userId"], "nextIndex": 0, "userCourseList": course_list} - def handle_get_user_portrait_api_request(self, data: Dict) -> Dict: + async def handle_get_user_portrait_api_request(self, data: Dict) -> Dict: # No support for custom pfps return {"length": 0, "userPortraitList": []} - def handle_get_user_friend_season_ranking_api_request(self, data: Dict) -> Dict: + async def handle_get_user_friend_season_ranking_api_request(self, data: Dict) -> Dict: friend_season_ranking = self.data.item.get_friend_season_ranking(data["userId"]) if friend_season_ranking is None: return { @@ -473,7 +473,7 @@ class Mai2DX(Mai2Base): "userFriendSeasonRankingList": friend_season_ranking_list, } - def handle_get_user_map_api_request(self, data: Dict) -> Dict: + async def handle_get_user_map_api_request(self, data: Dict) -> Dict: maps = self.data.item.get_maps(data["userId"]) if maps is None: return { @@ -506,7 +506,7 @@ class Mai2DX(Mai2Base): "userMapList": map_list, } - def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict: + async def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict: login_bonuses = self.data.item.get_login_bonuses(data["userId"]) if login_bonuses is None: return { @@ -539,7 +539,7 @@ class Mai2DX(Mai2Base): "userLoginBonusList": login_bonus_list, } - def handle_get_user_region_api_request(self, data: Dict) -> Dict: + async def handle_get_user_region_api_request(self, data: Dict) -> Dict: """ class UserRegionList: regionId: int @@ -548,7 +548,7 @@ class Mai2DX(Mai2Base): """ return {"userId": data["userId"], "length": 0, "userRegionList": []} - def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict: + async def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict: user_id = data["userId"] rival_id = data["rivalId"] @@ -559,7 +559,7 @@ class Mai2DX(Mai2Base): """ return {"userId": user_id, "userRivalData": {}} - def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict: + async def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict: user_id = data["userId"] rival_id = data["rivalId"] next_idx = data["nextIndex"] @@ -577,7 +577,7 @@ class Mai2DX(Mai2Base): """ return {"userId": user_id, "nextIndex": 0, "userRivalMusicList": []} - def handle_get_user_music_api_request(self, data: Dict) -> Dict: + async def handle_get_user_music_api_request(self, data: Dict) -> Dict: user_id = data.get("userId", 0) next_index = data.get("nextIndex", 0) max_ct = data.get("maxCount", 50) @@ -616,8 +616,8 @@ class Mai2DX(Mai2Base): "userMusicList": [{"userMusicDetailList": music_detail_list}], } - def handle_user_login_api_request(self, data: Dict) -> Dict: - ret = super().handle_user_login_api_request(data) + async def handle_user_login_api_request(self, data: Dict) -> Dict: + ret = await super().handle_user_login_api_request(data) if ret is None or not ret: return ret ret['loginId'] = ret.get('loginCount', 0) diff --git a/titles/mai2/festival.py b/titles/mai2/festival.py index 145fa71..94ce3ec 100644 --- a/titles/mai2/festival.py +++ b/titles/mai2/festival.py @@ -11,26 +11,26 @@ class Mai2Festival(Mai2UniversePlus): super().__init__(cfg, game_cfg) self.version = Mai2Constants.VER_MAIMAI_DX_FESTIVAL - def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: - user_data = super().handle_cm_get_user_preview_api_request(data) + async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: + user_data = await super().handle_cm_get_user_preview_api_request(data) # hardcode lastDataVersion for CardMaker user_data["lastDataVersion"] = "1.30.00" return user_data - def handle_user_login_api_request(self, data: Dict) -> Dict: - user_login = super().handle_user_login_api_request(data) + async def handle_user_login_api_request(self, data: Dict) -> Dict: + user_login = await super().handle_user_login_api_request(data) # useless? user_login["Bearer"] = "ARTEMiSTOKEN" return user_login - def handle_get_user_recommend_rate_music_api_request(self, data: Dict) -> Dict: + async def handle_get_user_recommend_rate_music_api_request(self, data: Dict) -> Dict: """ userRecommendRateMusicIdList: list[int] """ return {"userId": data["userId"], "userRecommendRateMusicIdList": []} - def handle_get_user_recommend_select_music_api_request(self, data: Dict) -> Dict: + async def handle_get_user_recommend_select_music_api_request(self, data: Dict) -> Dict: """ userRecommendSelectionMusicIdList: list[int] """ diff --git a/titles/mai2/festivalplus.py b/titles/mai2/festivalplus.py index 7deeb98..375d546 100644 --- a/titles/mai2/festivalplus.py +++ b/titles/mai2/festivalplus.py @@ -11,14 +11,14 @@ class Mai2FestivalPlus(Mai2Festival): super().__init__(cfg, game_cfg) self.version = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS - def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: - user_data = super().handle_cm_get_user_preview_api_request(data) + async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: + user_data = await super().handle_cm_get_user_preview_api_request(data) # hardcode lastDataVersion for CardMaker user_data["lastDataVersion"] = "1.35.00" return user_data - def handle_get_user_favorite_item_api_request(self, data: Dict) -> Dict: + async def handle_get_user_favorite_item_api_request(self, data: Dict) -> Dict: user_id = data.get("userId", 0) kind = data.get("kind", 2) next_index = data.get("nextIndex", 0) diff --git a/titles/mai2/index.py b/titles/mai2/index.py index 793aaef..2ee4cae 100644 --- a/titles/mai2/index.py +++ b/titles/mai2/index.py @@ -1,9 +1,9 @@ -from twisted.web.http import Request -from twisted.web.server import NOT_DONE_YET +from starlette.requests import Request +from starlette.responses import Response, JSONResponse +from starlette.routing import Route import json import inflection import yaml -import string import logging, coloredlogs import zlib from logging.handlers import TimedRotatingFileHandler @@ -101,33 +101,29 @@ class Mai2Servlet(BaseServlet): return True - def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]: - return ( - [ - ("handle_movie", "/{version}/MaimaiServlet/api/movie/{endpoint}", {}), - ("handle_old_srv", "/{version}/MaimaiServlet/old/{endpoint}", {}), - ("handle_old_srv_userdata", "/{version}/MaimaiServlet/old/{endpoint}/{placeid}/{keychip}/{userid}", {}), - ("handle_old_srv_userdata", "/{version}/MaimaiServlet/old/{endpoint}/{userid}", {}), - ("handle_usbdl", "/{version}/MaimaiServlet/usbdl/{endpoint}", {}), - ("handle_deliver", "/{version}/MaimaiServlet/deliver/{endpoint}", {}), - ], - [ - ("handle_movie", "/{version}/MaimaiServlet/api/movie/{endpoint}", {}), - ("handle_mai", "/{version}/MaimaiServlet/{endpoint}", {}), - ("handle_mai2", "/{version}/Maimai2Servlet/{endpoint}", {}), - ] - ) - + def get_routes(self) -> List[Route]: + return [ + Route("/{version:int}/MaimaiServlet/api/movie/{endpoint:str}", self.handle_movie, methods=['GET', 'POST']), + Route("/{version:int}/MaimaiServlet/old/{endpoint:str}", self.handle_old_srv), + Route("/{version:int}/MaimaiServlet/old/{endpoint:str}/{placeid:str}/{keychip:str}/{userid:int}", self.handle_old_srv_userdata), + Route("/{version:int}/MaimaiServlet/old/{endpoint:str}/{userid:int}", self.handle_old_srv_userdata), + Route("/{version:int}/MaimaiServlet/old/{endpoint:str}/{userid:int}", self.handle_old_srv_userdata), + Route("/{version:int}/MaimaiServlet/usbdl/{endpoint:str}", self.handle_usbdl), + Route("/{version:int}/MaimaiServlet/deliver/{endpoint:str}", self.handle_deliver), + Route("/{version:int}/MaimaiServlet/{endpoint:str}", self.handle_mai, methods=['POST']), + Route("/{version:int}/Maimai2Servlet/{endpoint:str}", self.handle_mai2, methods=['POST']), + ] + def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]: if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80: return ( - f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_ver}/", - f"{self.core_cfg.title.hostname}", + f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_ver}/", + f"{self.core_cfg.server.hostname}", ) return ( - f"http://{self.core_cfg.title.hostname}/{game_ver}/", - f"{self.core_cfg.title.hostname}", + f"http://{self.core_cfg.server.hostname}/{game_ver}/", + f"{self.core_cfg.server.hostname}", ) def setup(self): @@ -155,13 +151,22 @@ class Mai2Servlet(BaseServlet): f"Failed to make movie upload directory at {self.game_cfg.uploads.movies_dir}" ) - def handle_mai(self, request: Request, game_code: str, matchers: Dict) -> bytes: - endpoint = matchers['endpoint'] - version = int(matchers['version']) + async def handle_movie(self, request: Request): + return JSONResponse() + + async def handle_usbdl(self, request: Request): + return Response("OK") + + async def handle_deliver(self, request: Request): + return Response(status_code=404) + + async def handle_mai(self, request: Request) -> bytes: + endpoint: str = request.path_params.get('endpoint') + version: int = request.path_params.get('version') if endpoint.lower() == "ping": - return zlib.compress(b'{"returnCode": "1"}') + return Response(zlib.compress(b'{"returnCode": "1"}')) - req_raw = request.content.getvalue() + req_raw = await request.body() internal_ver = 0 client_ip = Utils.get_ip_addr(request) @@ -199,7 +204,7 @@ class Mai2Servlet(BaseServlet): self.logger.error( f"Failed to decompress v{version} {endpoint} request -> {e}" ) - return zlib.compress(b'{"stat": "0"}') + return Response(zlib.compress(b'{"stat": "0"}')) req_data = json.loads(unzip) @@ -216,26 +221,26 @@ class Mai2Servlet(BaseServlet): else: try: handler = getattr(handler_cls, func_to_find) - resp = handler(req_data) + resp = await handler(req_data) except Exception as e: self.logger.error(f"Error handling v{version} method {endpoint} - {e}") - return zlib.compress(b'{"returnCode": "0"}') + return Response(zlib.compress(b'{"returnCode": "0"}')) if resp == None: resp = {"returnCode": 1} self.logger.debug(f"Response {resp}") - return zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8")) + return Response(zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))) - def handle_mai2(self, request: Request, game_code: str, matchers: Dict) -> bytes: - endpoint = matchers['endpoint'] - version = int(matchers['version']) + async def handle_mai2(self, request: Request) -> bytes: + endpoint: str = request.path_params.get('endpoint') + version: int = request.path_params.get('version') if endpoint.lower() == "ping": - return zlib.compress(b'{"returnCode": "1"}') + return Response(zlib.compress(b'{"returnCode": "1"}')) - req_raw = request.content.getvalue() + req_raw = await request.body() internal_ver = 0 client_ip = Utils.get_ip_addr(request) if version < 105: # 1.0 @@ -256,17 +261,17 @@ class Mai2Servlet(BaseServlet): internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS if ( - request.getHeader("Mai-Encoding") is not None - or request.getHeader("X-Mai-Encoding") is not None + request.headers.get("Mai-Encoding") is not None + or request.headers.get("X-Mai-Encoding") is not None ): # The has is some flavor of MD5 of the endpoint with a constant bolted onto the end of it. # See cake.dll's Obfuscator function for details. Hopefully most DLL edits will remove # these two(?) headers to not cause issues, but given the general quality of SEGA data... - enc_ver = request.getHeader("Mai-Encoding") + enc_ver = request.headers.get("Mai-Encoding") if enc_ver is None: - enc_ver = request.getHeader("X-Mai-Encoding") + enc_ver = request.headers.get("X-Mai-Encoding") self.logger.debug( - f"Encryption v{enc_ver} - User-Agent: {request.getHeader('User-Agent')}" + f"Encryption v{enc_ver} - User-Agent: {request.headers.get('User-Agent')}" ) try: @@ -276,7 +281,7 @@ class Mai2Servlet(BaseServlet): self.logger.error( f"Failed to decompress v{version} {endpoint} request -> {e}" ) - return zlib.compress(b'{"stat": "0"}') + return Response(zlib.compress(b'{"stat": "0"}')) req_data = json.loads(unzip) @@ -293,80 +298,27 @@ class Mai2Servlet(BaseServlet): else: try: handler = getattr(handler_cls, func_to_find) - resp = handler(req_data) + resp = await handler(req_data) except Exception as e: self.logger.error(f"Error handling v{version} method {endpoint} - {e}") - return zlib.compress(b'{"stat": "0"}') + return Response(zlib.compress(b'{"stat": "0"}')) if resp == None: resp = {"returnCode": 1} self.logger.debug(f"Response {resp}") - return zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8")) + return Response(zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))) - def handle_old_srv(self, request: Request, game_code: str, matchers: Dict) -> bytes: - endpoint = matchers['endpoint'] - version = matchers['version'] + async def handle_old_srv(self, request: Request) -> bytes: + endpoint = request.path_params.get('endpoint') + version = request.path_params.get('version') self.logger.info(f"v{version} old server {endpoint}") - return zlib.compress(b"ok") + return Response(zlib.compress(b"ok")) - def handle_old_srv_userdata(self, request: Request, game_code: str, matchers: Dict) -> bytes: - endpoint = matchers['endpoint'] - version = matchers['version'] + async def handle_old_srv_userdata(self, request: Request) -> bytes: + endpoint = request.path_params.get('endpoint') + version = request.path_params.get('version') self.logger.info(f"v{version} old server {endpoint}") - return zlib.compress(b"{}") - - def render_GET(self, request: Request, version: int, url_path: str) -> bytes: - self.logger.debug(f"v{version} GET {url_path}") - url_split = url_path.split("/") - - if (url_split[0] == "api" and url_split[1] == "movie") or url_split[ - 0 - ] == "movie": - if url_split[2] == "moviestart": - return json.dumps({"moviestart": {"status": "OK"}}).encode() - - else: - request.setResponseCode(404) - return b"" - - elif url_split[0] == "usbdl": - if url_split[1] == "CONNECTIONTEST": - self.logger.info(f"v{version} usbdl server test") - return b"" - - elif self.game_cfg.deliver.udbdl_enable and path.exists( - f"{self.game_cfg.deliver.content_folder}/usb/{url_split[-1]}" - ): - with open( - f"{self.game_cfg.deliver.content_folder}/usb/{url_split[-1]}", "rb" - ) as f: - return f.read() - - else: - request.setResponseCode(404) - return b"" - - elif url_split[0] == "deliver": - file = url_split[len(url_split) - 1] - self.logger.info(f"v{version} {file} deliver inquire") - self.logger.debug( - f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}" - ) - - if self.game_cfg.deliver.enable and path.exists( - f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}" - ): - with open( - f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}", "rb" - ) as f: - return f.read() - - else: - request.setResponseCode(404) - return b"" - - else: - return zlib.compress(b"{}") + return Response(zlib.compress(b"{}")) diff --git a/titles/mai2/universe.py b/titles/mai2/universe.py index d25a295..a8d5b46 100644 --- a/titles/mai2/universe.py +++ b/titles/mai2/universe.py @@ -15,7 +15,7 @@ class Mai2Universe(Mai2SplashPlus): super().__init__(cfg, game_cfg) self.version = Mai2Constants.VER_MAIMAI_DX_UNIVERSE - def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: + async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: p = self.data.profile.get_profile_detail(data["userId"], self.version) if p is None: return {} @@ -30,7 +30,7 @@ class Mai2Universe(Mai2SplashPlus): "isExistSellingCard": True, } - def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict: + async def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict: # user already exists, because the preview checks that already p = self.data.profile.get_profile_detail(data["userId"], self.version) @@ -52,13 +52,13 @@ class Mai2Universe(Mai2SplashPlus): return {"userId": data["userId"], "userData": user_data} - def handle_cm_login_api_request(self, data: Dict) -> Dict: + async def handle_cm_login_api_request(self, data: Dict) -> Dict: return {"returnCode": 1} - def handle_cm_logout_api_request(self, data: Dict) -> Dict: + async def handle_cm_logout_api_request(self, data: Dict) -> Dict: return {"returnCode": 1} - def handle_cm_get_selling_card_api_request(self, data: Dict) -> Dict: + async def handle_cm_get_selling_card_api_request(self, data: Dict) -> Dict: selling_cards = self.data.static.get_enabled_cards(self.version) if selling_cards is None: return {"length": 0, "sellingCardList": []} @@ -88,7 +88,7 @@ class Mai2Universe(Mai2SplashPlus): return {"length": len(selling_card_list), "sellingCardList": selling_card_list} - def handle_cm_get_user_card_api_request(self, data: Dict) -> Dict: + async def handle_cm_get_user_card_api_request(self, data: Dict) -> Dict: user_cards = self.data.item.get_cards(data["userId"]) if user_cards is None: return {"returnCode": 1, "length": 0, "nextIndex": 0, "userCardList": []} @@ -124,10 +124,10 @@ class Mai2Universe(Mai2SplashPlus): "userCardList": card_list[start_idx:end_idx], } - def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict: + async def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict: super().handle_get_user_item_api_request(data) - def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict: + async def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict: characters = self.data.item.get_characters(data["userId"]) chara_list = [] @@ -153,10 +153,10 @@ class Mai2Universe(Mai2SplashPlus): "userCharacterList": chara_list, } - def handle_cm_get_user_card_print_error_api_request(self, data: Dict) -> Dict: + async def handle_cm_get_user_card_print_error_api_request(self, data: Dict) -> Dict: return {"length": 0, "userPrintDetailList": []} - def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict: + async def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict: user_id = data["userId"] upsert = data["userPrintDetail"] @@ -209,12 +209,12 @@ class Mai2Universe(Mai2SplashPlus): "endDate": datetime.strftime(end_date, Mai2Constants.DATE_TIME_FORMAT), } - def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict: + async def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict: return { "returnCode": 1, "orderId": 0, "serialId": data["userPrintlog"]["serialId"], } - def handle_cm_upsert_buy_card_api_request(self, data: Dict) -> Dict: + async def handle_cm_upsert_buy_card_api_request(self, data: Dict) -> Dict: return {"returnCode": 1} diff --git a/titles/mai2/universeplus.py b/titles/mai2/universeplus.py index e45c719..909300e 100644 --- a/titles/mai2/universeplus.py +++ b/titles/mai2/universeplus.py @@ -11,8 +11,8 @@ class Mai2UniversePlus(Mai2Universe): super().__init__(cfg, game_cfg) self.version = Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS - def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: - user_data = super().handle_cm_get_user_preview_api_request(data) + async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: + user_data = await super().handle_cm_get_user_preview_api_request(data) # hardcode lastDataVersion for CardMaker 1.35 user_data["lastDataVersion"] = "1.25.00" diff --git a/titles/ongeki/base.py b/titles/ongeki/base.py index 596fb22..2d50184 100644 --- a/titles/ongeki/base.py +++ b/titles/ongeki/base.py @@ -103,7 +103,7 @@ class OngekiBase: self.game = OngekiConstants.GAME_CODE self.version = OngekiConstants.VER_ONGEKI - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: # if reboot start/end time is not defined use the default behavior of being a few hours ago if self.core_cfg.title.reboot_start_time == "" or self.core_cfg.title.reboot_end_time == "": reboot_start = datetime.strftime( @@ -148,7 +148,7 @@ class OngekiBase: "isAou": "true", } - def handle_get_game_idlist_api_request(self, data: Dict) -> Dict: + async def handle_get_game_idlist_api_request(self, data: Dict) -> Dict: """ Gets lists of song IDs, either disabled songs or recomended songs depending on type? """ @@ -156,7 +156,7 @@ class OngekiBase: # id - int return {"type": data["type"], "length": 0, "gameIdlistList": []} - def handle_get_game_ranking_api_request(self, data: Dict) -> Dict: + async def handle_get_game_ranking_api_request(self, data: Dict) -> Dict: game_ranking_list = self.data.static.get_ranking_list(self.version) ranking_list = [] @@ -171,7 +171,7 @@ class OngekiBase: "gameRankingList": ranking_list, } - def handle_get_game_point_api_request(self, data: Dict) -> Dict: + async def handle_get_game_point_api_request(self, data: Dict) -> Dict: get_game_point = self.data.static.get_static_game_point() game_point = [] @@ -194,16 +194,16 @@ class OngekiBase: "gamePointList": game_point, } - def handle_game_login_api_request(self, data: Dict) -> Dict: + async def handle_game_login_api_request(self, data: Dict) -> Dict: return {"returnCode": 1, "apiName": "gameLogin"} - def handle_game_logout_api_request(self, data: Dict) -> Dict: + async def handle_game_logout_api_request(self, data: Dict) -> Dict: return {"returnCode": 1, "apiName": "gameLogout"} - def handle_extend_lock_time_api_request(self, data: Dict) -> Dict: + async def handle_extend_lock_time_api_request(self, data: Dict) -> Dict: return {"returnCode": 1, "apiName": "ExtendLockTimeApi"} - def handle_get_game_reward_api_request(self, data: Dict) -> Dict: + async def handle_get_game_reward_api_request(self, data: Dict) -> Dict: get_game_rewards = self.data.static.get_reward_list(self.version) reward_list = [] @@ -221,7 +221,7 @@ class OngekiBase: "gameRewardList": reward_list, } - def handle_get_game_present_api_request(self, data: Dict) -> Dict: + async def handle_get_game_present_api_request(self, data: Dict) -> Dict: get_present = self.data.static.get_present_list(self.version) present_list = [] @@ -238,13 +238,13 @@ class OngekiBase: "gamePresentList": present_list, } - def handle_get_game_message_api_request(self, data: Dict) -> Dict: + async def handle_get_game_message_api_request(self, data: Dict) -> Dict: return {"length": 0, "gameMessageList": []} - def handle_get_game_sale_api_request(self, data: Dict) -> Dict: + async def handle_get_game_sale_api_request(self, data: Dict) -> Dict: return {"length": 0, "gameSaleList": []} - def handle_get_game_tech_music_api_request(self, data: Dict) -> Dict: + async def handle_get_game_tech_music_api_request(self, data: Dict) -> Dict: music_list = self.data.static.get_tech_music(self.version) prep_music_list = [] @@ -262,7 +262,7 @@ class OngekiBase: "gameTechMusicList": prep_music_list, } - def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict: + async def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict: if self.core_cfg.server.is_develop: return {"returnCode": 1, "apiName": "UpsertClientSettingApi"} @@ -273,7 +273,7 @@ class OngekiBase: self.data.static.put_client_setting_data(cab['id'], client_setting_data) return {"returnCode": 1, "apiName": "UpsertClientSettingApi"} - def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict: + async def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict: if self.core_cfg.server.is_develop: return {"returnCode": 1, "apiName": "UpsertClientTestmodeApi"} @@ -282,16 +282,16 @@ class OngekiBase: self.data.static.put_client_testmode_data(region_id, client_testmode_data) return {"returnCode": 1, "apiName": "UpsertClientTestmodeApi"} - def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict: + async def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict: return {"returnCode": 1, "apiName": "upsertClientBookkeeping"} - def handle_upsert_client_develop_api_request(self, data: Dict) -> Dict: + async def handle_upsert_client_develop_api_request(self, data: Dict) -> Dict: return {"returnCode": 1, "apiName": "upsertClientDevelop"} - def handle_upsert_client_error_api_request(self, data: Dict) -> Dict: + async def handle_upsert_client_error_api_request(self, data: Dict) -> Dict: return {"returnCode": 1, "apiName": "upsertClientError"} - def handle_upsert_user_gplog_api_request(self, data: Dict) -> Dict: + async def handle_upsert_user_gplog_api_request(self, data: Dict) -> Dict: user = data["userId"] if user >= 200000000000000: # Account for guest play user = None @@ -309,10 +309,10 @@ class OngekiBase: return {"returnCode": 1, "apiName": "UpsertUserGplogApi"} - def handle_extend_lock_time_api_request(self, data: Dict) -> Dict: + async def handle_extend_lock_time_api_request(self, data: Dict) -> Dict: return {"returnCode": 1, "apiName": "ExtendLockTimeApi"} - def handle_get_game_event_api_request(self, data: Dict) -> Dict: + async def handle_get_game_event_api_request(self, data: Dict) -> Dict: evts = self.data.static.get_enabled_events(self.version) if evts is None: @@ -342,7 +342,7 @@ class OngekiBase: "gameEventList": evt_list, } - def handle_get_game_id_list_api_request(self, data: Dict) -> Dict: + async def handle_get_game_id_list_api_request(self, data: Dict) -> Dict: game_idlist: List[str, Any] = [] # 1 to 230 & 8000 to 8050 if data["type"] == 1: @@ -362,10 +362,10 @@ class OngekiBase: "gameIdlistList": game_idlist, } - def handle_get_user_region_api_request(self, data: Dict) -> Dict: + async def handle_get_user_region_api_request(self, data: Dict) -> Dict: return {"userId": data["userId"], "length": 0, "userRegionList": []} - def handle_get_user_preview_api_request(self, data: Dict) -> Dict: + async def handle_get_user_preview_api_request(self, data: Dict) -> Dict: profile = self.data.profile.get_profile_preview(data["userId"], self.version) if profile is None: @@ -417,7 +417,7 @@ class OngekiBase: "isWarningConfirmed": True, } - def handle_get_user_tech_count_api_request(self, data: Dict) -> Dict: + async def handle_get_user_tech_count_api_request(self, data: Dict) -> Dict: """ Gets the number of AB and ABPs a player has per-difficulty (7, 7+, 8, etc) The game sends this in upsert so we don't have to calculate it all out thankfully @@ -436,7 +436,7 @@ class OngekiBase: "userTechCountList": userTechCountList, } - def handle_get_user_tech_event_api_request(self, data: Dict) -> Dict: + async def handle_get_user_tech_event_api_request(self, data: Dict) -> Dict: user_tech_event_list = self.data.item.get_tech_event(self.version, data["userId"]) if user_tech_event_list is None: return {} @@ -455,7 +455,7 @@ class OngekiBase: "userTechEventList": tech_evt, } - def handle_get_user_tech_event_ranking_api_request(self, data: Dict) -> Dict: + async def handle_get_user_tech_event_ranking_api_request(self, data: Dict) -> Dict: user_tech_event_ranks = self.data.item.get_tech_event_ranking(self.version, data["userId"]) if user_tech_event_ranks is None: return { @@ -481,7 +481,7 @@ class OngekiBase: "userTechEventRankingList": evt_ranking, } - def handle_get_user_kop_api_request(self, data: Dict) -> Dict: + async def handle_get_user_kop_api_request(self, data: Dict) -> Dict: kop_list = self.data.profile.get_kop(data["userId"]) if kop_list is None: return {} @@ -496,7 +496,7 @@ class OngekiBase: "userKopList": kop_list, } - def handle_get_user_music_api_request(self, data: Dict) -> Dict: + async def handle_get_user_music_api_request(self, data: Dict) -> Dict: song_list = self.util_generate_music_list(data["userId"]) max_ct = data["maxCount"] next_idx = data["nextIndex"] @@ -516,7 +516,7 @@ class OngekiBase: "userMusicList": song_list[start_idx:end_idx], } - def handle_get_user_item_api_request(self, data: Dict) -> Dict: + async def handle_get_user_item_api_request(self, data: Dict) -> Dict: kind = data["nextIndex"] / 10000000000 p = self.data.item.get_items(data["userId"], kind) @@ -552,7 +552,7 @@ class OngekiBase: "userItemList": items, } - def handle_get_user_option_api_request(self, data: Dict) -> Dict: + async def handle_get_user_option_api_request(self, data: Dict) -> Dict: o = self.data.profile.get_profile_options(data["userId"]) if o is None: return {} @@ -566,7 +566,7 @@ class OngekiBase: return {"userId": data["userId"], "userOption": user_opts} - def handle_get_user_data_api_request(self, data: Dict) -> Dict: + async def handle_get_user_data_api_request(self, data: Dict) -> Dict: p = self.data.profile.get_profile_data(data["userId"], self.version) if p is None: return {} @@ -594,7 +594,7 @@ class OngekiBase: return {"userId": data["userId"], "userData": user_data} - def handle_get_user_event_ranking_api_request(self, data: Dict) -> Dict: + async def handle_get_user_event_ranking_api_request(self, data: Dict) -> Dict: user_event_ranking_list = self.data.item.get_ranking_event_ranks(self.version, data["userId"]) if user_event_ranking_list is None: return {} @@ -617,7 +617,7 @@ class OngekiBase: "userEventRankingList": prep_event_ranking, } - def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict: + async def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict: user_login_bonus_list = self.data.item.get_login_bonuses(data["userId"]) if user_login_bonus_list is None: return {} @@ -635,7 +635,7 @@ class OngekiBase: "userLoginBonusList": login_bonuses, } - def handle_get_user_bp_base_request(self, data: Dict) -> Dict: + async def handle_get_user_bp_base_request(self, data: Dict) -> Dict: p = self.data.profile.get_profile( self.game, self.version, user_id=data["userId"] ) @@ -648,7 +648,7 @@ class OngekiBase: "userBpBaseList": profile["userBpBaseList"], } - def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict: + async def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict: recent_rating = self.data.profile.get_profile_recent_rating(data["userId"]) if recent_rating is None: return { @@ -665,7 +665,7 @@ class OngekiBase: "userRecentRatingList": userRecentRatingList, } - def handle_get_user_activity_api_request(self, data: Dict) -> Dict: + async def handle_get_user_activity_api_request(self, data: Dict) -> Dict: activity = self.data.profile.get_profile_activity(data["userId"], data["kind"]) if activity is None: return {} @@ -692,7 +692,7 @@ class OngekiBase: "userActivityList": user_activity, } - def handle_get_user_story_api_request(self, data: Dict) -> Dict: + async def handle_get_user_story_api_request(self, data: Dict) -> Dict: user_stories = self.data.item.get_stories(data["userId"]) if user_stories is None: return {} @@ -710,7 +710,7 @@ class OngekiBase: "userStoryList": story_list, } - def handle_get_user_chapter_api_request(self, data: Dict) -> Dict: + async def handle_get_user_chapter_api_request(self, data: Dict) -> Dict: user_chapters = self.data.item.get_chapters(data["userId"]) if user_chapters is None: return {} @@ -728,14 +728,14 @@ class OngekiBase: "userChapterList": chapter_list, } - def handle_get_user_training_room_by_key_api_request(self, data: Dict) -> Dict: + async def handle_get_user_training_room_by_key_api_request(self, data: Dict) -> Dict: return { "userId": data["userId"], "length": 0, "userTrainingRoomList": [], } - def handle_get_user_character_api_request(self, data: Dict) -> Dict: + async def handle_get_user_character_api_request(self, data: Dict) -> Dict: user_characters = self.data.item.get_characters(data["userId"]) if user_characters is None: return {} @@ -753,7 +753,7 @@ class OngekiBase: "userCharacterList": character_list, } - def handle_get_user_card_api_request(self, data: Dict) -> Dict: + async def handle_get_user_card_api_request(self, data: Dict) -> Dict: user_cards = self.data.item.get_cards(data["userId"]) if user_cards is None: return {} @@ -771,7 +771,7 @@ class OngekiBase: "userCardList": card_list, } - def handle_get_user_deck_by_key_api_request(self, data: Dict) -> Dict: + async def handle_get_user_deck_by_key_api_request(self, data: Dict) -> Dict: # Auth key doesn't matter, it just wants all the decks decks = self.data.item.get_decks(data["userId"]) if decks is None: @@ -790,7 +790,7 @@ class OngekiBase: "userDeckList": deck_list, } - def handle_get_user_trade_item_api_request(self, data: Dict) -> Dict: + async def handle_get_user_trade_item_api_request(self, data: Dict) -> Dict: user_trade_items = self.data.item.get_trade_items(data["userId"]) if user_trade_items is None: return {} @@ -808,7 +808,7 @@ class OngekiBase: "userTradeItemList": trade_item_list, } - def handle_get_user_scenario_api_request(self, data: Dict) -> Dict: + async def handle_get_user_scenario_api_request(self, data: Dict) -> Dict: user_scenerio = self.data.item.get_scenerios(data["userId"]) if user_scenerio is None: return {} @@ -826,7 +826,7 @@ class OngekiBase: "userScenarioList": scenerio_list, } - def handle_get_user_ratinglog_api_request(self, data: Dict) -> Dict: + async def handle_get_user_ratinglog_api_request(self, data: Dict) -> Dict: rating_log = self.data.profile.get_profile_rating_log(data["userId"]) if rating_log is None: return {} @@ -844,7 +844,7 @@ class OngekiBase: "userRatinglogList": userRatinglogList, } - def handle_get_user_mission_point_api_request(self, data: Dict) -> Dict: + async def handle_get_user_mission_point_api_request(self, data: Dict) -> Dict: user_mission_point_list = self.data.item.get_mission_points(self.version, data["userId"]) if user_mission_point_list is None: return {} @@ -864,7 +864,7 @@ class OngekiBase: "userMissionPointList": mission_point_list, } - def handle_get_user_event_point_api_request(self, data: Dict) -> Dict: + async def handle_get_user_event_point_api_request(self, data: Dict) -> Dict: user_event_point_list = self.data.item.get_event_points(data["userId"]) if user_event_point_list is None: return {} @@ -886,7 +886,7 @@ class OngekiBase: "userEventPointList": event_point_list, } - def handle_get_user_music_item_api_request(self, data: Dict) -> Dict: + async def handle_get_user_music_item_api_request(self, data: Dict) -> Dict: user_music_item_list = self.data.item.get_music_items(data["userId"]) if user_music_item_list is None: return {} @@ -904,7 +904,7 @@ class OngekiBase: "userMusicItemList": music_item_list, } - def handle_get_user_event_music_api_request(self, data: Dict) -> Dict: + async def handle_get_user_event_music_api_request(self, data: Dict) -> Dict: user_evt_music_list = self.data.item.get_event_music(data["userId"]) if user_evt_music_list is None: return {} @@ -922,7 +922,7 @@ class OngekiBase: "userEventMusicList": evt_music_list, } - def handle_get_user_boss_api_request(self, data: Dict) -> Dict: + async def handle_get_user_boss_api_request(self, data: Dict) -> Dict: p = self.data.item.get_bosses(data["userId"]) if p is None: return {} @@ -940,7 +940,7 @@ class OngekiBase: "userBossList": boss_list, } - def handle_upsert_user_all_api_request(self, data: Dict) -> Dict: + async def handle_upsert_user_all_api_request(self, data: Dict) -> Dict: upsert = data["upsertUserAll"] user_id = data["userId"] @@ -1070,7 +1070,7 @@ class OngekiBase: return {"returnCode": 1, "apiName": "upsertUserAll"} - def handle_get_user_rival_api_request(self, data: Dict) -> Dict: + async def handle_get_user_rival_api_request(self, data: Dict) -> Dict: """ Added in Bright """ @@ -1094,7 +1094,7 @@ class OngekiBase: "userRivalList": rival_list, } - def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict: + async def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict: """ Added in Bright """ @@ -1112,7 +1112,7 @@ class OngekiBase: "userRivalDataList": rivals, } - def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict: + async def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict: """ Added in Bright """ diff --git a/titles/ongeki/bright.py b/titles/ongeki/bright.py index 49d6216..0af5127 100644 --- a/titles/ongeki/bright.py +++ b/titles/ongeki/bright.py @@ -15,13 +15,13 @@ class OngekiBright(OngekiBase): super().__init__(core_cfg, game_cfg) self.version = OngekiConstants.VER_ONGEKI_BRIGHT - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: - ret = super().handle_get_game_setting_api_request(data) + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + ret = await super().handle_get_game_setting_api_request(data) ret["gameSetting"]["dataVersion"] = "1.30.00" ret["gameSetting"]["onlineDataVersion"] = "1.30.00" return ret - def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict: + async def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict: # check for a bright profile p = self.data.profile.get_profile_data(data["userId"], self.version) if p is None: @@ -55,13 +55,13 @@ class OngekiBright(OngekiBase): return {"userId": data["userId"], "userData": user_data} - def handle_printer_login_api_request(self, data: Dict): + async def handle_printer_login_api_request(self, data: Dict): return {"returnCode": 1} - def handle_printer_logout_api_request(self, data: Dict): + async def handle_printer_logout_api_request(self, data: Dict): return {"returnCode": 1} - def handle_cm_get_user_card_api_request(self, data: Dict) -> Dict: + async def handle_cm_get_user_card_api_request(self, data: Dict) -> Dict: user_cards = self.data.item.get_cards(data["userId"]) if user_cards is None: return {} @@ -90,7 +90,7 @@ class OngekiBright(OngekiBase): "userCardList": card_list[start_idx:end_idx], } - def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict: + async def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict: user_characters = self.data.item.get_characters(data["userId"]) if user_characters is None: return { @@ -124,7 +124,7 @@ class OngekiBright(OngekiBase): "userCharacterList": character_list[start_idx:end_idx], } - def handle_get_user_gacha_api_request(self, data: Dict) -> Dict: + async def handle_get_user_gacha_api_request(self, data: Dict) -> Dict: user_gachas = self.data.item.get_user_gachas(data["userId"]) if user_gachas is None: return {"userId": data["userId"], "length": 0, "userGachaList": []} @@ -143,10 +143,10 @@ class OngekiBright(OngekiBase): "userGachaList": user_gacha_list, } - def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict: + async def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict: return self.handle_get_user_item_api_request(data) - def handle_cm_get_user_gacha_supply_api_request(self, data: Dict) -> Dict: + async def handle_cm_get_user_gacha_supply_api_request(self, data: Dict) -> Dict: # not used for now? not sure what it even does user_gacha_supplies = self.data.item.get_user_gacha_supplies(data["userId"]) if user_gacha_supplies is None: @@ -160,7 +160,7 @@ class OngekiBright(OngekiBase): "supplyCardList": supply_list, } - def handle_get_game_gacha_api_request(self, data: Dict) -> Dict: + async def handle_get_game_gacha_api_request(self, data: Dict) -> Dict: """ returns all current active banners (gachas) "Select Gacha" requires maxSelectPoint set and isCeiling set to 1 @@ -207,7 +207,7 @@ class OngekiBright(OngekiBase): "registIdList": [], } - def handle_roll_gacha_api_request(self, data: Dict) -> Dict: + async def handle_roll_gacha_api_request(self, data: Dict) -> Dict: """ Handle a gacha roll API request """ @@ -323,7 +323,7 @@ class OngekiBright(OngekiBase): "gameGachaCardList": game_gacha_card_list, } - def handle_cm_upsert_user_gacha_api_request(self, data: Dict): + async def handle_cm_upsert_user_gacha_api_request(self, data: Dict): upsert = data["cmUpsertUserGacha"] user_id = data["userId"] @@ -405,7 +405,7 @@ class OngekiBright(OngekiBase): return {"returnCode": 1, "apiName": "CMUpsertUserGachaApi"} - def handle_cm_upsert_user_select_gacha_api_request(self, data: Dict) -> Dict: + async def handle_cm_upsert_user_select_gacha_api_request(self, data: Dict) -> Dict: upsert = data["cmUpsertUserSelectGacha"] user_id = data["userId"] @@ -442,7 +442,7 @@ class OngekiBright(OngekiBase): return {"returnCode": 1, "apiName": "cmUpsertUserSelectGacha"} - def handle_get_game_gacha_card_by_id_api_request(self, data: Dict) -> Dict: + async def handle_get_game_gacha_card_by_id_api_request(self, data: Dict) -> Dict: game_gacha_cards = self.data.static.get_gacha_cards(data["gachaId"]) if game_gacha_cards == []: # fallback to be at least able to select that gacha @@ -522,7 +522,7 @@ class OngekiBright(OngekiBase): "ssrBookCalcList": [], } - def handle_get_game_theater_api_request(self, data: Dict) -> Dict: + async def handle_get_game_theater_api_request(self, data: Dict) -> Dict: """ shows a banner after every print, not sure what its used for """ @@ -548,7 +548,7 @@ class OngekiBright(OngekiBase): return {"length": 0, "gameTheaterList": [], "registIdList": []} - def handle_cm_upsert_user_print_playlog_api_request(self, data: Dict) -> Dict: + async def handle_cm_upsert_user_print_playlog_api_request(self, data: Dict) -> Dict: return { "returnCode": 1, "orderId": 0, @@ -556,7 +556,7 @@ class OngekiBright(OngekiBase): "apiName": "CMUpsertUserPrintPlaylogApi", } - def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict: + async def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict: return { "returnCode": 1, "orderId": 0, @@ -564,7 +564,7 @@ class OngekiBright(OngekiBase): "apiName": "CMUpsertUserPrintlogApi", } - def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict: + async def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict: user_print_detail = data["userPrintDetail"] # generate random serial id @@ -589,7 +589,7 @@ class OngekiBright(OngekiBase): "apiName": "CMUpsertUserPrintApi", } - def handle_cm_upsert_user_all_api_request(self, data: Dict) -> Dict: + async def handle_cm_upsert_user_all_api_request(self, data: Dict) -> Dict: upsert = data["cmUpsertUserAll"] user_id = data["userId"] diff --git a/titles/ongeki/brightmemory.py b/titles/ongeki/brightmemory.py index d7103a3..0bff264 100644 --- a/titles/ongeki/brightmemory.py +++ b/titles/ongeki/brightmemory.py @@ -15,8 +15,8 @@ class OngekiBrightMemory(OngekiBright): super().__init__(core_cfg, game_cfg) self.version = OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: - ret = super().handle_get_game_setting_api_request(data) + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + ret = await super().handle_get_game_setting_api_request(data) ret["gameSetting"]["dataVersion"] = "1.35.00" ret["gameSetting"]["onlineDataVersion"] = "1.35.00" ret["gameSetting"]["maxCountCharacter"] = 50 @@ -27,7 +27,7 @@ class OngekiBrightMemory(OngekiBright): ret["gameSetting"]["maxCountRivalMusic"] = 300 return ret - def handle_get_user_memory_chapter_api_request(self, data: Dict) -> Dict: + async def handle_get_user_memory_chapter_api_request(self, data: Dict) -> Dict: memories = self.data.item.get_memorychapters(data["userId"]) if not memories: return { @@ -134,5 +134,5 @@ class OngekiBrightMemory(OngekiBright): "userMemoryChapterList": memory_chp, } - def handle_get_game_music_release_state_api_request(self, data: Dict) -> Dict: + async def handle_get_game_music_release_state_api_request(self, data: Dict) -> Dict: return {"techScore": 0, "cardNum": 0} diff --git a/titles/ongeki/frontend.py b/titles/ongeki/frontend.py index 987776f..4c17165 100644 --- a/titles/ongeki/frontend.py +++ b/titles/ongeki/frontend.py @@ -1,6 +1,6 @@ import yaml import jinja2 -from twisted.web.http import Request +from starlette.requests import Request from os import path from twisted.web.util import redirectTo from twisted.web.server import Session diff --git a/titles/ongeki/index.py b/titles/ongeki/index.py index baf1ee3..f80214b 100644 --- a/titles/ongeki/index.py +++ b/titles/ongeki/index.py @@ -1,4 +1,6 @@ -from twisted.web.http import Request +from starlette.requests import Request +from starlette.routing import Route +from starlette.responses import Response import json import inflection import yaml @@ -120,35 +122,28 @@ class OngekiServlet(BaseServlet): return True - def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]: - return ( - [], - [("render_POST", "/SDDT/{version}/{endpoint}", {})] - ) + def get_routes(self) -> List[Route]: + return [ + Route("/SDDT/{version:int}/{endpoint:str}", self.render_POST, methods=['POST']) + ] def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]: title_port_int = Utils.get_title_port(self.core_cfg) - title_port_ssl_int = Utils.get_title_port_ssl(self.core_cfg) proto = "https" if self.game_cfg.server.use_https and game_ver >= 120 else "http" - - if proto == "https": - t_port = f":{title_port_ssl_int}" if title_port_ssl_int and not self.core_cfg.server.is_using_proxy else "" - - else: - t_port = f":{title_port_int}" if title_port_int and not self.core_cfg.server.is_using_proxy else "" + t_port = f":{title_port_int}" if title_port_int and not self.core_cfg.server.is_using_proxy else "" return ( - f"{proto}://{self.core_cfg.title.hostname}{t_port}/{game_code}/{game_ver}/", - f"{self.core_cfg.title.hostname}{t_port}/", + f"{proto}://{self.core_cfg.server.hostname}{t_port}/{game_code}/{game_ver}/", + f"{self.core_cfg.server.hostname}{t_port}/", ) - def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes: - endpoint = matchers['endpoint'] - version = int(matchers['version']) + async def render_POST(self, request: Request) -> bytes: + endpoint: str = request.path_params.get('endpoint', '') + version: int = request.path_params.get('version', 0) if endpoint.lower() == "ping": - return zlib.compress(b'{"returnCode": 1}') + return Response(zlib.compress(b'{"returnCode": 1}')) - req_raw = request.content.getvalue() + req_raw = await request.body() encrtped = False internal_ver = 0 client_ip = Utils.get_ip_addr(request) @@ -178,13 +173,13 @@ class OngekiServlet(BaseServlet): self.logger.error( f"v{version} does not support encryption or no keys entered" ) - return zlib.compress(b'{"stat": "0"}') + return Response(zlib.compress(b'{"stat": "0"}')) elif endpoint.lower() not in self.hash_table[internal_ver]: self.logger.error( f"No hash found for v{version} endpoint {endpoint}" ) - return zlib.compress(b'{"stat": "0"}') + return Response(zlib.compress(b'{"stat": "0"}')) endpoint = self.hash_table[internal_ver][endpoint.lower()] @@ -201,7 +196,7 @@ class OngekiServlet(BaseServlet): self.logger.error( f"Failed to decrypt v{version} request to {endpoint} -> {e}" ) - return zlib.compress(b'{"stat": "0"}') + return Response(zlib.compress(b'{"stat": "0"}')) encrtped = True @@ -213,7 +208,7 @@ class OngekiServlet(BaseServlet): self.logger.error( f"Unencrypted v{version} {endpoint} request, but config is set to encrypted only: {req_raw}" ) - return zlib.compress(b'{"stat": "0"}') + return Response(zlib.compress(b'{"stat": "0"}')) try: unzip = zlib.decompress(req_raw) @@ -222,7 +217,7 @@ class OngekiServlet(BaseServlet): self.logger.error( f"Failed to decompress v{version} {endpoint} request -> {e}" ) - return zlib.compress(b'{"stat": "0"}') + return Response(zlib.compress(b'{"stat": "0"}')) req_data = json.loads(unzip) @@ -235,15 +230,15 @@ class OngekiServlet(BaseServlet): if not hasattr(self.versions[internal_ver], func_to_find): self.logger.warning(f"Unhandled v{version} request {endpoint}") - return zlib.compress(b'{"returnCode": 1}') + return Response(zlib.compress(b'{"returnCode": 1}')) try: handler = getattr(self.versions[internal_ver], func_to_find) - resp = handler(req_data) + resp = await handler(req_data) except Exception as e: self.logger.error(f"Error handling v{version} method {endpoint} - {e}") - return zlib.compress(b'{"stat": "0"}') + return Response(zlib.compress(b'{"stat": "0"}')) if resp == None: resp = {"returnCode": 1} @@ -253,7 +248,7 @@ class OngekiServlet(BaseServlet): zipped = zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8")) if not encrtped or version < 120: - return zipped + return Response(zipped) padded = pad(zipped, 16) @@ -263,4 +258,4 @@ class OngekiServlet(BaseServlet): bytes.fromhex(self.game_cfg.crypto.keys[internal_ver][1]), ) - return crypt.encrypt(padded) \ No newline at end of file + return Response(crypt.encrypt(padded)) \ No newline at end of file diff --git a/titles/ongeki/plus.py b/titles/ongeki/plus.py index 9168576..12c46b2 100644 --- a/titles/ongeki/plus.py +++ b/titles/ongeki/plus.py @@ -11,8 +11,8 @@ class OngekiPlus(OngekiBase): super().__init__(core_cfg, game_cfg) self.version = OngekiConstants.VER_ONGEKI_PLUS - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: - ret = super().handle_get_game_setting_api_request(data) + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + ret = await super().handle_get_game_setting_api_request(data) ret["gameSetting"]["dataVersion"] = "1.05.00" ret["gameSetting"]["onlineDataVersion"] = "1.05.00" return ret diff --git a/titles/ongeki/red.py b/titles/ongeki/red.py index 52b9d59..eac20ae 100644 --- a/titles/ongeki/red.py +++ b/titles/ongeki/red.py @@ -11,8 +11,8 @@ class OngekiRed(OngekiBase): super().__init__(core_cfg, game_cfg) self.version = OngekiConstants.VER_ONGEKI_RED - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: - ret = super().handle_get_game_setting_api_request(data) + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + ret = await super().handle_get_game_setting_api_request(data) ret["gameSetting"]["dataVersion"] = "1.20.00" ret["gameSetting"]["onlineDataVersion"] = "1.20.00" return ret diff --git a/titles/ongeki/redplus.py b/titles/ongeki/redplus.py index 1f69690..99e63db 100644 --- a/titles/ongeki/redplus.py +++ b/titles/ongeki/redplus.py @@ -11,8 +11,8 @@ class OngekiRedPlus(OngekiBase): super().__init__(core_cfg, game_cfg) self.version = OngekiConstants.VER_ONGEKI_RED_PLUS - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: - ret = super().handle_get_game_setting_api_request(data) + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + ret = await super().handle_get_game_setting_api_request(data) ret["gameSetting"]["dataVersion"] = "1.25.00" ret["gameSetting"]["onlineDataVersion"] = "1.25.00" ret["gameSetting"]["maxCountCharacter"] = 50 diff --git a/titles/ongeki/summer.py b/titles/ongeki/summer.py index adc8c0f..c3f2ce0 100644 --- a/titles/ongeki/summer.py +++ b/titles/ongeki/summer.py @@ -11,8 +11,8 @@ class OngekiSummer(OngekiBase): super().__init__(core_cfg, game_cfg) self.version = OngekiConstants.VER_ONGEKI_SUMMER - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: - ret = super().handle_get_game_setting_api_request(data) + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + ret = await super().handle_get_game_setting_api_request(data) ret["gameSetting"]["dataVersion"] = "1.10.00" ret["gameSetting"]["onlineDataVersion"] = "1.10.00" return ret diff --git a/titles/ongeki/summerplus.py b/titles/ongeki/summerplus.py index 8b2cd03..576d615 100644 --- a/titles/ongeki/summerplus.py +++ b/titles/ongeki/summerplus.py @@ -11,8 +11,8 @@ class OngekiSummerPlus(OngekiBase): super().__init__(core_cfg, game_cfg) self.version = OngekiConstants.VER_ONGEKI_SUMMER_PLUS - def handle_get_game_setting_api_request(self, data: Dict) -> Dict: - ret = super().handle_get_game_setting_api_request(data) + async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: + ret = await super().handle_get_game_setting_api_request(data) ret["gameSetting"]["dataVersion"] = "1.15.00" ret["gameSetting"]["onlineDataVersion"] = "1.15.00" return ret diff --git a/titles/pokken/base.py b/titles/pokken/base.py index 9663e81..9eab298 100644 --- a/titles/pokken/base.py +++ b/titles/pokken/base.py @@ -20,21 +20,21 @@ class PokkenBase: self.data = PokkenData(core_cfg) self.SUPPORT_SET_NONE = 4294967295 - def handle_noop(self, request: Any) -> bytes: + async def handle_noop(self, request: Any) -> bytes: res = jackal_pb2.Response() res.result = 1 res.type = request.type return res.SerializeToString() - def handle_ping(self, request: jackal_pb2.Request) -> bytes: + async def handle_ping(self, request: jackal_pb2.Request) -> bytes: res = jackal_pb2.Response() res.result = 1 res.type = jackal_pb2.MessageType.PING return res.SerializeToString() - def handle_register_pcb(self, request: jackal_pb2.Request) -> bytes: + async def handle_register_pcb(self, request: jackal_pb2.Request) -> bytes: res = jackal_pb2.Response() res.result = 1 res.type = jackal_pb2.MessageType.REGISTER_PCB @@ -61,35 +61,35 @@ class PokkenBase: "logfilename": "JackalMatchingLibrary.log", "biwalogfilename": "./biwa.log", } - regist_pcb.bnp_baseuri = f"{self.core_cfg.title.hostname}/bna" + regist_pcb.bnp_baseuri = f"{self.core_cfg.server.hostname}/bna" regist_pcb.biwa_setting = json.dumps(biwa_setting) res.register_pcb.CopyFrom(regist_pcb) return res.SerializeToString() - def handle_save_ads(self, request: jackal_pb2.Request) -> bytes: + async def handle_save_ads(self, request: jackal_pb2.Request) -> bytes: res = jackal_pb2.Response() res.result = 1 res.type = jackal_pb2.MessageType.SAVE_ADS return res.SerializeToString() - def handle_save_client_log(self, request: jackal_pb2.Request) -> bytes: + async def handle_save_client_log(self, request: jackal_pb2.Request) -> bytes: res = jackal_pb2.Response() res.result = 1 res.type = jackal_pb2.MessageType.SAVE_CLIENT_LOG return res.SerializeToString() - def handle_check_diagnosis(self, request: jackal_pb2.Request) -> bytes: + async def handle_check_diagnosis(self, request: jackal_pb2.Request) -> bytes: res = jackal_pb2.Response() res.result = 1 res.type = jackal_pb2.MessageType.CHECK_DIAGNOSIS return res.SerializeToString() - def handle_load_client_settings(self, request: jackal_pb2.Request) -> bytes: + async def handle_load_client_settings(self, request: jackal_pb2.Request) -> bytes: res = jackal_pb2.Response() res.result = 1 res.type = jackal_pb2.MessageType.LOAD_CLIENT_SETTINGS @@ -112,7 +112,7 @@ class PokkenBase: return res.SerializeToString() - def handle_load_ranking(self, request: jackal_pb2.Request) -> bytes: + async def handle_load_ranking(self, request: jackal_pb2.Request) -> bytes: res = jackal_pb2.Response() res.result = 1 res.type = jackal_pb2.MessageType.LOAD_RANKING @@ -126,7 +126,7 @@ class PokkenBase: res.load_ranking.CopyFrom(ranking) return res.SerializeToString() - def handle_load_user(self, request: jackal_pb2.Request) -> bytes: + async def handle_load_user(self, request: jackal_pb2.Request) -> bytes: res = jackal_pb2.Response() res.result = 1 res.type = jackal_pb2.MessageType.LOAD_USER @@ -287,13 +287,13 @@ class PokkenBase: res.load_user.CopyFrom(load_usr) return res.SerializeToString() - def handle_set_bnpassid_lock(self, data: jackal_pb2.Request) -> bytes: + async def handle_set_bnpassid_lock(self, data: jackal_pb2.Request) -> bytes: res = jackal_pb2.Response() res.result = 1 res.type = jackal_pb2.MessageType.SET_BNPASSID_LOCK return res.SerializeToString() - def handle_save_user(self, request: jackal_pb2.Request) -> bytes: + async def handle_save_user(self, request: jackal_pb2.Request) -> bytes: res = jackal_pb2.Response() res.result = 1 res.type = jackal_pb2.MessageType.SAVE_USER @@ -394,38 +394,31 @@ class PokkenBase: return res.SerializeToString() - def handle_save_ingame_log(self, data: jackal_pb2.Request) -> bytes: + async def handle_save_ingame_log(self, data: jackal_pb2.Request) -> bytes: res = jackal_pb2.Response() res.result = 1 res.type = jackal_pb2.MessageType.SAVE_INGAME_LOG return res.SerializeToString() - def handle_save_charge(self, data: jackal_pb2.Request) -> bytes: + async def handle_save_charge(self, data: jackal_pb2.Request) -> bytes: res = jackal_pb2.Response() res.result = 1 res.type = jackal_pb2.MessageType.SAVE_CHARGE return res.SerializeToString() - def handle_matching_noop( + async def handle_matching_noop( self, data: Dict = {}, client_ip: str = "127.0.0.1" ) -> Dict: return {} - def handle_matching_start_matching( + async def handle_matching_start_matching( self, data: Dict = {}, client_ip: str = "127.0.0.1" ) -> Dict: return {} - def handle_matching_is_matching( + async def handle_matching_is_matching( self, data: Dict = {}, client_ip: str = "127.0.0.1" ) -> Dict: - """ - "sessionId":"12345678", - "A":{ - "pcb_id": data["data"]["must"]["pcb_id"], - "gip": client_ip - }, - """ return { "data": { "sessionId":"12345678", @@ -437,15 +430,15 @@ class PokkenBase: } } - def handle_matching_stop_matching( + async def handle_matching_stop_matching( self, data: Dict = {}, client_ip: str = "127.0.0.1" ) -> Dict: return {} - def handle_admission_noop(self, data: Dict, req_ip: str = "127.0.0.1") -> Dict: + async def handle_admission_noop(self, data: Dict, req_ip: str = "127.0.0.1") -> Dict: return {} - def handle_admission_joinsession(self, data: Dict, req_ip: str = "127.0.0.1") -> Dict: + async def handle_admission_joinsession(self, data: Dict, req_ip: str = "127.0.0.1") -> Dict: self.logger.info(f"Admission: JoinSession from {req_ip}") return { 'data': { diff --git a/titles/pokken/const.py b/titles/pokken/const.py index 9fa3b06..4983d50 100644 --- a/titles/pokken/const.py +++ b/titles/pokken/const.py @@ -3,6 +3,7 @@ from enum import Enum class PokkenConstants: GAME_CODE = "SDAK" + GAME_CDS = ["PKF1"] CONFIG_NAME = "pokken.yaml" @@ -10,6 +11,12 @@ class PokkenConstants: VERSION_NAMES = "Pokken Tournament" + SERIAL_IDENT = [2747] + NETID_PREFIX = ["ABGN"] + SERIAL_REGIONS = [1] + SERIAL_ROLES = [3] + SERIAL_CAB_IDENTS = [19] + class BATTLE_TYPE(Enum): TUTORIAL = 1 AI = 2 diff --git a/titles/pokken/frontend.py b/titles/pokken/frontend.py index af344dc..be22c50 100644 --- a/titles/pokken/frontend.py +++ b/titles/pokken/frontend.py @@ -1,6 +1,6 @@ import yaml import jinja2 -from twisted.web.http import Request +from starlette.requests import Request from os import path from twisted.web.server import Session diff --git a/titles/pokken/index.py b/titles/pokken/index.py index 30b6617..ffd916c 100644 --- a/titles/pokken/index.py +++ b/titles/pokken/index.py @@ -1,8 +1,10 @@ from typing import Tuple, List, Dict -from twisted.web.http import Request -from twisted.web import resource -from twisted.internet import reactor -import json, ast +from starlette.requests import Request +from starlette.requests import Request +from starlette.responses import Response, JSONResponse +from starlette.routing import Route, WebSocketRoute +from starlette.websockets import WebSocket, WebSocketState, WebSocketDisconnect +import ast from datetime import datetime import yaml import logging, coloredlogs @@ -17,8 +19,6 @@ from .config import PokkenConfig from .base import PokkenBase from .const import PokkenConstants from .proto import jackal_pb2 -from .services import PokkenAdmissionFactory - class PokkenServlet(BaseServlet): def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None: @@ -69,47 +69,73 @@ class PokkenServlet(BaseServlet): return True - def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]: - return ( - [], - [ - ("render_POST", "/pokken/", {}), - ("handle_matching", "/pokken/matching", {}), - ] - ) + def get_routes(self) -> List[Route]: + return [ + Route("/pokken/", self.render_POST, methods=['POST']), + Route("/pokken/matching", self.handle_matching, methods=['POST']), + WebSocketRoute("/pokken/admission", self.handle_admission) + ] def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]: - if self.game_cfg.ports.game != 443: - return ( - f"https://{self.game_cfg.server.hostname}:{self.game_cfg.ports.game}/pokken/", - f"{self.game_cfg.server.hostname}/pokken/", - ) return ( - f"https://{self.game_cfg.server.hostname}/pokken/", - f"{self.game_cfg.server.hostname}/pokken/", + f"https://{self.game_cfg.server.hostname}:{self.game_cfg.ports.game}/pokken/", + f"{self.game_cfg.server.hostname}:{self.game_cfg.ports.game}/pokken/", ) def get_mucha_info(self, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, str]: - game_cfg = PokkenConfig() + if not self.game_cfg.server.enable: + return (False, [], []) - if path.exists(f"{cfg_dir}/{PokkenConstants.CONFIG_NAME}"): - game_cfg.update( - yaml.safe_load(open(f"{cfg_dir}/{PokkenConstants.CONFIG_NAME}")) - ) + return (True, PokkenConstants.GAME_CDS, PokkenConstants.NETID_PREFIX) + + async def handle_admission(self, ws: WebSocket) -> None: + client_ip = Utils.get_ip_addr(ws) + await ws.accept() + while True: + try: + msg: Dict = await ws.receive_json() + except WebSocketDisconnect as e: + self.logger.debug(f"Client {client_ip} disconnected - {e}") + break + except Exception as e: + self.logger.error(f"Could not load JSON from message from {client_ip} - {e}") + if ws.client_state != WebSocketState.DISCONNECTED: + await ws.close() + break + + self.logger.debug(f"Admission: Message from {client_ip}:{ws.client.port} - {msg}") + + api = msg.get("api", "noop") + handler = getattr(self.base, f"handle_admission_{api.lower()}") + resp = await handler(msg, client_ip) + + if resp is None: + resp = {} - if not game_cfg.server.enable: - return (False, "") + if "type" not in resp: + resp['type'] = "res" + if "data" not in resp: + resp['data'] = {} + if "api" not in resp: + resp['api'] = api + if "result" not in resp: + resp['result'] = 'true' + + self.logger.debug(f"Websocket response: {resp}") + try: + await ws.send_json(resp) + except WebSocketDisconnect as e: + self.logger.debug(f"Client {client_ip} disconnected - {e}") + break + except Exception as e: + self.logger.error(f"Could not send JSON message to {client_ip} - {e}") + break + + if ws.client_state != WebSocketState.DISCONNECTED: + await ws.close() - return (True, "PKF1") - - def setup(self) -> None: - if self.game_cfg.server.enable_matching: - reactor.listenTCP( - self.game_cfg.ports.admission, PokkenAdmissionFactory(self.core_cfg, self.game_cfg) - ) - - def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes: - content = request.content.getvalue() + async def render_POST(self, request: Request) -> bytes: + content = await request.body() if content == b"": self.logger.info("Empty request") return b"" @@ -134,19 +160,19 @@ class PokkenServlet(BaseServlet): self.logger.info(f"{endpoint} request from {Utils.get_ip_addr(request)}") - ret = handler(pokken_request) - return ret + ret = await handler(pokken_request) + return Response(ret) - def handle_matching(self, request: Request, game_code: str, matchers: Dict) -> bytes: + async def handle_matching(self, request: Request) -> bytes: if not self.game_cfg.server.enable_matching: - return b"" + return Response() - content = request.content.getvalue() + content = await request.body() client_ip = Utils.get_ip_addr(request) if content is None or content == b"": self.logger.info("Empty matching request") - return json.dumps(self.base.handle_matching_noop()).encode() + return JSONResponse(self.base.handle_matching_noop()) json_content = ast.literal_eval( content.decode() @@ -166,7 +192,7 @@ class PokkenServlet(BaseServlet): self.logger.warning( f"No handler found for message type {json_content['call']}" ) - return json.dumps(self.base.handle_matching_noop()).encode() + return JSONResponse(self.base.handle_matching_noop()) ret = handler(json_content, client_ip) @@ -181,4 +207,4 @@ class PokkenServlet(BaseServlet): self.logger.debug(f"Response {ret}") - return json.dumps(ret).encode() + return JSONResponse(ret) diff --git a/titles/sao/base.py b/titles/sao/base.py index 09a9f88..9d933ed 100644 --- a/titles/sao/base.py +++ b/titles/sao/base.py @@ -32,42 +32,42 @@ class SaoBase: self.logger.warning(f"Failed to find csv file {file}.csv") return ret - def handle_noop(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_noop(self, header: SaoRequestHeader, request: bytes) -> bytes: self.logger.info(f"Using Generic handler") resp_thing = SaoNoopResponse(header.cmd + 1) return resp_thing.make() - def handle_c122(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c122(self, header: SaoRequestHeader, request: bytes) -> bytes: #common/get_maintenance_info resp = SaoGetMaintResponse(header.cmd +1) return resp.make() - def handle_c12e(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c12e(self, header: SaoRequestHeader, request: bytes) -> bytes: #common/ac_cabinet_boot_notification resp = SaoCommonAcCabinetBootNotificationResponse(header.cmd +1) return resp.make() - def handle_c100(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c100(self, header: SaoRequestHeader, request: bytes) -> bytes: #common/get_app_versions resp = SaoCommonGetAppVersionsRequest(header.cmd +1) return resp.make() - def handle_c102(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c102(self, header: SaoRequestHeader, request: bytes) -> bytes: #common/master_data_version_check resp = SaoMasterDataVersionCheckResponse(header.cmd +1) return resp.make() - def handle_c10a(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c10a(self, header: SaoRequestHeader, request: bytes) -> bytes: #common/paying_play_start resp = SaoCommonPayingPlayStartRequest(header.cmd +1) return resp.make() - def handle_ca02(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_ca02(self, header: SaoRequestHeader, request: bytes) -> bytes: #quest_multi_play_room/get_quest_scene_multi_play_photon_server resp = SaoGetQuestSceneMultiPlayPhotonServerResponse(header.cmd +1) return resp.make() - def handle_c11e(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c11e(self, header: SaoRequestHeader, request: bytes) -> bytes: #common/get_auth_card_data req = SaoGetAuthCardDataRequest(header, request) @@ -120,12 +120,12 @@ class SaoBase: resp = SaoGetAuthCardDataResponse(header.cmd +1, profile_data) return resp.make() - def handle_c40c(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c40c(self, header: SaoRequestHeader, request: bytes) -> bytes: #home/check_ac_login_bonus resp = SaoHomeCheckAcLoginBonusResponse(header.cmd +1) return resp.make() - def handle_c104(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c104(self, header: SaoRequestHeader, request: bytes) -> bytes: #common/login req = SaoCommonLoginRequest(header, request) @@ -135,17 +135,17 @@ class SaoBase: resp = SaoCommonLoginResponse(header.cmd +1, profile_data) return resp.make() - def handle_c404(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c404(self, header: SaoRequestHeader, request: bytes) -> bytes: #home/check_comeback_event resp = SaoCheckComebackEventRequest(header.cmd +1) return resp.make() - def handle_c000(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c000(self, header: SaoRequestHeader, request: bytes) -> bytes: #ticket/ticket resp = SaoTicketResponse(header.cmd +1) return resp.make() - def handle_c500(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c500(self, header: SaoRequestHeader, request: bytes) -> bytes: #user_info/get_user_basic_data req = SaoGetUserBasicDataRequest(header, request) @@ -154,7 +154,7 @@ class SaoBase: resp = SaoGetUserBasicDataResponse(header.cmd +1, profile_data) return resp.make() - def handle_c600(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c600(self, header: SaoRequestHeader, request: bytes) -> bytes: #have_object/get_hero_log_user_data_list req = SaoGetHeroLogUserDataListRequest(header, request) @@ -163,7 +163,7 @@ class SaoBase: resp = SaoGetHeroLogUserDataListResponse(header.cmd +1, hero_data) return resp.make() - def handle_c602(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c602(self, header: SaoRequestHeader, request: bytes) -> bytes: #have_object/get_equipment_user_data_list req = SaoGetEquipmentUserDataListRequest(header, request) @@ -172,7 +172,7 @@ class SaoBase: resp = SaoGetEquipmentUserDataListResponse(header.cmd +1, equipment_data) return resp.make() - def handle_c604(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c604(self, header: SaoRequestHeader, request: bytes) -> bytes: #have_object/get_item_user_data_list req = SaoGetItemUserDataListRequest(header, request) @@ -181,21 +181,21 @@ class SaoBase: resp = SaoGetItemUserDataListResponse(header.cmd +1, item_data) return resp.make() - def handle_c606(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c606(self, header: SaoRequestHeader, request: bytes) -> bytes: #have_object/get_support_log_user_data_list supportIdsData = self.game_data.static.get_support_log_ids(0, True) resp = SaoGetSupportLogUserDataListResponse(header.cmd +1, supportIdsData) return resp.make() - def handle_c800(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c800(self, header: SaoRequestHeader, request: bytes) -> bytes: #custom/get_title_user_data_list titleIdsData = self.game_data.static.get_title_ids(0, True) resp = SaoGetTitleUserDataListResponse(header.cmd +1, titleIdsData) return resp.make() - def handle_c608(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c608(self, header: SaoRequestHeader, request: bytes) -> bytes: #have_object/get_episode_append_data_list req = SaoGetEpisodeAppendDataListRequest(header, request) @@ -204,7 +204,7 @@ class SaoBase: resp = SaoGetEpisodeAppendDataListResponse(header.cmd +1, profile_data) return resp.make() - def handle_c804(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c804(self, header: SaoRequestHeader, request: bytes) -> bytes: #custom/get_party_data_list req = SaoGetPartyDataListRequest(header, request) @@ -216,17 +216,17 @@ class SaoBase: resp = SaoGetPartyDataListResponse(header.cmd +1, hero1_data, hero2_data, hero3_data) return resp.make() - def handle_c902(self, header: SaoRequestHeader, request: bytes) -> bytes: # for whatever reason, having all entries empty or filled changes nothing + async def handle_c902(self, header: SaoRequestHeader, request: bytes) -> bytes: # for whatever reason, having all entries empty or filled changes nothing #quest/get_quest_scene_prev_scan_profile_card resp = SaoGetQuestScenePrevScanProfileCardResponse(header.cmd +1) return resp.make() - def handle_c124(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c124(self, header: SaoRequestHeader, request: bytes) -> bytes: #common/get_resource_path_info resp = SaoGetResourcePathInfoResponse(header.cmd +1) return resp.make() - def handle_c900(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c900(self, header: SaoRequestHeader, request: bytes) -> bytes: #quest/get_quest_scene_user_data_list // QuestScene.csv req = SaoGetQuestSceneUserDataListRequest(header, request) @@ -235,22 +235,22 @@ class SaoBase: resp = SaoGetQuestSceneUserDataListResponse(header.cmd +1, quest_data) return resp.make() - def handle_c400(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c400(self, header: SaoRequestHeader, request: bytes) -> bytes: #home/check_yui_medal_get_condition resp = SaoCheckYuiMedalGetConditionResponse(header.cmd +1) return resp.make() - def handle_c402(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c402(self, header: SaoRequestHeader, request: bytes) -> bytes: #home/get_yui_medal_bonus_user_data resp = SaoGetYuiMedalBonusUserDataResponse(header.cmd +1) return resp.make() - def handle_c40a(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c40a(self, header: SaoRequestHeader, request: bytes) -> bytes: #home/check_profile_card_used_reward resp = SaoCheckProfileCardUsedRewardResponse(header.cmd +1) return resp.make() - def handle_c814(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c814(self, header: SaoRequestHeader, request: bytes) -> bytes: #custom/synthesize_enhancement_hero_log req = SaoSynthesizeEnhancementHeroLogRequest(header, request) @@ -324,7 +324,7 @@ class SaoBase: resp = SaoSynthesizeEnhancementHeroLogResponse(header.cmd +1, synthesize_hero_log_data) return resp.make() - def handle_c816(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c816(self, header: SaoRequestHeader, request: bytes) -> bytes: #custom/synthesize_enhancement_equipment req_data = SaoSynthesizeEnhancementEquipmentRequest(header, request) synthesize_equipment_data = self.game_data.item.get_user_equipment(req_data.user_id, req_data.origin_user_equipment_id) @@ -386,7 +386,7 @@ class SaoBase: resp = SaoSynthesizeEnhancementEquipmentResponse(header.cmd +1, synthesize_equipment_data) return resp.make() - def handle_c806(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c806(self, header: SaoRequestHeader, request: bytes) -> bytes: #custom/change_party req_data = SaoChangePartyRequest(header, request) party_hero_list = [] @@ -421,7 +421,7 @@ class SaoBase: resp = SaoNoopResponse(header.cmd +1) return resp.make() - def handle_c904(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c904(self, header: SaoRequestHeader, request: bytes) -> bytes: #quest/episode_play_start req_data = SaoEpisodePlayStartRequest(header, request) @@ -439,7 +439,7 @@ class SaoBase: resp = SaoEpisodePlayStartResponse(header.cmd +1, profile_data) return resp.make() - def handle_c908(self, header: SaoRequestHeader, request: bytes) -> bytes: # Level calculation missing for the profile and heroes + async def handle_c908(self, header: SaoRequestHeader, request: bytes) -> bytes: # Level calculation missing for the profile and heroes #quest/episode_play_end req_data = SaoEpisodePlayEndRequest(header, request) @@ -599,7 +599,7 @@ class SaoBase: resp = SaoEpisodePlayEndResponse(header.cmd +1) return resp.make() - def handle_c914(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c914(self, header: SaoRequestHeader, request: bytes) -> bytes: #quest/trial_tower_play_start req_data = SaoTrialTowerPlayStartRequest(header, request) @@ -618,7 +618,7 @@ class SaoBase: resp = SaoEpisodePlayStartResponse(header.cmd +1, profile_data) return resp.make() - def handle_c918(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c918(self, header: SaoRequestHeader, request: bytes) -> bytes: #quest/trial_tower_play_end req_data = SaoTrialTowerPlayEndRequest(header, request) @@ -799,7 +799,7 @@ class SaoBase: resp = SaoTrialTowerPlayEndResponse(header.cmd +1) return resp.make() - def handle_c90a(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c90a(self, header: SaoRequestHeader, request: bytes) -> bytes: #quest/episode_play_end_unanalyzed_log_fixed req = SaoEpisodePlayEndUnanalyzedLogFixedRequest(header, request) @@ -809,7 +809,7 @@ class SaoBase: resp = SaoEpisodePlayEndUnanalyzedLogFixedResponse(header.cmd +1, end_session_data[4]) return resp.make() - def handle_c91a(self, header: SaoRequestHeader, request: bytes) -> bytes: # handler is identical to the episode + async def handle_c91a(self, header: SaoRequestHeader, request: bytes) -> bytes: # handler is identical to the episode #quest/trial_tower_play_end_unanalyzed_log_fixed req = TrialTowerPlayEndUnanalyzedLogFixed(header, request) @@ -818,58 +818,58 @@ class SaoBase: resp = SaoEpisodePlayEndUnanalyzedLogFixedResponse(header.cmd +1, end_session_data[4]) return resp.make() - def handle_cd00(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_cd00(self, header: SaoRequestHeader, request: bytes) -> bytes: #defrag_match/get_defrag_match_basic_data resp = SaoGetDefragMatchBasicDataResponse(header.cmd +1) return resp.make() - def handle_cd02(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_cd02(self, header: SaoRequestHeader, request: bytes) -> bytes: #defrag_match/get_defrag_match_ranking_user_data resp = SaoGetDefragMatchRankingUserDataResponse(header.cmd +1) return resp.make() - def handle_cd04(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_cd04(self, header: SaoRequestHeader, request: bytes) -> bytes: #defrag_match/get_defrag_match_league_point_ranking_list resp = SaoGetDefragMatchLeaguePointRankingListResponse(header.cmd +1) return resp.make() - def handle_cd06(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_cd06(self, header: SaoRequestHeader, request: bytes) -> bytes: #defrag_match/get_defrag_match_league_score_ranking_list resp = SaoGetDefragMatchLeagueScoreRankingListResponse(header.cmd +1) return resp.make() - def handle_d404(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_d404(self, header: SaoRequestHeader, request: bytes) -> bytes: #other/bnid_serial_code_check resp = SaoBnidSerialCodeCheckResponse(header.cmd +1) return resp.make() - def handle_c306(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c306(self, header: SaoRequestHeader, request: bytes) -> bytes: #card/scan_qr_quest_profile_card resp = SaoScanQrQuestProfileCardResponse(header.cmd +1) return resp.make() - def handle_c700(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c700(self, header: SaoRequestHeader, request: bytes) -> bytes: # shop/get_shop_resource_sales_data_list # TODO: Get user shop data req = GetShopResourceSalesDataListRequest(header, request) resp = GetShopResourceSalesDataListResponse(header.cmd + 1) return resp.make() - def handle_d100(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_d100(self, header: SaoRequestHeader, request: bytes) -> bytes: # shop/get_yui_medal_shop_user_data_list # TODO: Get user shop data req = GetYuiMedalShopUserDataListRequest(header, request) resp = GetYuiMedalShopUserDataListResponse(header.cmd + 1) return resp.make() - def handle_cf0e(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_cf0e(self, header: SaoRequestHeader, request: bytes) -> bytes: # gasha/get_gasha_medal_shop_user_data_list # TODO: Get user shop data req = GetGashaMedalShopUserDataListRequest(header, request) resp = GetGashaMedalShopUserDataListResponse(header.cmd + 1) return resp.make() - def handle_d5da(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_d5da(self, header: SaoRequestHeader, request: bytes) -> bytes: # master_data/get_m_yui_medal_shops req = GetMYuiMedalShopDataRequest(header, request) resp = GetMYuiMedalShopDataResponse(header.cmd + 1) @@ -897,7 +897,7 @@ class SaoBase: self.logger.debug(f"Load {len(resp.data_list)} Yui Medal Shops") return resp.make() - def handle_d5dc(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_d5dc(self, header: SaoRequestHeader, request: bytes) -> bytes: # master_data/get_m_yui_medal_shop_items req = GetMYuiMedalShopItemsRequest(header, request) resp = GetMYuiMedalShopItemsResponse(header.cmd + 1) @@ -927,7 +927,7 @@ class SaoBase: self.logger.debug(f"Load {len(resp.data_list)} Yui Medal Shop Items") return resp.make() - def handle_d5fc(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_d5fc(self, header: SaoRequestHeader, request: bytes) -> bytes: # master_data/get_m_gasha_medal_shops req = GetMGashaMedalShopsRequest(header, request) resp = GetMGashaMedalShopsResponse(header.cmd + 1) @@ -942,11 +942,11 @@ class SaoBase: self.logger.debug(f"Load {len(resp.data_list)} Gasha Medal Shops") return resp.make() - def handle_d5fe(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_d5fe(self, header: SaoRequestHeader, request: bytes) -> bytes: # master_data/get_m_gasha_medal_shop_items return SaoNoopResponse(header.cmd + 1).make() - def handle_d604(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_d604(self, header: SaoRequestHeader, request: bytes) -> bytes: # master_data_2/get_m_res_earn_campaign_shops req = GetMResEarnCampaignShopsRequest(header, request) resp = GetMResEarnCampaignShopsResponse(header.cmd + 1) @@ -968,6 +968,6 @@ class SaoBase: #self.logger.debug(f"Load {len(resp.data_list)} Res Earn Campaign Shops") return SaoNoopResponse(header.cmd + 1).make() - def handle_d606(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_d606(self, header: SaoRequestHeader, request: bytes) -> bytes: # master_data_2/get_m_res_earn_campaign_shop_items return SaoNoopResponse(header.cmd + 1).make() \ No newline at end of file diff --git a/titles/sao/const.py b/titles/sao/const.py index 8bdea0f..d181556 100644 --- a/titles/sao/const.py +++ b/titles/sao/const.py @@ -3,12 +3,22 @@ from enum import Enum class SaoConstants: GAME_CODE = "SDEW" + GAME_CDS = ["SAO1"] CONFIG_NAME = "sao.yaml" VER_SAO = 0 VERSION_NAMES = ("Sword Art Online Arcade") + + SERIAL_IDENT_SATALITE = 4 + SERIAL_IDENT_TERMINAL = 5 + + SERIAL_IDENT = [2825] + NETID_PREFIX = ["ABLN"] + SERIAL_REGIONS = [1] + SERIAL_ROLES = [3] + SERIAL_CAB_IDENTS = [SERIAL_IDENT_SATALITE, SERIAL_IDENT_TERMINAL] @classmethod def game_ver_to_string(cls, ver: int): diff --git a/titles/sao/index.py b/titles/sao/index.py index 78d641e..4cabe30 100644 --- a/titles/sao/index.py +++ b/titles/sao/index.py @@ -1,5 +1,7 @@ from typing import Tuple, Dict, List -from twisted.web.http import Request +from starlette.requests import Request +from starlette.responses import Response +from starlette.routing import Route import yaml import logging, coloredlogs from logging.handlers import TimedRotatingFileHandler @@ -55,11 +57,10 @@ class SaoServlet(BaseServlet): if self.game_cfg.hash.verify_hash: self.static_hash = md5(self.game_cfg.hash.hash_base.encode()).digest() # Greate hashing guys, really validates the data - def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]: - return ( - [], - [("render_POST", "/{datecode}/proto/if/{category}/{endpoint}", {})] - ) + def get_routes(self) -> List[Route]: + return [ + Route("/{datecode:int}/proto/if/{category:str}/{endpoint:str}", self.render_POST, methods=['POST']) + ] @classmethod def is_game_enabled(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> bool: @@ -86,30 +87,29 @@ class SaoServlet(BaseServlet): proto = "https" port = f":{port_ssl}" if not self.core_cfg.server.is_using_proxy and port_ssl != 443 else "" - return (f"{proto}://{self.core_cfg.title.hostname}{port}/", "") + return (f"{proto}://{self.core_cfg.server.hostname}{port}/", "") def get_mucha_info(self, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, str]: if not self.game_cfg.server.enable: - return (False, "") + return (False, [], []) - return (True, "SAO1") + return (True, SaoConstants.GAME_CDS, SaoConstants.NETID_PREFIX) - def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes: - endpoint = matchers.get('endpoint', '') - request.responseHeaders.addRawHeader(b"content-type", b"text/html; charset=utf-8") + async def render_POST(self, request: Request) -> bytes: + endpoint = request.path_params.get('endpoint', '') iv = b"" - req_raw = request.content.read() + req_raw = await request.body() if len(req_raw) < 40: self.logger.warn(f"Malformed request to {endpoint} - {req_raw.hex()}") - return b"" + return Response() req_header = SaoRequestHeader(req_raw) cmd_str = f"{req_header.cmd:04x}" if self.game_cfg.hash.verify_hash and self.static_hash != req_header.hash: self.logger.error(f"Hash mismatch! Expecting {self.static_hash} but recieved {req_header.hash}") - return b"" + return Response() if self.game_cfg.crypt.enable: iv = req_raw[40:48] @@ -124,7 +124,7 @@ class SaoServlet(BaseServlet): handler = getattr(self.base, f"handle_{cmd_str}", self.base.handle_noop) self.logger.info(f"{endpoint} - {cmd_str} request") self.logger.debug(f"Request: {req_raw.hex()}") - resp = handler(req_header, req_data) + resp = await handler(req_header, req_data) if resp is None: resp = SaoNoopResponse(req_header.cmd + 1).make() @@ -137,7 +137,7 @@ class SaoServlet(BaseServlet): else: self.logger.error(f"Unknown response type {type(resp)}") - return b"" + return Response() self.logger.debug(f"Response: {resp.hex()}") @@ -153,5 +153,6 @@ class SaoServlet(BaseServlet): tmp = struct.pack("!I", crypt_data_len) # does it want the length of the encrypted response?? resp = resp[:20] + tmp + iv + data_crypt self.logger.debug(f"Encrypted Response: {resp.hex()}") - - return resp \ No newline at end of file + + + return Response(resp, media_type="text/html; charset=utf-8") \ No newline at end of file diff --git a/titles/wacca/base.py b/titles/wacca/base.py index dd3fe24..c0be449 100644 --- a/titles/wacca/base.py +++ b/titles/wacca/base.py @@ -83,18 +83,18 @@ class WaccaBase: else: self.region_id = WaccaConstants.Region[prefecture_name] - def handle_housing_get_request(self, data: Dict) -> Dict: + async def handle_housing_get_request(self, data: Dict) -> Dict: req = BaseRequest(data) housing_id = 1337 self.logger.info(f"{req.chipId} -> {housing_id}") resp = HousingGetResponse(housing_id) return resp.make() - def handle_advertise_GetRanking_request(self, data: Dict) -> Dict: + async def handle_advertise_GetRanking_request(self, data: Dict) -> Dict: req = AdvertiseGetRankingRequest(data) return AdvertiseGetRankingResponse().make() - def handle_housing_start_request(self, data: Dict) -> Dict: + async def handle_housing_start_request(self, data: Dict) -> Dict: req = HousingStartRequestV1(data) allnet_region_id = None @@ -126,16 +126,16 @@ class WaccaBase: resp = HousingStartResponseV1(region_id) return resp.make() - def handle_advertise_GetNews_request(self, data: Dict) -> Dict: + async def handle_advertise_GetNews_request(self, data: Dict) -> Dict: resp = GetNewsResponseV1() return resp.make() - def handle_user_status_logout_request(self, data: Dict) -> Dict: + async def handle_user_status_logout_request(self, data: Dict) -> Dict: req = UserStatusLogoutRequest(data) self.logger.info(f"Log out user {req.userId} from {req.chipId}") return BaseResponse().make() - def handle_user_status_get_request(self, data: Dict) -> Dict: + async def handle_user_status_get_request(self, data: Dict) -> Dict: req = UserStatusGetRequest(data) resp = UserStatusGetV1Response() @@ -181,7 +181,7 @@ class WaccaBase: return resp.make() - def handle_user_status_login_request(self, data: Dict) -> Dict: + async def handle_user_status_login_request(self, data: Dict) -> Dict: req = UserStatusLoginRequest(data) resp = UserStatusLoginResponseV1() is_consec_day = True @@ -227,7 +227,7 @@ class WaccaBase: return resp.make() - def handle_user_status_create_request(self, data: Dict) -> Dict: + async def handle_user_status_create_request(self, data: Dict) -> Dict: req = UserStatusCreateRequest(data) profileId = self.data.profile.create_profile( @@ -268,7 +268,7 @@ class WaccaBase: return UserStatusCreateResponseV2(profileId, req.username).make() - def handle_user_status_getDetail_request(self, data: Dict) -> Dict: + async def handle_user_status_getDetail_request(self, data: Dict) -> Dict: req = UserStatusGetDetailRequest(data) resp = UserStatusGetDetailResponseV1() @@ -433,7 +433,7 @@ class WaccaBase: return resp.make() - def handle_user_trial_get_request(self, data: Dict) -> Dict: + async def handle_user_trial_get_request(self, data: Dict) -> Dict: req = UserTrialGetRequest(data) resp = UserTrialGetResponse() @@ -475,7 +475,7 @@ class WaccaBase: return resp.make() - def handle_user_trial_update_request(self, data: Dict) -> Dict: + async def handle_user_trial_update_request(self, data: Dict) -> Dict: req = UserTrialUpdateRequest(data) total_score = 0 @@ -571,7 +571,7 @@ class WaccaBase: ) return BaseResponse().make() - def handle_user_sugoroku_update_request(self, data: Dict) -> Dict: + async def handle_user_sugoroku_update_request(self, data: Dict) -> Dict: ver_split = data["appVersion"].split(".") resp = BaseResponse() @@ -603,10 +603,10 @@ class WaccaBase: ) return resp.make() - def handle_user_info_getMyroom_request(self, data: Dict) -> Dict: + async def handle_user_info_getMyroom_request(self, data: Dict) -> Dict: return UserInfogetMyroomResponseV1().make() - def handle_user_music_unlock_request(self, data: Dict) -> Dict: + async def handle_user_music_unlock_request(self, data: Dict) -> Dict: req = UserMusicUnlockRequest(data) profile = self.data.profile.get_profile(req.profileId) @@ -672,12 +672,12 @@ class WaccaBase: return UserMusicUnlockResponse(current_wp, new_tickets).make() - def handle_user_info_getRanking_request(self, data: Dict) -> Dict: + async def handle_user_info_getRanking_request(self, data: Dict) -> Dict: # total score, high score by song, cumulative socre, stage up score, other score, WP ranking # This likely requies calculating standings at regular intervals and caching the results return UserInfogetRankingResponse().make() - def handle_user_music_update_request(self, data: Dict) -> Dict: + async def handle_user_music_update_request(self, data: Dict) -> Dict: ver_split = data["appVersion"].split(".") if int(ver_split[0]) >= 3: resp = UserMusicUpdateResponseV3() @@ -831,18 +831,18 @@ class WaccaBase: return resp.make() # TODO: Coop and vs data - def handle_user_music_updateCoop_request(self, data: Dict) -> Dict: + async def handle_user_music_updateCoop_request(self, data: Dict) -> Dict: coop_info = data["params"][4] return self.handle_user_music_update_request(data) - def handle_user_music_updateVersus_request(self, data: Dict) -> Dict: + async def handle_user_music_updateVersus_request(self, data: Dict) -> Dict: vs_info = data["params"][4] return self.handle_user_music_update_request(data) - def handle_user_music_updateTrial_request(self, data: Dict) -> Dict: + async def handle_user_music_updateTrial_request(self, data: Dict) -> Dict: return self.handle_user_music_update_request(data) - def handle_user_mission_update_request(self, data: Dict) -> Dict: + async def handle_user_mission_update_request(self, data: Dict) -> Dict: req = UserMissionUpdateRequest(data) page_status = req.params[1][1] @@ -860,7 +860,7 @@ class WaccaBase: return BaseResponse().make() - def handle_user_goods_purchase_request(self, data: Dict) -> Dict: + async def handle_user_goods_purchase_request(self, data: Dict) -> Dict: req = UserGoodsPurchaseRequest(data) resp = UserGoodsPurchaseResponse() @@ -905,13 +905,13 @@ class WaccaBase: return resp.make() - def handle_competition_status_login_request(self, data: Dict) -> Dict: + async def handle_competition_status_login_request(self, data: Dict) -> Dict: return BaseResponse().make() - def handle_competition_status_update_request(self, data: Dict) -> Dict: + async def handle_competition_status_update_request(self, data: Dict) -> Dict: return BaseResponse().make() - def handle_user_rating_update_request(self, data: Dict) -> Dict: + async def handle_user_rating_update_request(self, data: Dict) -> Dict: req = UserRatingUpdateRequest(data) user_id = self.data.profile.profile_to_aime_user(req.profileId) @@ -931,7 +931,7 @@ class WaccaBase: return BaseResponse().make() - def handle_user_status_update_request(self, data: Dict) -> Dict: + async def handle_user_status_update_request(self, data: Dict) -> Dict: req = UserStatusUpdateRequestV1(data) user_id = self.data.profile.profile_to_aime_user(req.profileId) @@ -970,7 +970,7 @@ class WaccaBase: ) return BaseResponse().make() - def handle_user_info_update_request(self, data: Dict) -> Dict: + async def handle_user_info_update_request(self, data: Dict) -> Dict: req = UserInfoUpdateRequest(data) user_id = self.data.profile.profile_to_aime_user(req.profileId) @@ -989,7 +989,7 @@ class WaccaBase: return BaseResponse().make() - def handle_user_vip_get_request(self, data: Dict) -> Dict: + async def handle_user_vip_get_request(self, data: Dict) -> Dict: req = UserVipGetRequest(data) resp = UserVipGetResponse() @@ -1021,7 +1021,7 @@ class WaccaBase: return resp.make() - def handle_user_vip_start_request(self, data: Dict) -> Dict: + async def handle_user_vip_start_request(self, data: Dict) -> Dict: req = UserVipStartRequest(data) profile = self.data.profile.get_profile(req.profileId) diff --git a/titles/wacca/frontend.py b/titles/wacca/frontend.py index cc40644..fb55de1 100644 --- a/titles/wacca/frontend.py +++ b/titles/wacca/frontend.py @@ -1,6 +1,6 @@ import yaml import jinja2 -from twisted.web.http import Request +from starlette.requests import Request from os import path from twisted.web.server import Session diff --git a/titles/wacca/index.py b/titles/wacca/index.py index 90e4a0b..3f58a9d 100644 --- a/titles/wacca/index.py +++ b/titles/wacca/index.py @@ -1,16 +1,19 @@ +from starlette.routing import Route import yaml import logging, coloredlogs from logging.handlers import TimedRotatingFileHandler import logging import json from hashlib import md5 -from twisted.web.http import Request +from starlette.requests import Request +from starlette.responses import JSONResponse, Response from typing import Dict, Tuple, List from os import path import traceback import sys from core import CoreConfig, Utils +from core.title import BaseServlet from .config import WaccaConfig from .config import WaccaConfig from .const import WaccaConstants @@ -23,7 +26,7 @@ from .handlers.base import BaseResponse, BaseRequest from .handlers.helpers import Version -class WaccaServlet: +class WaccaServlet(BaseServlet): def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None: self.core_cfg = core_cfg self.game_cfg = WaccaConfig() @@ -62,15 +65,12 @@ class WaccaServlet: coloredlogs.install( level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str ) - - def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]: - return ( - [], - [ - ("render_POST", "/WaccaServlet/api/{api}/{endpoint}", {}), - ("render_POST", "/WaccaServlet/api/{api}/{branch}/{endpoint}", {}) - ] - ) + + def get_routes(self) -> List[Route]: + return [ + Route("/WaccaServlet/api/{api:str}/{endpoint:str}", self.render_POST, methods=['POST']), + Route("/WaccaServlet/api/{api:str}/{branch:str}/{endpoint:str}", self.render_POST, methods=['POST']), + ] @classmethod def is_game_enabled(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> bool: @@ -88,22 +88,22 @@ class WaccaServlet: def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]: if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80: return ( - f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/WaccaServlet", - self.core_cfg.title.hostname, + f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}/WaccaServlet", + self.core_cfg.server.hostname, ) - return (f"http://{self.core_cfg.title.hostname}/WaccaServlet", self.core_cfg.title.hostname) + return (f"http://{self.core_cfg.server.hostname}/WaccaServlet", self.core_cfg.server.hostname) - def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes: + async def render_POST(self, request: Request) -> bytes: def end(resp: Dict) -> bytes: hash = md5(json.dumps(resp, ensure_ascii=False).encode()).digest() - request.responseHeaders.addRawHeader(b"X-Wacca-Hash", hash.hex().encode()) - return json.dumps(resp).encode() + return JSONResponse(resp, headers=["X-Wacca-Hash", hash.hex()]) - api = matchers['api'] - branch = matchers.get('branch', '') - endpoint = matchers['endpoint'] + api = request.path_params.get('api', '') + branch = request.path_params.get('branch', '') + endpoint = request.path_params.get('endpoint', '') client_ip = Utils.get_ip_addr(request) + bod = await request.body() if branch: url_path = f"{api}/{branch}/{endpoint}" @@ -114,13 +114,13 @@ class WaccaServlet: func_to_find = f"handle_{api}_{endpoint}_request" try: - req_json = json.loads(request.content.getvalue()) + req_json = json.loads(bod) version_full = Version(req_json["appVersion"]) req = BaseRequest(req_json) except KeyError as e: self.logger.error( - f"Failed to parse request to {request.content.getvalue()} -> Missing required value {e}" + f"Failed to parse request to {bod} -> Missing required value {e}" ) resp = BaseResponse() resp.status = 1 @@ -129,7 +129,7 @@ class WaccaServlet: except Exception as e: self.logger.error( - f"Failed to parse request to {url_path} -> {request.content.getvalue()} -> {e}" + f"Failed to parse request to {url_path} -> {bod} -> {e}" ) resp = BaseResponse() resp.status = 1 @@ -176,7 +176,7 @@ class WaccaServlet: try: handler = getattr(self.versions[internal_ver], func_to_find) - resp = handler(req_json) + resp = await handler(req_json) self.logger.debug(f"{req.appVersion} response {resp}") return end(resp) diff --git a/titles/wacca/lily.py b/titles/wacca/lily.py index 33928ec..d92e417 100644 --- a/titles/wacca/lily.py +++ b/titles/wacca/lily.py @@ -37,13 +37,13 @@ class WaccaLily(WaccaS): (210003, 0), ] - def handle_advertise_GetNews_request(self, data: Dict) -> Dict: + async def handle_advertise_GetNews_request(self, data: Dict) -> Dict: resp = GetNewsResponseV3() return resp.make() - def handle_user_status_create_request(self, data: Dict) -> Dict: + async def handle_user_status_create_request(self, data: Dict) -> Dict: req = UserStatusCreateRequest(data) - ret = super().handle_user_status_create_request(data) + ret = await super().handle_user_status_create_request(data) new_user = self.data.profile.get_profile(aime_id=req.aimeId) @@ -64,7 +64,7 @@ class WaccaLily(WaccaS): return ret - def handle_user_status_get_request(self, data: Dict) -> Dict: + async def handle_user_status_get_request(self, data: Dict) -> Dict: req = UserStatusGetRequest(data) resp = UserStatusGetV2Response() @@ -145,7 +145,7 @@ class WaccaLily(WaccaS): return resp.make() - def handle_user_status_login_request(self, data: Dict) -> Dict: + async def handle_user_status_login_request(self, data: Dict) -> Dict: req = UserStatusLoginRequest(data) resp = UserStatusLoginResponseV2() is_consec_day = True @@ -189,7 +189,7 @@ class WaccaLily(WaccaS): return resp.make() - def handle_user_status_getDetail_request(self, data: Dict) -> Dict: + async def handle_user_status_getDetail_request(self, data: Dict) -> Dict: req = UserStatusGetDetailRequest(data) if req.appVersion.minor >= 53: resp = UserStatusGetDetailResponseV3() @@ -440,10 +440,10 @@ class WaccaLily(WaccaS): return resp.make() - def handle_user_info_getMyroom_request(self, data: Dict) -> Dict: + async def handle_user_info_getMyroom_request(self, data: Dict) -> Dict: return UserInfogetMyroomResponseV2().make() - def handle_user_status_update_request(self, data: Dict) -> Dict: + async def handle_user_status_update_request(self, data: Dict) -> Dict: super().handle_user_status_update_request(data) req = UserStatusUpdateRequestV2(data) self.data.profile.update_profile_lastplayed( diff --git a/titles/wacca/lilyr.py b/titles/wacca/lilyr.py index 7204761..83f9bf6 100644 --- a/titles/wacca/lilyr.py +++ b/titles/wacca/lilyr.py @@ -39,7 +39,7 @@ class WaccaLilyR(WaccaLily): (210003, 0), ] - def handle_housing_start_request(self, data: Dict) -> Dict: + async def handle_housing_start_request(self, data: Dict) -> Dict: req = HousingStartRequestV2(data) allnet_region_id = None @@ -71,9 +71,9 @@ class WaccaLilyR(WaccaLily): resp = HousingStartResponseV1(region_id) return resp.make() - def handle_user_status_create_request(self, data: Dict) -> Dict: + async def handle_user_status_create_request(self, data: Dict) -> Dict: req = UserStatusCreateRequest(data) - resp = super().handle_user_status_create_request(data) + resp = await super().handle_user_status_create_request(data) self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210054 @@ -102,5 +102,5 @@ class WaccaLilyR(WaccaLily): return resp - def handle_user_status_logout_request(self, data: Dict) -> Dict: + async def handle_user_status_logout_request(self, data: Dict) -> Dict: return BaseResponse().make() diff --git a/titles/wacca/reverse.py b/titles/wacca/reverse.py index 6206940..51e8c63 100644 --- a/titles/wacca/reverse.py +++ b/titles/wacca/reverse.py @@ -47,12 +47,12 @@ class WaccaReverse(WaccaLilyR): (310006, 0), ] - def handle_user_status_login_request(self, data: Dict) -> Dict: - resp = super().handle_user_status_login_request(data) + async def handle_user_status_login_request(self, data: Dict) -> Dict: + resp = await super().handle_user_status_login_request(data) resp["params"].append([]) return resp - def handle_user_status_getDetail_request(self, data: Dict) -> Dict: + async def handle_user_status_getDetail_request(self, data: Dict) -> Dict: req = UserStatusGetDetailRequest(data) resp = UserStatusGetDetailResponseV4() @@ -305,9 +305,9 @@ class WaccaReverse(WaccaLilyR): return resp.make() - def handle_user_status_create_request(self, data: Dict) -> Dict: + async def handle_user_status_create_request(self, data: Dict) -> Dict: req = UserStatusCreateRequest(data) - resp = super().handle_user_status_create_request(data) + resp = await super().handle_user_status_create_request(data) self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 310001 diff --git a/titles/wacca/s.py b/titles/wacca/s.py index 4b1e997..aab2659 100644 --- a/titles/wacca/s.py +++ b/titles/wacca/s.py @@ -31,6 +31,6 @@ class WaccaS(WaccaBase): super().__init__(cfg, game_cfg) self.version = WaccaConstants.VER_WACCA_S - def handle_advertise_GetNews_request(self, data: Dict) -> Dict: + async def handle_advertise_GetNews_request(self, data: Dict) -> Dict: resp = GetNewsResponseV2() return resp.make() From cc68b7f6c67d381c819503af800c029f252835d9 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Tue, 9 Jan 2024 03:16:31 -0500 Subject: [PATCH 02/32] adb: fix timeout issue --- core/aimedb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/aimedb.py b/core/aimedb.py index 2fe1ef7..dbf9761 100644 --- a/core/aimedb.py +++ b/core/aimedb.py @@ -74,7 +74,7 @@ class AimedbServlette(): self.logger.debug(f"Connection made from {writer.get_extra_info('peername')[0]}") while True: try: - data: bytes = await reader.read() + data: bytes = await reader.read(4096) if len(data) == 0: self.logger.debug("Connection closed") return From 4933ad72f227ac7c30968b4abe98fcc2ed11ed31 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Tue, 9 Jan 2024 03:17:23 -0500 Subject: [PATCH 03/32] adb: change warning to debug --- core/aimedb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/aimedb.py b/core/aimedb.py index dbf9761..c5c1837 100644 --- a/core/aimedb.py +++ b/core/aimedb.py @@ -81,7 +81,7 @@ class AimedbServlette(): await self.process_data(data, reader, writer) await writer.drain() except ConnectionResetError as e: - self.logger.warn("Connection reset, disconnecting") + self.logger.debug("Connection reset, disconnecting") return async def process_data(self, data: bytes, reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> Optional[bytes]: From 5c124a7d616ec4dcb8392cc55b88c137768ca5f2 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 9 Jan 2024 11:48:28 -0500 Subject: [PATCH 04/32] chuni: remove semicolon --- titles/chuni/schema/profile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/titles/chuni/schema/profile.py b/titles/chuni/schema/profile.py index ea70583..43b5641 100644 --- a/titles/chuni/schema/profile.py +++ b/titles/chuni/schema/profile.py @@ -697,7 +697,7 @@ class ChuniProfileData(BaseData): ) return 0 - total_play_count = 0; + total_play_count = 0 for row in playcount_sql: total_play_count += row[0] return { From 4b9db8be3bc1a38da946b1241369160c76c3f5f6 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 9 Jan 2024 11:49:22 -0500 Subject: [PATCH 05/32] add uvicorn to requirements --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index fb27cb0..20192fc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,3 +19,4 @@ pyjwt==2.8.0 websockets starlette asyncio +uvicorn From f65aa4d60a34a6f7a88fea0fac6d84185c75f794 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 9 Jan 2024 12:21:31 -0500 Subject: [PATCH 06/32] idz: add socket servers --- titles/idz/echo.py | 19 ++-- titles/idz/handlers/load_server_info.py | 2 +- titles/idz/index.py | 41 ++++---- titles/idz/userdb.py | 129 +++++++++--------------- 4 files changed, 77 insertions(+), 114 deletions(-) diff --git a/titles/idz/echo.py b/titles/idz/echo.py index 979fd19..a8ff3a2 100644 --- a/titles/idz/echo.py +++ b/titles/idz/echo.py @@ -4,16 +4,11 @@ import logging from core.config import CoreConfig from .config import IDZConfig +class IDZEcho: + def connection_made(self, transport): + self.transport = transport -class IDZEcho(DatagramProtocol): - def __init__(self, cfg: CoreConfig, game_cfg: IDZConfig) -> None: - super().__init__() - self.core_config = cfg - self.game_config = game_cfg - self.logger = logging.getLogger("idz") - - def datagramReceived(self, data, addr): - self.logger.debug( - f"Echo from from {addr[0]}:{addr[1]} -> {self.transport.getHost().port} - {data.hex()}" - ) - self.transport.write(data, addr) + def datagram_received(self, data, addr): + message = data.decode() + self.logger.debug(f'Received echo from {addr}') + self.transport.sendto(data, addr) diff --git a/titles/idz/handlers/load_server_info.py b/titles/idz/handlers/load_server_info.py index 9eb63ab..f0ace03 100644 --- a/titles/idz/handlers/load_server_info.py +++ b/titles/idz/handlers/load_server_info.py @@ -91,7 +91,7 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase): ) struct.pack_into(" bytes: diff --git a/titles/idz/userdb.py b/titles/idz/userdb.py index b585bd2..cd6ea9c 100644 --- a/titles/idz/userdb.py +++ b/titles/idz/userdb.py @@ -1,15 +1,9 @@ -from twisted.internet.protocol import Factory, Protocol -import logging, coloredlogs +import logging from Crypto.Cipher import AES import struct from typing import Dict, Optional, List, Type -from twisted.web import server, resource -from twisted.internet import reactor, endpoints -from starlette.requests import Request -from routes import Mapper import random -from os import walk -import importlib +import asyncio from core.config import CoreConfig from .database import IDZData @@ -28,7 +22,7 @@ class IDZKey: self.hashN = hashN -class IDZUserDBProtocol(Protocol): +class IDZUserDB: def __init__( self, core_cfg: CoreConfig, @@ -45,6 +39,10 @@ class IDZUserDBProtocol(Protocol): self.version = None self.version_internal = None self.skip_next = False + + def start(self) -> None: + self.logger.info(f"Start on port {self.config.aimedb.port}") + asyncio.create_task(asyncio.start_server(self.connection_cb, self.config.server.listen_address, self.config.aimedb.port)) def append_padding(self, data: bytes): """Appends 0s to the end of the data until it's at the correct size""" @@ -52,43 +50,54 @@ class IDZUserDBProtocol(Protocol): padding_size = length[0] - len(data) data += bytes(padding_size) return data + + async def connection_cb(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter): + self.logger.debug(f"Connection made from {writer.get_extra_info('peername')[0]}") + while True: + try: + base = 0 - def connectionMade(self) -> None: - self.logger.debug(f"{self.transport.getPeer().host} Connected") - base = 0 + for i in range(len(self.static_key) - 1): + shift = 8 * i + byte = self.static_key[i] - for i in range(len(self.static_key) - 1): - shift = 8 * i - byte = self.static_key[i] + base |= byte << shift - base |= byte << shift + rsa_key = random.choice(self.rsa_keys) + key_enc: int = pow(base, rsa_key.e, rsa_key.N) + result = ( + key_enc.to_bytes(0x40, "little") + + struct.pack(" None: - self.logger.debug( - f"{self.transport.getPeer().host} Disconnected - {reason.value}" - ) - - def dataReceived(self, data: bytes) -> None: + def dataReceived(self, data: bytes, reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None: self.logger.debug(f"Receive data {data.hex()}") + client_ip = writer.get_extra_info('peername')[0] crypt = AES.new(self.static_key, AES.MODE_ECB) try: data_dec = crypt.decrypt(data) except Exception as e: - self.logger.error(f"Failed to decrypt UserDB request from {self.transport.getPeer().host} because {e} - {data.hex()}") + self.logger.error(f"Failed to decrypt UserDB request from {client_ip} because {e} - {data.hex()}") self.logger.debug(f"Decrypt data {data_dec.hex()}") @@ -99,7 +108,7 @@ class IDZUserDBProtocol(Protocol): self.logger.info(f"Userdb serverbox request {data_dec.hex()}") self.skip_next = True - self.transport.write(b"\x00") + writer.write(b"\x00") return elif magic == 0x01020304: @@ -119,21 +128,21 @@ class IDZUserDBProtocol(Protocol): self.version_internal = None self.logger.debug( - f"Userdb v{self.version} handshake response from {self.transport.getPeer().host}" + f"Userdb v{self.version} handshake response from {client_ip}" ) return elif self.skip_next: self.skip_next = False - self.transport.write(b"\x00") + writer.write(b"\x00") return elif self.version is None: # We didn't get a handshake before, and this isn't one now, so we're up the creek self.logger.info( - f"Bad UserDB request from from {self.transport.getPeer().host}" + f"Bad UserDB request from from {client_ip}" ) - self.transport.write(b"\x00") + writer.write(b"\x00") return cmd = struct.unpack_from(" None: - self.core_config = cfg - self.game_config = game_cfg - self.keys = keys - self.handlers = handlers - - def buildProtocol(self, addr): - return IDZUserDBProtocol( - self.core_config, self.game_config, self.keys, self.handlers - ) - - -class IDZUserDBWeb(resource.Resource): - def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig): - super().__init__() - self.isLeaf = True - self.core_config = core_cfg - self.game_config = game_cfg - self.logger = logging.getLogger("idz") - - def render_POST(self, request: Request) -> bytes: - self.logger.info( - f"IDZUserDBWeb POST from {request.getClientAddress().host} to {request.uri} with data {request.content.getvalue()}" - ) - return b"" - - def render_GET(self, request: Request) -> bytes: - self.logger.info( - f"IDZUserDBWeb GET from {request.getClientAddress().host} to {request.uri}" - ) - return b"" + writer.write(response_enc) From 7b49a8ab58de1bde582e0078fa805da9589d5176 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 9 Jan 2024 12:41:32 -0500 Subject: [PATCH 07/32] add JSONResponseNoASCII --- core/title.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/core/title.py b/core/title.py index 8bead43..7aeb433 100644 --- a/core/title.py +++ b/core/title.py @@ -1,4 +1,5 @@ -from typing import Dict, List, Tuple +from typing import Dict, List, Tuple, Any +import json import logging, coloredlogs from logging.handlers import TimedRotatingFileHandler from starlette.requests import Request @@ -9,6 +10,15 @@ from core.config import CoreConfig from core.data import Data from core.utils import Utils +class JSONResponseNoASCII(Response): + media_type = "application/json" + + def render(self, content: Any) -> bytes: + return json.dumps( + content, + ensure_ascii=False, + ).encode("utf-8") + class BaseServlet: def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None: self.core_cfg = core_cfg From 1c22c6bec050f37812a158187c22345cbb7252cc Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 9 Jan 2024 12:41:47 -0500 Subject: [PATCH 08/32] idac: add echo server --- titles/idac/echo.py | 19 +++++------- titles/idac/index.py | 70 +++++++++++++++++++++++++++++------------- titles/idac/season2.py | 8 ++--- titles/idz/echo.py | 3 +- 4 files changed, 61 insertions(+), 39 deletions(-) diff --git a/titles/idac/echo.py b/titles/idac/echo.py index 08e5526..5520392 100644 --- a/titles/idac/echo.py +++ b/titles/idac/echo.py @@ -10,19 +10,14 @@ from titles.idac.config import IDACConfig from titles.idac.database import IDACData -class IDACEchoUDP(DatagramProtocol): - def __init__(self, cfg: CoreConfig, game_cfg: IDACConfig, port: int) -> None: - super().__init__() - self.port = port - self.core_config = cfg - self.game_config = game_cfg - self.logger = logging.getLogger("idac") +class IDACEchoUDP: + def connection_made(self, transport): + self.transport = transport + + def datagram_received(self, data, addr): + logging.getLogger('idz').debug(f'Received echo from {addr}') + self.transport.sendto(data, addr) - def datagramReceived(self, data, addr): - self.logger.info( - f"UDP Ping from from {addr[0]}:{addr[1]} -> {self.port} - {data.hex()}" - ) - self.transport.write(data, addr) class IDACEchoTCP(BaseRequestHandler): diff --git a/titles/idac/index.py b/titles/idac/index.py index 1aa8050..c1e9d02 100644 --- a/titles/idac/index.py +++ b/titles/idac/index.py @@ -9,10 +9,10 @@ import coloredlogs from os import path from typing import Dict, List, Tuple from logging.handlers import TimedRotatingFileHandler - +import asyncio from core.config import CoreConfig -from core.title import BaseServlet +from core.title import BaseServlet, JSONResponseNoASCII from core.utils import Utils from titles.idac.base import IDACBase from titles.idac.season2 import IDACSeason2 @@ -75,7 +75,8 @@ class IDACServlet(BaseServlet): def get_routes(self) -> List[Route]: return [ - Route("/{version:int}/initiald/{category:str}/{endpoint:str}", self.render_POST, methods=["POST"]) + Route("/{version:int}/initiald/{category:str}/{endpoint:str}", self.render_POST, methods=["POST"]), + Route("/{version:int}/initiald-matching/{endpoint:str}", self.render_POST, methods=["POST"]), ] def get_allnet_info( @@ -87,7 +88,7 @@ class IDACServlet(BaseServlet): return ( f"", # requires http or else it defaults to https - f"http://{self.core_cfg.server.hostname}{t_port}/{game_code}/{game_ver}/", + f"http://{self.core_cfg.server.hostname}{t_port}/{game_ver}/", ) async def render_POST(self, request: Request) -> bytes: @@ -134,8 +135,43 @@ class IDACServlet(BaseServlet): resp = {"status_code": "0"} self.logger.debug(f"Response {resp}") - return JSONResponse(json.dumps(resp, ensure_ascii=False)) + return JSONResponseNoASCII(resp) + async def render_matching(self, request: Request): + url: str = request.path_params.get("endpoint") + ver: int = request.path_params.get("version") + client_ip = Utils.get_ip_addr(request) + req_data = await request.json() + header_application = self.decode_header(request.headers.get('application', '')) + user_id = int(header_application["session"]) + + # self.getMatchingStatus(user_id) + + self.logger.info( + f"IDAC Matching request from {client_ip}: {url} - {req_data}" + ) + + resp = {"status_code": "0"} + if url == "/regist": + self.queue = self.queue + 1 + elif url == "/status": + if req_data.get("cancel_flag"): + self.queue = self.queue - 1 + self.logger.info( + f"IDAC Matching endpoint {client_ip} had quited" + ) + + resp = { + "status_code": "0", + # Only IPv4 is supported + "host": self.game_config.server.matching_host, + "port": self.game_config.server.matching_p2p, + "room_name": "INDTA", + "state": 1, + } + + self.logger.debug(f"Response {resp}") + return JSONResponseNoASCII(resp) def decode_header(self, app: str) -> Dict: ret = {} @@ -147,22 +183,14 @@ class IDACServlet(BaseServlet): return ret def setup(self): - return - """ if self.game_cfg.server.enable: - endpoints.serverFromString( - reactor, - f"tcp:{self.game_cfg.server.matching}:interface={self.core_cfg.server.listen_address}", - ).listen(server.Site(IDACMatching(self.core_cfg, self.game_cfg))) - - reactor.listenUDP( - self.game_cfg.server.echo1, - IDACEchoUDP(self.core_cfg, self.game_cfg, self.game_cfg.server.echo1), - ) - reactor.listenUDP( - self.game_cfg.server.echo2, - IDACEchoUDP(self.core_cfg, self.game_cfg, self.game_cfg.server.echo2), + loop = asyncio.get_running_loop() + asyncio.create_task( + loop.create_datagram_endpoint( + lambda: IDACEchoUDP(), + local_addr=(self.core_cfg.server.listen_address, self.game_cfg.server.echo1) + ) ) - self.logger.info(f"Matching listening on {self.game_cfg.server.matching} with echos on {self.game_cfg.server.echo1} and {self.game_cfg.server.echo2}") - """ + self.logger.info(f"Matching listening on {self.game_cfg.server.matching} with echo on {self.game_cfg.server.echo1}") + diff --git a/titles/idac/season2.py b/titles/idac/season2.py index fabcbcd..2797990 100644 --- a/titles/idac/season2.py +++ b/titles/idac/season2.py @@ -109,9 +109,9 @@ class IDACSeason2(IDACBase): ver_str = version.replace(".", "")[:3] if self.core_cfg.server.is_develop: - domain_api_game = f"http://{self.core_cfg.server.hostname}:{self.core_cfg.server.port}/SDGT/{ver_str}/" + domain_api_game = f"http://{self.core_cfg.server.hostname}:{self.core_cfg.server.port}/{ver_str}/" else: - domain_api_game = f"http://{self.core_cfg.server.hostname}/SDGT/{ver_str}/" + domain_api_game = f"http://{self.core_cfg.server.hostname}/{ver_str}/" return { "status_code": "0", @@ -136,9 +136,9 @@ class IDACSeason2(IDACBase): "server_maintenance_end_hour": 0, "server_maintenance_end_minutes": 0, "domain_api_game": domain_api_game, - "domain_matching": f"http://{self.core_cfg.server.hostname}:{self.game_config.server.matching}", + "domain_matching": f"{domain_api_game}initiald-matching/", "domain_echo1": f"{self.core_cfg.server.hostname}:{self.game_config.server.echo1}", - "domain_echo2": f"{self.core_cfg.server.hostname}:{self.game_config.server.echo2}", + "domain_echo2": f"{self.core_cfg.server.hostname}:{self.game_config.server.echo1}", "domain_ping": f"{self.core_cfg.server.hostname}", "battle_gift_event_master": [], "round_event": [ diff --git a/titles/idz/echo.py b/titles/idz/echo.py index a8ff3a2..82f7003 100644 --- a/titles/idz/echo.py +++ b/titles/idz/echo.py @@ -9,6 +9,5 @@ class IDZEcho: self.transport = transport def datagram_received(self, data, addr): - message = data.decode() - self.logger.debug(f'Received echo from {addr}') + logging.getLogger('idz').debug(f'Received echo from {addr}') self.transport.sendto(data, addr) From 4bc77a8ef6b6475465501b2559b0297b4d6a9461 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 9 Jan 2024 12:44:48 -0500 Subject: [PATCH 09/32] cxb: impl JSONResponseNoASCII --- titles/cxb/index.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/titles/cxb/index.py b/titles/cxb/index.py index 3f719ff..345aa04 100644 --- a/titles/cxb/index.py +++ b/titles/cxb/index.py @@ -1,6 +1,6 @@ from starlette.requests import Request from starlette.routing import Route -from starlette.responses import Response, PlainTextResponse, JSONResponse +from starlette.responses import Response, JSONResponse import traceback import sys import yaml @@ -13,7 +13,7 @@ from typing import Dict, Tuple, List from os import path from core.config import CoreConfig -from core.title import BaseServlet +from core.title import BaseServlet, JSONResponseNoASCII from core.utils import Utils from .config import CxbConfig from .const import CxbConstants @@ -200,7 +200,7 @@ class CxbServlet(BaseServlet): return Response() self.logger.debug(f"{version_string} Response {resp}") - return JSONResponse(resp, ensure_ascii=False) + return JSONResponseNoASCII(resp) async def handle_action(self, request: Request) -> bytes: req_json = await self.preprocess(request) @@ -229,7 +229,7 @@ class CxbServlet(BaseServlet): return Response() self.logger.debug(f"Response {resp}") - return JSONResponse(resp) + return JSONResponseNoASCII(resp) async def handle_auth(self, request: Request) -> bytes: req_json = await self.preprocess(request) @@ -258,4 +258,4 @@ class CxbServlet(BaseServlet): return Response() self.logger.debug(f"Response {resp}") - return JSONResponse(resp) + return JSONResponseNoASCII(resp) From 07cbbcc3776b1c42e3f53cc4d135b9b8a16f8163 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 9 Jan 2024 12:52:53 -0500 Subject: [PATCH 10/32] idac: fix matching --- titles/idac/index.py | 2 +- titles/idac/season2.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/titles/idac/index.py b/titles/idac/index.py index c1e9d02..d6ac62a 100644 --- a/titles/idac/index.py +++ b/titles/idac/index.py @@ -76,7 +76,7 @@ class IDACServlet(BaseServlet): def get_routes(self) -> List[Route]: return [ Route("/{version:int}/initiald/{category:str}/{endpoint:str}", self.render_POST, methods=["POST"]), - Route("/{version:int}/initiald-matching/{endpoint:str}", self.render_POST, methods=["POST"]), + Route("/{version:int}/initiald-matching/{endpoint:str}", self.render_matching, methods=["POST"]), ] def get_allnet_info( diff --git a/titles/idac/season2.py b/titles/idac/season2.py index 2797990..6523177 100644 --- a/titles/idac/season2.py +++ b/titles/idac/season2.py @@ -6,6 +6,7 @@ import json import logging from core.config import CoreConfig +from core.utils import Utils from titles.idac.const import IDACConstants from titles.idac.config import IDACConfig from titles.idac.base import IDACBase @@ -108,10 +109,10 @@ class IDACSeason2(IDACBase): version = headers["device_version"] ver_str = version.replace(".", "")[:3] - if self.core_cfg.server.is_develop: - domain_api_game = f"http://{self.core_cfg.server.hostname}:{self.core_cfg.server.port}/{ver_str}/" - else: + if self.core_cfg.server.is_using_proxy: domain_api_game = f"http://{self.core_cfg.server.hostname}/{ver_str}/" + else: + domain_api_game = f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}/{ver_str}/" return { "status_code": "0", From edd3ce8eadf904e9a51c73dceab5c871aba9b6b8 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 9 Jan 2024 13:57:59 -0500 Subject: [PATCH 11/32] move to alembic --- core/data/alembic/alembic.ini | 33 +- core/data/alembic/env.py | 36 +- .../835b862f9bf0_initial_migration.py | 24 ++ .../d8950c7ce2fc_remove_old_db_mgmt_system.py | 29 ++ core/data/database.py | 321 ++++-------------- core/data/schema/base.py | 53 +-- dbutils.py | 8 +- example_config/core.yaml | 3 +- titles/chuni/__init__.py | 1 - titles/cm/__init__.py | 3 - titles/cxb/__init__.py | 1 - titles/diva/__init__.py | 1 - titles/idac/__init__.py | 1 - titles/idz/__init__.py | 1 - titles/mai2/__init__.py | 1 - titles/ongeki/__init__.py | 1 - titles/pokken/__init__.py | 1 - titles/sao/__init__.py | 1 - titles/wacca/__init__.py | 1 - 19 files changed, 152 insertions(+), 368 deletions(-) create mode 100644 core/data/alembic/versions/835b862f9bf0_initial_migration.py create mode 100644 core/data/alembic/versions/d8950c7ce2fc_remove_old_db_mgmt_system.py diff --git a/core/data/alembic/alembic.ini b/core/data/alembic/alembic.ini index 299f51f..26b89ea 100644 --- a/core/data/alembic/alembic.ini +++ b/core/data/alembic/alembic.ini @@ -1,25 +1,14 @@ # A generic, single database configuration. [alembic] -# path to migration scripts -script_location = . +script_location=. # template used to generate migration files # file_template = %%(rev)s_%%(slug)s -# sys.path path, will be prepended to sys.path if present. -# defaults to the current working directory. -prepend_sys_path = . - -# timezone to use when rendering the date -# within the migration file as well as the filename. -# string value is passed to dateutil.tz.gettz() -# leave blank for localtime -# timezone = - # max length of characters to apply to the # "slug" field -# truncate_slug_length = 40 +#truncate_slug_length = 40 # set to 'true' to run the environment during # the 'revision' command, regardless of autogenerate @@ -31,28 +20,14 @@ prepend_sys_path = . # sourceless = false # version location specification; this defaults -# to ./versions. When using multiple version +# to migrations//versions. When using multiple version # directories, initial revisions must be specified with --version-path -# version_locations = %(here)s/bar %(here)s/bat ./versions +# version_locations = %(here)s/bar %(here)s/bat migrations//versions # the output encoding used when revision files # are written from script.py.mako # output_encoding = utf-8 -sqlalchemy.url = driver://user:pass@localhost/dbname - - -[post_write_hooks] -# post_write_hooks defines scripts or Python functions that are run -# on newly generated revision scripts. See the documentation for further -# detail and examples - -# format using "black" - use the console_scripts runner, against the "black" entrypoint -# hooks=black -# black.type=console_scripts -# black.entrypoint=black -# black.options=-l 79 - # Logging configuration [loggers] keys = root,sqlalchemy,alembic diff --git a/core/data/alembic/env.py b/core/data/alembic/env.py index 70518a2..d532093 100644 --- a/core/data/alembic/env.py +++ b/core/data/alembic/env.py @@ -1,9 +1,9 @@ +from __future__ import with_statement +from alembic import context +from sqlalchemy import engine_from_config, pool from logging.config import fileConfig -from sqlalchemy import engine_from_config -from sqlalchemy import pool - -from alembic import context +from core.data.schema.base import metadata # this is the Alembic Config object, which provides # access to the values within the .ini file in use. @@ -17,7 +17,7 @@ fileConfig(config.config_file_name) # for 'autogenerate' support # from myapp import mymodel # target_metadata = mymodel.Base.metadata -target_metadata = None +target_metadata = metadata # other values from the config, defined by the needs of env.py, # can be acquired: @@ -37,13 +37,11 @@ def run_migrations_offline(): script output. """ + raise Exception('Not implemented or configured!') + url = config.get_main_option("sqlalchemy.url") context.configure( - url=url, - target_metadata=target_metadata, - literal_binds=True, - dialect_opts={"paramstyle": "named"}, - ) + url=url, target_metadata=target_metadata, literal_binds=True) with context.begin_transaction(): context.run_migrations() @@ -56,21 +54,27 @@ def run_migrations_online(): and associate a connection with the context. """ + ini_section = config.get_section(config.config_ini_section) + overrides = context.get_x_argument(as_dictionary=True) + for override in overrides: + ini_section[override] = overrides[override] + connectable = engine_from_config( - config.get_section(config.config_ini_section), - prefix="sqlalchemy.", - poolclass=pool.NullPool, - ) + ini_section, + prefix='sqlalchemy.', + poolclass=pool.NullPool) with connectable.connect() as connection: context.configure( - connection=connection, target_metadata=target_metadata + connection=connection, + target_metadata=target_metadata, + compare_type=True, + compare_server_default=True, ) with context.begin_transaction(): context.run_migrations() - if context.is_offline_mode(): run_migrations_offline() else: diff --git a/core/data/alembic/versions/835b862f9bf0_initial_migration.py b/core/data/alembic/versions/835b862f9bf0_initial_migration.py new file mode 100644 index 0000000..bea17d7 --- /dev/null +++ b/core/data/alembic/versions/835b862f9bf0_initial_migration.py @@ -0,0 +1,24 @@ +"""Initial Migration + +Revision ID: 835b862f9bf0 +Revises: +Create Date: 2024-01-09 13:06:10.787432 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '835b862f9bf0' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + pass + + +def downgrade(): + pass diff --git a/core/data/alembic/versions/d8950c7ce2fc_remove_old_db_mgmt_system.py b/core/data/alembic/versions/d8950c7ce2fc_remove_old_db_mgmt_system.py new file mode 100644 index 0000000..61990bc --- /dev/null +++ b/core/data/alembic/versions/d8950c7ce2fc_remove_old_db_mgmt_system.py @@ -0,0 +1,29 @@ +"""Remove old db mgmt system + +Revision ID: d8950c7ce2fc +Revises: 835b862f9bf0 +Create Date: 2024-01-09 13:43:51.381175 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'd8950c7ce2fc' +down_revision = '835b862f9bf0' +branch_labels = None +depends_on = None + + +def upgrade(): + op.drop_table("schema_versions") + + +def downgrade(): + op.create_table( + "schema_versions", + sa.Column("game", sa.String(4), primary_key=True, nullable=False), + sa.Column("version", sa.Integer, nullable=False, server_default="1"), + mysql_charset="utf8mb4", + ) diff --git a/core/data/database.py b/core/data/database.py index e39d864..1629aaa 100644 --- a/core/data/database.py +++ b/core/data/database.py @@ -1,13 +1,13 @@ import logging, coloredlogs -from typing import Optional, Dict, List +from typing import Optional from sqlalchemy.orm import scoped_session, sessionmaker -from sqlalchemy.exc import SQLAlchemyError from sqlalchemy import create_engine from logging.handlers import TimedRotatingFileHandler -import importlib, os +import os import secrets, string import bcrypt from hashlib import sha256 +import alembic.config from core.config import CoreConfig from core.data.schema import * @@ -15,7 +15,6 @@ from core.utils import Utils class Data: - current_schema_version = 6 engine = None session = None user = None @@ -77,281 +76,85 @@ class Data: ) self.logger.handler_set = True # type: ignore + def __alembic_cmd(self, command: str, *args: str) -> None: + old_dir = os.path.abspath(os.path.curdir) + base_dir = os.path.join(os.path.abspath(os.path.curdir), 'core', 'data', 'alembic') + alembicArgs = [ + "-c", + os.path.join(base_dir, "alembic.ini"), + "-x", + f"script_location={base_dir}", + "-x", + f"sqlalchemy.url={self.__url}", + command, + ] + alembicArgs.extend(args) + os.chdir(base_dir) + alembic.config.main(argv=alembicArgs) + os.chdir(old_dir) + def create_database(self): self.logger.info("Creating databases...") - try: - metadata.create_all(self.__engine.connect()) - except SQLAlchemyError as e: - self.logger.error(f"Failed to create databases! {e}") - return - - games = Utils.get_all_titles() - for game_dir, game_mod in games.items(): - try: - if hasattr(game_mod, "database") and hasattr( - game_mod, "current_schema_version" - ): - game_mod.database(self.config) - metadata.create_all(self.__engine.connect()) - - self.base.touch_schema_ver( - game_mod.current_schema_version, game_mod.game_codes[0] - ) - - except Exception as e: - self.logger.warning( - f"Could not load database schema from {game_dir} - {e}" - ) - - self.logger.info(f"Setting base_schema_ver to {self.current_schema_version}") - self.base.set_schema_ver(self.current_schema_version) - - self.logger.info( - f"Setting user auto_incrememnt to {self.config.database.user_table_autoincrement_start}" - ) - self.user.reset_autoincrement( - self.config.database.user_table_autoincrement_start + metadata.create_all( + self.engine, + checkfirst=True, ) - def recreate_database(self): - self.logger.info("Dropping all databases...") - self.base.execute("SET FOREIGN_KEY_CHECKS=0") - try: - metadata.drop_all(self.__engine.connect()) - except SQLAlchemyError as e: - self.logger.error(f"Failed to drop databases! {e}") - return + for _, mod in Utils.get_all_titles().items(): + if hasattr(mod, "database"): + mod.database(self.config) + metadata.create_all( + self.engine, + checkfirst=True, + ) - for root, dirs, files in os.walk("./titles"): - for dir in dirs: - if not dir.startswith("__"): - try: - mod = importlib.import_module(f"titles.{dir}") + # Stamp the end revision as if alembic had created it, so it can take off after this. + self.__alembic_cmd( + "stamp", + "head", + ) - try: - if hasattr(mod, "database"): - mod.database(self.config) - metadata.drop_all(self.__engine.connect()) + def schema_upgrade(self, ver: str = None): + self.__alembic_cmd( + "upgrade", + "head", + ) - except Exception as e: - self.logger.warning( - f"Could not load database schema from {dir} - {e}" - ) - - except ImportError as e: - self.logger.warning( - f"Failed to load database schema dir {dir} - {e}" - ) - break - - self.base.execute("SET FOREIGN_KEY_CHECKS=1") - - self.create_database() - - def migrate_database(self, game: str, version: Optional[int], action: str) -> None: - old_ver = self.base.get_schema_ver(game) - sql = "" - if version is None: - if not game == "CORE": - titles = Utils.get_all_titles() - - for folder, mod in titles.items(): - if not mod.game_codes[0] == game: - continue - - if hasattr(mod, "current_schema_version"): - version = mod.current_schema_version - - else: - self.logger.warning( - f"current_schema_version not found for {folder}" - ) - - else: - version = self.current_schema_version - - if version is None: - self.logger.warning( - f"Could not determine latest version for {game}, please specify --version" - ) - - if old_ver is None: - self.logger.error( - f"Schema for game {game} does not exist, did you run the creation script?" - ) - return - - if old_ver == version: - self.logger.info( - f"Schema for game {game} is already version {old_ver}, nothing to do" - ) - return - - if action == "upgrade": - for x in range(old_ver, version): - if not os.path.exists( - f"core/data/schema/versions/{game.upper()}_{x + 1}_{action}.sql" - ): - self.logger.error( - f"Could not find {action} script {game.upper()}_{x + 1}_{action}.sql in core/data/schema/versions folder" - ) - return - - with open( - f"core/data/schema/versions/{game.upper()}_{x + 1}_{action}.sql", - "r", - encoding="utf-8", - ) as f: - sql = f.read() - - result = self.base.execute(sql) - if result is None: - self.logger.error("Error execuing sql script!") - return None - - else: - for x in range(old_ver, version, -1): - if not os.path.exists( - f"core/data/schema/versions/{game.upper()}_{x - 1}_{action}.sql" - ): - self.logger.error( - f"Could not find {action} script {game.upper()}_{x - 1}_{action}.sql in core/data/schema/versions folder" - ) - return - - with open( - f"core/data/schema/versions/{game.upper()}_{x - 1}_{action}.sql", - "r", - encoding="utf-8", - ) as f: - sql = f.read() - - result = self.base.execute(sql) - if result is None: - self.logger.error("Error execuing sql script!") - return None - - result = self.base.set_schema_ver(version, game) - if result is None: - self.logger.error("Error setting version in schema_version table!") - return None - - self.logger.info(f"Successfully migrated {game} to schema version {version}") - - def create_owner(self, email: Optional[str] = None) -> None: + async def create_owner(self, email: Optional[str] = None, code: Optional[str] = "00000000000000000000") -> None: pw = "".join( secrets.choice(string.ascii_letters + string.digits) for i in range(20) ) hash = bcrypt.hashpw(pw.encode(), bcrypt.gensalt()) - user_id = self.user.create_user(email=email, permission=255, password=hash) + user_id = await self.user.create_user("sysowner", email, hash.decode(), 255) if user_id is None: self.logger.error(f"Failed to create owner with email {email}") return - card_id = self.card.create_card(user_id, "00000000000000000000") + card_id = await self.card.create_card(user_id, code) if card_id is None: self.logger.error(f"Failed to create card for owner with id {user_id}") return self.logger.warning( - f"Successfully created owner with email {email}, access code 00000000000000000000, and password {pw} Make sure to change this password and assign a real card ASAP!" + f"Successfully created owner with email {email}, access code {code}, and password {pw} Make sure to change this password and assign a real card ASAP!" ) - - def migrate_card(self, old_ac: str, new_ac: str, should_force: bool) -> None: - if old_ac == new_ac: - self.logger.error("Both access codes are the same!") - return - - new_card = self.card.get_card_by_access_code(new_ac) - if new_card is None: - self.card.update_access_code(old_ac, new_ac) - return - - if not should_force: - self.logger.warning( - f"Card already exists for access code {new_ac} (id {new_card['id']}). If you wish to continue, rerun with the '--force' flag." - f" All exiting data on the target card {new_ac} will be perminently erased and replaced with data from card {old_ac}." - ) - return - - self.logger.info( - f"All exiting data on the target card {new_ac} will be perminently erased and replaced with data from card {old_ac}." - ) - self.card.delete_card(new_card["id"]) - self.card.update_access_code(old_ac, new_ac) - - hanging_user = self.user.get_user(new_card["user"]) - if hanging_user["password"] is None: - self.logger.info(f"Delete hanging user {hanging_user['id']}") - self.user.delete_user(hanging_user["id"]) - - def delete_hanging_users(self) -> None: - """ - Finds and deletes users that have not registered for the webui that have no cards assocated with them. - """ - unreg_users = self.user.get_unregistered_users() - if unreg_users is None: - self.logger.error("Error occoured finding unregistered users") - - for user in unreg_users: - cards = self.card.get_user_cards(user["id"]) - if cards is None: - self.logger.error(f"Error getting cards for user {user['id']}") - continue - - if not cards: - self.logger.info(f"Delete hanging user {user['id']}") - self.user.delete_user(user["id"]) - - def autoupgrade(self) -> None: - all_game_versions = self.base.get_all_schema_vers() - if all_game_versions is None: - self.logger.warning("Failed to get schema versions") - return - - all_games = Utils.get_all_titles() - all_games_list: Dict[str, int] = {} - for _, mod in all_games.items(): - if hasattr(mod, "current_schema_version"): - all_games_list[mod.game_codes[0]] = mod.current_schema_version - - for x in all_game_versions: - failed = False - game = x["game"].upper() - update_ver = int(x["version"]) - latest_ver = all_games_list.get(game, 1) - if game == "CORE": - latest_ver = self.current_schema_version - - if update_ver == latest_ver: - self.logger.info(f"{game} is already latest version") - continue - - for y in range(update_ver + 1, latest_ver + 1): - if os.path.exists(f"core/data/schema/versions/{game}_{y}_upgrade.sql"): - with open( - f"core/data/schema/versions/{game}_{y}_upgrade.sql", - "r", - encoding="utf-8", - ) as f: - sql = f.read() - - result = self.base.execute(sql) - if result is None: - self.logger.error( - f"Error execuing sql script for game {game} v{y}!" - ) - failed = True - break - else: - self.logger.warning(f"Could not find script {game}_{y}_upgrade.sql") - failed = True - - if not failed: - self.base.set_schema_ver(latest_ver, game) - def show_versions(self) -> None: - all_game_versions = self.base.get_all_schema_vers() - for ver in all_game_versions: - self.logger.info(f"{ver['game']} -> v{ver['version']}") + async def migrate(self) -> None: + exist = await self.base.execute("SELECT * FROM alembic_version") + if exist is not None: + self.logger.warn("No need to migrate as you have already migrated to alembic. If you are trying to upgrade the schema, use `upgrade` instead!") + return + + self.logger.info("Stamp with initial revision") + self.__alembic_cmd( + "stamp", + "835b862f9bf0", + ) + + self.logger.info("Upgrade") + self.__alembic_cmd( + "upgrade", + "head", + ) + diff --git a/core/data/schema/base.py b/core/data/schema/base.py index ef980e5..1ad65e5 100644 --- a/core/data/schema/base.py +++ b/core/data/schema/base.py @@ -43,11 +43,11 @@ class BaseData: self.conn = conn self.logger = logging.getLogger("database") - def execute(self, sql: str, opts: Dict[str, Any] = {}) -> Optional[CursorResult]: + async def execute(self, sql: str, opts: Dict[str, Any] = {}) -> Optional[CursorResult]: res = None try: - self.logger.info(f"SQL Execute: {''.join(str(sql).splitlines())}") + self.logger.debug(f"SQL Execute: {''.join(str(sql).splitlines())}") res = self.conn.execute(text(sql), opts) except SQLAlchemyError as e: @@ -82,52 +82,7 @@ class BaseData: """ return randrange(10000, 9999999) - def get_all_schema_vers(self) -> Optional[List[Row]]: - sql = select(schema_ver) - - result = self.execute(sql) - if result is None: - return None - return result.fetchall() - - def get_schema_ver(self, game: str) -> Optional[int]: - sql = select(schema_ver).where(schema_ver.c.game == game) - - result = self.execute(sql) - if result is None: - return None - - row = result.fetchone() - if row is None: - return None - - return row["version"] - - def touch_schema_ver(self, ver: int, game: str = "CORE") -> Optional[int]: - sql = insert(schema_ver).values(game=game, version=ver) - conflict = sql.on_duplicate_key_update(version=schema_ver.c.version) - - result = self.execute(conflict) - if result is None: - self.logger.error( - f"Failed to update schema version for game {game} (v{ver})" - ) - return None - return result.lastrowid - - def set_schema_ver(self, ver: int, game: str = "CORE") -> Optional[int]: - sql = insert(schema_ver).values(game=game, version=ver) - conflict = sql.on_duplicate_key_update(version=ver) - - result = self.execute(conflict) - if result is None: - self.logger.error( - f"Failed to update schema version for game {game} (v{ver})" - ) - return None - return result.lastrowid - - def log_event( + async def log_event( self, system: str, type: str, severity: int, message: str, details: Dict = {} ) -> Optional[int]: sql = event_log.insert().values( @@ -147,7 +102,7 @@ class BaseData: return result.lastrowid - def get_event_log(self, entries: int = 100) -> Optional[List[Dict]]: + async def get_event_log(self, entries: int = 100) -> Optional[List[Dict]]: sql = event_log.select().limit(entries).all() result = self.execute(sql) diff --git a/dbutils.py b/dbutils.py index f5e57be..e10b814 100644 --- a/dbutils.py +++ b/dbutils.py @@ -5,7 +5,8 @@ from os import mkdir, path, access, W_OK import yaml import asyncio -from core import Data, CoreConfig +from core.data import Data +from core.config import CoreConfig if __name__ == "__main__": parser = argparse.ArgumentParser(description="Database utilities") @@ -49,6 +50,11 @@ if __name__ == "__main__": elif args.action == "create-owner": loop = asyncio.get_event_loop() loop.run_until_complete(data.create_owner(args.email, args.access_code)) + data.schema_upgrade(args.version) + + elif args.action == "migrate": + loop = asyncio.get_event_loop() + loop.run_until_complete(data.migrate()) else: logging.getLogger("database").info(f"Unknown action {args.action}") diff --git a/example_config/core.yaml b/example_config/core.yaml index f5b11f4..600c66b 100644 --- a/example_config/core.yaml +++ b/example_config/core.yaml @@ -19,6 +19,7 @@ title: reboot_start_time: "04:00" reboot_end_time: "05:00" ssl_key: "cert/title.key" + database: host: "localhost" username: "aime" @@ -27,7 +28,7 @@ database: port: 3306 protocol: "mysql" sha2_password: False - loglevel: "warn" + loglevel: "info" enable_memcached: True memcached_host: "localhost" diff --git a/titles/chuni/__init__.py b/titles/chuni/__init__.py index dc0e2f4..d886d18 100644 --- a/titles/chuni/__init__.py +++ b/titles/chuni/__init__.py @@ -7,4 +7,3 @@ index = ChuniServlet database = ChuniData reader = ChuniReader game_codes = [ChuniConstants.GAME_CODE, ChuniConstants.GAME_CODE_NEW, ChuniConstants.GAME_CODE_INT] -current_schema_version = 5 \ No newline at end of file diff --git a/titles/cm/__init__.py b/titles/cm/__init__.py index 1115f96..a9dad07 100644 --- a/titles/cm/__init__.py +++ b/titles/cm/__init__.py @@ -6,7 +6,4 @@ from titles.cm.database import CardMakerData index = CardMakerServlet reader = CardMakerReader database = CardMakerData - game_codes = [CardMakerConstants.GAME_CODE] - -current_schema_version = 1 diff --git a/titles/cxb/__init__.py b/titles/cxb/__init__.py index 37abdab..cfd4e91 100644 --- a/titles/cxb/__init__.py +++ b/titles/cxb/__init__.py @@ -7,4 +7,3 @@ index = CxbServlet database = CxbData reader = CxbReader game_codes = [CxbConstants.GAME_CODE] -current_schema_version = 1 diff --git a/titles/diva/__init__.py b/titles/diva/__init__.py index 9a9e6ef..d298ba2 100644 --- a/titles/diva/__init__.py +++ b/titles/diva/__init__.py @@ -7,4 +7,3 @@ index = DivaServlet database = DivaData reader = DivaReader game_codes = [DivaConstants.GAME_CODE] -current_schema_version = 6 diff --git a/titles/idac/__init__.py b/titles/idac/__init__.py index 0c632bd..d308d43 100644 --- a/titles/idac/__init__.py +++ b/titles/idac/__init__.py @@ -9,4 +9,3 @@ database = IDACData reader = IDACReader frontend = IDACFrontend game_codes = [IDACConstants.GAME_CODE] -current_schema_version = 1 diff --git a/titles/idz/__init__.py b/titles/idz/__init__.py index 958d08a..457b6a4 100644 --- a/titles/idz/__init__.py +++ b/titles/idz/__init__.py @@ -5,4 +5,3 @@ from titles.idz.database import IDZData index = IDZServlet database = IDZData game_codes = [IDZConstants.GAME_CODE] -current_schema_version = 1 diff --git a/titles/mai2/__init__.py b/titles/mai2/__init__.py index 4857644..962423b 100644 --- a/titles/mai2/__init__.py +++ b/titles/mai2/__init__.py @@ -16,4 +16,3 @@ game_codes = [ Mai2Constants.GAME_CODE_GREEN, Mai2Constants.GAME_CODE, ] -current_schema_version = 8 diff --git a/titles/ongeki/__init__.py b/titles/ongeki/__init__.py index f12343d..587a154 100644 --- a/titles/ongeki/__init__.py +++ b/titles/ongeki/__init__.py @@ -9,4 +9,3 @@ database = OngekiData reader = OngekiReader frontend = OngekiFrontend game_codes = [OngekiConstants.GAME_CODE] -current_schema_version = 6 diff --git a/titles/pokken/__init__.py b/titles/pokken/__init__.py index 94237c4..ace562d 100644 --- a/titles/pokken/__init__.py +++ b/titles/pokken/__init__.py @@ -6,5 +6,4 @@ from .frontend import PokkenFrontend index = PokkenServlet database = PokkenData game_codes = [PokkenConstants.GAME_CODE] -current_schema_version = 1 frontend = PokkenFrontend diff --git a/titles/sao/__init__.py b/titles/sao/__init__.py index 15a46f9..b6943e6 100644 --- a/titles/sao/__init__.py +++ b/titles/sao/__init__.py @@ -7,4 +7,3 @@ index = SaoServlet database = SaoData reader = SaoReader game_codes = [SaoConstants.GAME_CODE] -current_schema_version = 1 diff --git a/titles/wacca/__init__.py b/titles/wacca/__init__.py index a3bf96b..e9584d1 100644 --- a/titles/wacca/__init__.py +++ b/titles/wacca/__init__.py @@ -9,4 +9,3 @@ database = WaccaData reader = WaccaReader frontend = WaccaFrontend game_codes = [WaccaConstants.GAME_CODE] -current_schema_version = 5 From 05586df08a431b1cd13a34235dea52589d3ba4e3 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 9 Jan 2024 14:42:17 -0500 Subject: [PATCH 12/32] move to async database --- core/aimedb.py | 28 +- core/allnet.py | 28 +- core/data/schema/arcade.py | 54 ++-- core/data/schema/base.py | 4 +- core/data/schema/card.py | 30 +- core/data/schema/user.py | 37 +-- core/frontend.py | 62 ++-- example_config/core.yaml | 1 - read.py | 8 +- titles/chuni/base.py | 108 +++---- titles/chuni/new.py | 66 ++--- titles/chuni/read.py | 34 +-- titles/chuni/schema/item.py | 108 +++---- titles/chuni/schema/profile.py | 100 +++---- titles/chuni/schema/score.py | 32 +-- titles/chuni/schema/static.py | 104 +++---- titles/cm/read.py | 40 +-- titles/cxb/base.py | 46 +-- titles/cxb/read.py | 33 +-- titles/cxb/rev.py | 2 +- titles/cxb/schema/item.py | 8 +- titles/cxb/schema/profile.py | 12 +- titles/cxb/schema/score.py | 24 +- titles/cxb/schema/static.py | 12 +- titles/diva/base.py | 96 +++---- titles/diva/read.py | 28 +- titles/diva/schema/customize.py | 10 +- titles/diva/schema/item.py | 8 +- titles/diva/schema/module.py | 10 +- titles/diva/schema/profile.py | 12 +- titles/diva/schema/pv_customize.py | 8 +- titles/diva/schema/score.py | 24 +- titles/diva/schema/static.py | 44 +-- titles/idac/frontend.py | 17 +- titles/idac/index.py | 2 +- titles/idac/read.py | 12 +- titles/idac/schema/item.py | 184 ++++++------ titles/idac/schema/profile.py | 52 ++-- titles/idac/season2.py | 444 ++++++++++++++--------------- titles/idz/userdb.py | 2 +- titles/mai2/base.py | 104 +++---- titles/mai2/dx.py | 72 ++--- titles/mai2/read.py | 46 +-- titles/mai2/schema/item.py | 88 +++--- titles/mai2/schema/profile.py | 100 +++---- titles/mai2/schema/score.py | 24 +- titles/mai2/schema/static.py | 40 +-- titles/mai2/universe.py | 20 +- titles/ongeki/base.py | 142 ++++----- titles/ongeki/bright.py | 68 ++--- titles/ongeki/brightmemory.py | 2 +- titles/ongeki/frontend.py | 10 +- titles/ongeki/read.py | 35 +-- titles/ongeki/schema/item.py | 168 +++++------ titles/ongeki/schema/log.py | 4 +- titles/ongeki/schema/profile.py | 78 ++--- titles/ongeki/schema/score.py | 20 +- titles/ongeki/schema/static.py | 108 +++---- titles/pokken/base.py | 44 +-- titles/pokken/schema/item.py | 4 +- titles/pokken/schema/match.py | 8 +- titles/pokken/schema/profile.py | 62 ++-- titles/sao/handlers/base.py | 34 +-- titles/sao/read.py | 30 +- titles/sao/schema/item.py | 80 +++--- titles/sao/schema/profile.py | 12 +- titles/sao/schema/static.py | 74 ++--- titles/wacca/base.py | 178 ++++++------ titles/wacca/frontend.py | 2 +- titles/wacca/lily.py | 40 +-- titles/wacca/lilyr.py | 20 +- titles/wacca/read.py | 14 +- titles/wacca/reverse.py | 28 +- titles/wacca/schema/item.py | 36 +-- titles/wacca/schema/profile.py | 104 +++---- titles/wacca/schema/score.py | 32 +-- titles/wacca/schema/static.py | 8 +- 77 files changed, 1925 insertions(+), 1948 deletions(-) diff --git a/core/aimedb.py b/core/aimedb.py index c5c1837..727a310 100644 --- a/core/aimedb.py +++ b/core/aimedb.py @@ -177,9 +177,9 @@ class AimedbServlette(): async def handle_lookup(self, data: bytes, resp_code: int) -> ADBBaseResponse: req = ADBLookupRequest(data) - user_id = self.data.card.get_user_id_from_card(req.access_code) - is_banned = self.data.card.get_card_banned(req.access_code) - is_locked = self.data.card.get_card_locked(req.access_code) + user_id = await self.data.card.get_user_id_from_card(req.access_code) + is_banned = await self.data.card.get_card_banned(req.access_code) + is_locked = await self.data.card.get_card_locked(req.access_code) ret = ADBLookupResponse.from_req(req.head, user_id) if is_banned and is_locked: @@ -196,10 +196,10 @@ class AimedbServlette(): async def handle_lookup_ex(self, data: bytes, resp_code: int) -> ADBBaseResponse: req = ADBLookupRequest(data) - user_id = self.data.card.get_user_id_from_card(req.access_code) + user_id = await self.data.card.get_user_id_from_card(req.access_code) - is_banned = self.data.card.get_card_banned(req.access_code) - is_locked = self.data.card.get_card_locked(req.access_code) + is_banned = await self.data.card.get_card_banned(req.access_code) + is_locked = await self.data.card.get_card_locked(req.access_code) ret = ADBLookupExResponse.from_req(req.head, user_id) if is_banned and is_locked: @@ -233,7 +233,7 @@ class AimedbServlette(): be fine. """ req = ADBFelicaLookupRequest(data) - ac = self.data.card.to_access_code(req.idm) + ac = await self.data.card.to_access_code(req.idm) self.logger.info( f"idm {req.idm} ipm {req.pmm} -> access_code {ac}" ) @@ -244,17 +244,17 @@ class AimedbServlette(): I've never seen this used. """ req = ADBFelicaLookupRequest(data) - ac = self.data.card.to_access_code(req.idm) + ac = await self.data.card.to_access_code(req.idm) if self.config.server.allow_user_registration: - user_id = self.data.user.create_user() + user_id = await self.data.user.create_user() if user_id is None: self.logger.error("Failed to register user!") user_id = -1 else: - card_id = self.data.card.create_card(user_id, ac) + card_id = await self.data.card.create_card(user_id, ac) if card_id is None: self.logger.error("Failed to register card!") @@ -273,8 +273,8 @@ class AimedbServlette(): async def handle_felica_lookup_ex(self, data: bytes, resp_code: int) -> bytes: req = ADBFelicaLookup2Request(data) - access_code = self.data.card.to_access_code(req.idm) - user_id = self.data.card.get_user_id_from_card(access_code=access_code) + access_code = await self.data.card.to_access_code(req.idm) + user_id = await self.data.card.get_user_id_from_card(access_code=access_code) if user_id is None: user_id = -1 @@ -308,14 +308,14 @@ class AimedbServlette(): user_id = -1 if self.config.server.allow_user_registration: - user_id = self.data.user.create_user() + user_id = await self.data.user.create_user() if user_id is None: self.logger.error("Failed to register user!") user_id = -1 else: - card_id = self.data.card.create_card(user_id, req.access_code) + card_id = await self.data.card.create_card(user_id, req.access_code) if card_id is None: self.logger.error("Failed to register card!") diff --git a/core/allnet.py b/core/allnet.py index 712b945..219f331 100644 --- a/core/allnet.py +++ b/core/allnet.py @@ -170,10 +170,10 @@ class AllnetServlet: self.logger.debug(f"Allnet request: {vars(req)}") - machine = self.data.arcade.get_machine(req.serial) + machine = await self.data.arcade.get_machine(req.serial) if machine is None and not self.config.server.allow_unregistered_serials: msg = f"Unrecognised serial {req.serial} attempted allnet auth from {request_ip}." - self.data.base.log_event( + await self.data.base.log_event( "allnet", "ALLNET_AUTH_UNKNOWN_SERIAL", logging.WARN, msg ) self.logger.warning(msg) @@ -183,11 +183,11 @@ class AllnetServlet: return PlainTextResponse(urllib.parse.unquote(urllib.parse.urlencode(resp_dict)) + "\n") if machine is not None: - arcade = self.data.arcade.get_arcade(machine["arcade"]) + arcade = await self.data.arcade.get_arcade(machine["arcade"]) if self.config.server.check_arcade_ip: if arcade["ip"] and arcade["ip"] is not None and arcade["ip"] != req.ip: msg = f"Serial {req.serial} attempted allnet auth from bad IP {req.ip} (expected {arcade['ip']})." - self.data.base.log_event( + await self.data.base.log_event( "allnet", "ALLNET_AUTH_BAD_IP", logging.ERROR, msg ) self.logger.warning(msg) @@ -198,7 +198,7 @@ class AllnetServlet: elif (not arcade["ip"] or arcade["ip"] is None) and self.config.server.strict_ip_checking: msg = f"Serial {req.serial} attempted allnet auth from bad IP {req.ip}, but arcade {arcade['id']} has no IP set! (strict checking enabled)." - self.data.base.log_event( + await self.data.base.log_event( "allnet", "ALLNET_AUTH_NO_SHOP_IP", logging.ERROR, msg ) self.logger.warning(msg) @@ -242,7 +242,7 @@ class AllnetServlet: if req.game_id not in TitleServlet.title_registry: if not self.config.server.is_develop: msg = f"Unrecognised game {req.game_id} attempted allnet auth from {request_ip}." - self.data.base.log_event( + await self.data.base.log_event( "allnet", "ALLNET_AUTH_UNKNOWN_GAME", logging.WARN, msg ) self.logger.warning(msg) @@ -269,7 +269,7 @@ class AllnetServlet: resp.uri, resp.host = TitleServlet.title_registry[req.game_id].get_allnet_info(req.game_id, int(int_ver), req.serial) msg = f"{req.serial} authenticated from {request_ip}: {req.game_id} v{req.ver}" - self.data.base.log_event("allnet", "ALLNET_AUTH_SUCCESS", logging.INFO, msg) + await self.data.base.log_event("allnet", "ALLNET_AUTH_SUCCESS", logging.INFO, msg) self.logger.info(msg) resp_dict = {k: v for k, v in vars(resp).items() if v is not None} @@ -335,7 +335,7 @@ class AllnetServlet: resp.uri += f"|http://{self.config.server.hostname}:{self.config.server.port}/dl/ini/{req.game_id}-{req.ver.replace('.', '')}-opt.ini" self.logger.debug(f"Sending download uri {resp.uri}") - self.data.base.log_event("allnet", "DLORDER_REQ_SUCCESS", logging.INFO, f"{Utils.get_ip_addr(request)} requested DL Order for {req.serial} {req.game_id} v{req.ver}") + await self.data.base.log_event("allnet", "DLORDER_REQ_SUCCESS", logging.INFO, f"{Utils.get_ip_addr(request)} requested DL Order for {req.serial} {req.game_id} v{req.ver}") res_str = urllib.parse.unquote(urllib.parse.urlencode(vars(resp))) + "\n" """if is_dfi: @@ -352,7 +352,7 @@ class AllnetServlet: if path.exists(f"{self.config.allnet.update_cfg_folder}/{req_file}"): self.logger.info(f"Request for DL INI file {req_file} from {Utils.get_ip_addr(request)} successful") - self.data.base.log_event("allnet", "DLORDER_INI_SENT", logging.INFO, f"{Utils.get_ip_addr(request)} successfully recieved {req_file}") + await self.data.base.log_event("allnet", "DLORDER_INI_SENT", logging.INFO, f"{Utils.get_ip_addr(request)} successfully recieved {req_file}") return PlainTextResponse(open( f"{self.config.allnet.update_cfg_folder}/{req_file}", "r" @@ -390,7 +390,7 @@ class AllnetServlet: msg = f"{rep.serial} @ {client_ip} reported {rep.rep_type.name} download state {rep.rf_state.name} for {rep.gd} v{rep.dav}:"\ f" {rep.tdsc}/{rep.tsc} segments downloaded for working files {rep.wfl} with {rep.dfl if rep.dfl else 'none'} complete." - self.data.base.log_event("allnet", "DL_REPORT", logging.INFO, msg, dl_data) + await self.data.base.log_event("allnet", "DL_REPORT", logging.INFO, msg, dl_data) self.logger.info(msg) return PlainTextResponse("OK") @@ -540,10 +540,10 @@ class BillingServlet: kc_serial_bytes = req.keychipid.encode() - machine = self.data.arcade.get_machine(req.keychipid) + machine = await self.data.arcade.get_machine(req.keychipid) if machine is None and not self.config.server.allow_unregistered_serials: msg = f"Unrecognised serial {req.keychipid} attempted billing checkin from {request_ip} for {req.gameid} v{req.gamever}." - self.data.base.log_event( + await self.data.base.log_event( "allnet", "BILLING_CHECKIN_NG_SERIAL", logging.WARN, msg ) self.logger.warning(msg) @@ -555,7 +555,7 @@ class BillingServlet: f"{req.playcnt} billing_type {req.billingtype.name} nearfull {req.nearfull} playlimit {req.playlimit}" ) self.logger.info(msg) - self.data.base.log_event("billing", "BILLING_CHECKIN_OK", logging.INFO, msg) + await self.data.base.log_event("billing", "BILLING_CHECKIN_OK", logging.INFO, msg) if req.traceleft > 0: self.logger.warn(f"{req.traceleft} unsent tracelogs") kc_playlimit = req.playlimit @@ -699,7 +699,7 @@ class BillingInfo: self.boardid = str(data.get("boardid", None)) self.tenpoip = str(data.get("tenpoip", None)) self.libalibver = float(data.get("libalibver", None)) - self.datamax = int(data.get("datamax", None)) + self.data.max = int(data.get("datamax", None)) self.billingtype = BillingType(int(data.get("billingtype", None))) self.protocolver = float(data.get("protocolver", None)) self.operatingfix = bool(data.get("operatingfix", None)) diff --git a/core/data/schema/arcade.py b/core/data/schema/arcade.py index 2fb8e43..03d0915 100644 --- a/core/data/schema/arcade.py +++ b/core/data/schema/arcade.py @@ -69,7 +69,7 @@ arcade_owner = Table( class ArcadeData(BaseData): - def get_machine(self, serial: str = None, id: int = None) -> Optional[Row]: + async def get_machine(self, serial: str = None, id: int = None) -> Optional[Row]: if serial is not None: serial = serial.replace("-", "") if len(serial) == 11: @@ -89,12 +89,12 @@ class ArcadeData(BaseData): self.logger.error(f"{__name__ }: Need either serial or ID to look up!") return None - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_machine( + async def put_machine( self, arcade_id: int, serial: str = "", @@ -110,13 +110,13 @@ class ArcadeData(BaseData): arcade=arcade_id, keychip=serial, board=board, game=game, is_cab=is_cab ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.lastrowid - def set_machine_serial(self, machine_id: int, serial: str) -> None: - result = self.execute( + async def set_machine_serial(self, machine_id: int, serial: str) -> None: + result = await self.execute( machine.update(machine.c.id == machine_id).values(keychip=serial) ) if result is None: @@ -125,8 +125,8 @@ class ArcadeData(BaseData): ) return result.lastrowid - def set_machine_boardid(self, machine_id: int, boardid: str) -> None: - result = self.execute( + async def set_machine_boardid(self, machine_id: int, boardid: str) -> None: + result = await self.execute( machine.update(machine.c.id == machine_id).values(board=boardid) ) if result is None: @@ -134,21 +134,21 @@ class ArcadeData(BaseData): f"Failed to update board id for machine {machine_id} -> {boardid}" ) - def get_arcade(self, id: int) -> Optional[Row]: + async def get_arcade(self, id: int) -> Optional[Row]: sql = arcade.select(arcade.c.id == id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_arcade_machines(self, id: int) -> Optional[List[Row]]: + async def get_arcade_machines(self, id: int) -> Optional[List[Row]]: sql = machine.select(machine.c.arcade == id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_arcade( + async def put_arcade( self, name: str, nickname: str = None, @@ -171,42 +171,42 @@ class ArcadeData(BaseData): regional_id=regional_id, ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.lastrowid - def get_arcades_managed_by_user(self, user_id: int) -> Optional[List[Row]]: + async def get_arcades_managed_by_user(self, user_id: int) -> Optional[List[Row]]: sql = select(arcade).join(arcade_owner, arcade_owner.c.arcade == arcade.c.id).where(arcade_owner.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return False return result.fetchall() - def get_manager_permissions(self, user_id: int, arcade_id: int) -> Optional[int]: + async def get_manager_permissions(self, user_id: int, arcade_id: int) -> Optional[int]: sql = select(arcade_owner.c.permissions).where(and_(arcade_owner.c.user == user_id, arcade_owner.c.arcade == arcade_id)) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return False return result.fetchone() - def get_arcade_owners(self, arcade_id: int) -> Optional[Row]: + async def get_arcade_owners(self, arcade_id: int) -> Optional[Row]: sql = select(arcade_owner).where(arcade_owner.c.arcade == arcade_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def add_arcade_owner(self, arcade_id: int, user_id: int) -> None: + async def add_arcade_owner(self, arcade_id: int, user_id: int) -> None: sql = insert(arcade_owner).values(arcade=arcade_id, user=user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.lastrowid - def format_serial( + async def format_serial( self, platform_code: str, platform_rev: int, serial_num: int, append: int = 4152 ) -> str: return f"{platform_code}{platform_rev:02d}A{serial_num:04d}{append:04d}" # 0x41 = A, 0x52 = R @@ -217,16 +217,16 @@ class ArcadeData(BaseData): return True - def get_arcade_by_name(self, name: str) -> Optional[List[Row]]: + async def get_arcade_by_name(self, name: str) -> Optional[List[Row]]: sql = arcade.select(or_(arcade.c.name.like(f"%{name}%"), arcade.c.nickname.like(f"%{name}%"))) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_arcades_by_ip(self, ip: str) -> Optional[List[Row]]: + async def get_arcades_by_ip(self, ip: str) -> Optional[List[Row]]: sql = arcade.select().where(arcade.c.ip == ip) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() diff --git a/core/data/schema/base.py b/core/data/schema/base.py index 1ad65e5..5549ab2 100644 --- a/core/data/schema/base.py +++ b/core/data/schema/base.py @@ -92,7 +92,7 @@ class BaseData: message=message, details=json.dumps(details), ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( @@ -104,7 +104,7 @@ class BaseData: async def get_event_log(self, entries: int = 100) -> Optional[List[Dict]]: sql = event_log.select().limit(entries).all() - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None diff --git a/core/data/schema/card.py b/core/data/schema/card.py index a95684e..6953b26 100644 --- a/core/data/schema/card.py +++ b/core/data/schema/card.py @@ -27,34 +27,34 @@ aime_card = Table( class CardData(BaseData): - def get_card_by_access_code(self, access_code: str) -> Optional[Row]: + async def get_card_by_access_code(self, access_code: str) -> Optional[Row]: sql = aime_card.select(aime_card.c.access_code == access_code) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_card_by_id(self, card_id: int) -> Optional[Row]: + async def get_card_by_id(self, card_id: int) -> Optional[Row]: sql = aime_card.select(aime_card.c.id == card_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def update_access_code(self, old_ac: str, new_ac: str) -> None: + async def update_access_code(self, old_ac: str, new_ac: str) -> None: sql = aime_card.update(aime_card.c.access_code == old_ac).values( access_code=new_ac ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"Failed to change card access code from {old_ac} to {new_ac}" ) - def get_user_id_from_card(self, access_code: str) -> Optional[int]: + async def get_user_id_from_card(self, access_code: str) -> Optional[int]: """ Given a 20 digit access code as a string, get the user id associated with that card """ @@ -64,7 +64,7 @@ class CardData(BaseData): return int(card["user"]) - def get_card_banned(self, access_code: str) -> Optional[bool]: + async def get_card_banned(self, access_code: str) -> Optional[bool]: """ Given a 20 digit access code as a string, check if the card is banned """ @@ -74,7 +74,7 @@ class CardData(BaseData): if card["is_banned"]: return True return False - def get_card_locked(self, access_code: str) -> Optional[bool]: + async def get_card_locked(self, access_code: str) -> Optional[bool]: """ Given a 20 digit access code as a string, check if the card is locked """ @@ -85,29 +85,29 @@ class CardData(BaseData): return True return False - def delete_card(self, card_id: int) -> None: + async def delete_card(self, card_id: int) -> None: sql = aime_card.delete(aime_card.c.id == card_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error(f"Failed to delete card with id {card_id}") - def get_user_cards(self, aime_id: int) -> Optional[List[Row]]: + async def get_user_cards(self, aime_id: int) -> Optional[List[Row]]: """ Returns all cards owned by a user """ sql = aime_card.select(aime_card.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def create_card(self, user_id: int, access_code: str) -> Optional[int]: + async def create_card(self, user_id: int, access_code: str) -> Optional[int]: """ Given a aime_user id and a 20 digit access code as a string, create a card and return the ID if successful """ sql = aime_card.insert().values(user=user_id, access_code=access_code) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.lastrowid diff --git a/core/data/schema/user.py b/core/data/schema/user.py index 221ba81..6b733e2 100644 --- a/core/data/schema/user.py +++ b/core/data/schema/user.py @@ -1,4 +1,3 @@ -from enum import Enum from typing import Optional, List from sqlalchemy import Table, Column from sqlalchemy.types import Integer, String, TIMESTAMP @@ -24,15 +23,8 @@ aime_user = Table( mysql_charset="utf8mb4", ) - -class PermissionBits(Enum): - PermUser = 1 - PermMod = 2 - PermSysAdmin = 4 - - class UserData(BaseData): - def create_user( + async def create_user( self, id: int = None, username: str = None, @@ -60,14 +52,14 @@ class UserData(BaseData): username=username, email=email, password=password, permissions=permission ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_user(self, user_id: int) -> Optional[Row]: + async def get_user(self, user_id: int) -> Optional[Row]: sql = select(aime_user).where(aime_user.c.id == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return False return result.fetchone() @@ -85,39 +77,34 @@ class UserData(BaseData): return bcrypt.checkpw(passwd, usr["password"].encode()) - def reset_autoincrement(self, ai_value: int) -> None: - # ALTER TABLE isn't in sqlalchemy so we do this the ugly way - sql = f"ALTER TABLE aime_user AUTO_INCREMENT={ai_value}" - self.execute(sql) - - def delete_user(self, user_id: int) -> None: + async def delete_user(self, user_id: int) -> None: sql = aime_user.delete(aime_user.c.id == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error(f"Failed to delete user with id {user_id}") - def get_unregistered_users(self) -> List[Row]: + async def get_unregistered_users(self) -> List[Row]: """ Returns a list of users who have not registered with the webui. They may or may not have cards. """ sql = select(aime_user).where(aime_user.c.password == None) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def find_user_by_email(self, email: str) -> Row: + async def find_user_by_email(self, email: str) -> Row: sql = select(aime_user).where(aime_user.c.email == email) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return False return result.fetchone() - def find_user_by_username(self, username: str) -> List[Row]: + async def find_user_by_username(self, username: str) -> List[Row]: sql = aime_user.select(aime_user.c.username.like(f"%{username}%")) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return False return result.fetchall() diff --git a/core/frontend.py b/core/frontend.py index 054092c..26796d3 100644 --- a/core/frontend.py +++ b/core/frontend.py @@ -110,7 +110,7 @@ class FrontendServlet(resource.Resource): async def robots(cls, request: Request) -> PlainTextResponse: return PlainTextResponse("User-agent: *\nDisallow: /\n\nUser-agent: AdsBot-Google\nDisallow: /") - def render_GET(self, request): + async def render_GET(self, request): self.logger.debug(f"{Utils.get_ip_addr(request)} -> {request.uri.decode()}") template = self.environment.get_template("core/frontend/index.jinja") return template.render( @@ -167,7 +167,7 @@ class FE_Gate(FE_Base): sesh=vars(usr_sesh), ).encode("utf-16") - def render_POST(self, request: Request): + async def render_POST(self, request: Request): uri = request.uri.decode() ip = Utils.get_ip_addr(request) @@ -177,13 +177,13 @@ class FE_Gate(FE_Base): if passwd == b"": passwd = None - uid = self.data.card.get_user_id_from_card(access_code) - user = self.data.user.get_user(uid) + uid = await self.data.card.get_user_id_from_card(access_code) + user = await self.data.user.get_user(uid) if uid is None: return redirectTo(b"/gate?e=1", request) if passwd is None: - sesh = self.data.user.check_password(uid) + sesh = await self.data.user.check_password(uid) if sesh is not None: return redirectTo( @@ -210,14 +210,14 @@ class FE_Gate(FE_Base): email: str = request.args[b"email"][0].decode() passwd: bytes = request.args[b"passwd"][0] - uid = self.data.card.get_user_id_from_card(access_code) + uid = await self.data.card.get_user_id_from_card(access_code) if uid is None: return redirectTo(b"/gate?e=1", request) salt = bcrypt.gensalt() hashed = bcrypt.hashpw(passwd, salt) - result = self.data.user.create_user( + result = await self.data.user.create_user( uid, username, email.lower(), hashed.decode(), 1 ) if result is None: @@ -231,16 +231,16 @@ class FE_Gate(FE_Base): else: return b"" - def create_user(self, request: Request): + async def create_user(self, request: Request): if b"ac" not in request.args or len(request.args[b"ac"][0].decode()) != 20: return redirectTo(b"/gate?e=2", request) ac = request.args[b"ac"][0].decode() - card = self.data.card.get_card_by_access_code(ac) + card = await self.data.card.get_card_by_access_code(ac) if card is None: return redirectTo(b"/gate?e=1", request) - user = self.data.user.get_user(card['user']) + user = await self.data.user.get_user(card['user']) if user is None: self.logger.warning(f"Card {ac} exists with no/invalid associated user ID {card['user']}") return redirectTo(b"/gate?e=0", request) @@ -257,7 +257,7 @@ class FE_Gate(FE_Base): class FE_User(FE_Base): - def render_GET(self, request: Request): + async def render_GET(self, request: Request): uri = request.uri.decode() template = self.environment.get_template("core/frontend/user/index.jinja") @@ -276,12 +276,12 @@ class FE_User(FE_Base): else: usrid = usr_sesh.userId - user = self.data.user.get_user(usrid) + user = await self.data.user.get_user(usrid) if user is None: return redirectTo(b"/user", request) - cards = self.data.card.get_user_cards(usrid) - arcades = self.data.arcade.get_arcades_managed_by_user(usrid) + cards = await self.data.card.get_user_cards(usrid) + arcades = await self.data.arcade.get_arcades_managed_by_user(usrid) card_data = [] arcade_data = [] @@ -307,12 +307,12 @@ class FE_User(FE_Base): arcades=arcade_data ).encode("utf-16") - def render_POST(self, request: Request): + async def render_POST(self, request: Request): pass class FE_System(FE_Base): - def render_GET(self, request: Request): + async def render_GET(self, request: Request): uri = request.uri.decode() template = self.environment.get_template("core/frontend/sys/index.jinja") usrlist: List[Dict] = [] @@ -331,17 +331,17 @@ class FE_System(FE_Base): uname_search = uri_parse.get("usrName") if uid_search is not None: - u = self.data.user.get_user(uid_search[0]) + u = await self.data.user.get_user(uid_search[0]) if u is not None: usrlist.append(u._asdict()) elif email_search is not None: - u = self.data.user.find_user_by_email(email_search[0]) + u = await self.data.user.find_user_by_email(email_search[0]) if u is not None: usrlist.append(u._asdict()) elif uname_search is not None: - ul = self.data.user.find_user_by_username(uname_search[0]) + ul = await self.data.user.find_user_by_username(uname_search[0]) for u in ul: usrlist.append(u._asdict()) @@ -353,24 +353,24 @@ class FE_System(FE_Base): ac_ip_search = uri_parse.get("arcadeIp") if ac_id_search is not None: - u = self.data.arcade.get_arcade(ac_id_search[0]) + u = await self.data.arcade.get_arcade(ac_id_search[0]) if u is not None: aclist.append(u._asdict()) elif ac_name_search is not None: - ul = self.data.arcade.get_arcade_by_name(ac_name_search[0]) + ul = await self.data.arcade.get_arcade_by_name(ac_name_search[0]) if ul is not None: for u in ul: aclist.append(u._asdict()) elif ac_user_search is not None: - ul = self.data.arcade.get_arcades_managed_by_user(ac_user_search[0]) + ul = await self.data.arcade.get_arcades_managed_by_user(ac_user_search[0]) if ul is not None: for u in ul: aclist.append(u._asdict()) elif ac_ip_search is not None: - ul = self.data.arcade.get_arcades_by_ip(ac_ip_search[0]) + ul = await self.data.arcade.get_arcades_by_ip(ac_ip_search[0]) if ul is not None: for u in ul: aclist.append(u._asdict()) @@ -382,17 +382,17 @@ class FE_System(FE_Base): cab_acid_search = uri_parse.get("cabAcId") if cab_id_search is not None: - u = self.data.arcade.get_machine(id=cab_id_search[0]) + u = await self.data.arcade.get_machine(id=cab_id_search[0]) if u is not None: cablist.append(u._asdict()) elif cab_serial_search is not None: - u = self.data.arcade.get_machine(serial=cab_serial_search[0]) + u = await self.data.arcade.get_machine(serial=cab_serial_search[0]) if u is not None: cablist.append(u._asdict()) elif cab_acid_search is not None: - ul = self.data.arcade.get_arcade_machines(cab_acid_search[0]) + ul = await self.data.arcade.get_arcade_machines(cab_acid_search[0]) for u in ul: cablist.append(u._asdict()) @@ -414,12 +414,12 @@ class FE_Game(FE_Base): return self return resource.Resource.getChild(self, name, request) - def render_GET(self, request: Request) -> bytes: + async def render_GET(self, request: Request) -> bytes: return redirectTo(b"/user", request) class FE_Arcade(FE_Base): - def render_GET(self, request: Request): + async def render_GET(self, request: Request): uri = request.uri.decode() template = self.environment.get_template("core/frontend/arcade/index.jinja") managed = [] @@ -433,8 +433,8 @@ class FE_Arcade(FE_Base): if m is not None: arcadeid = m.group(1) - perms = self.data.arcade.get_manager_permissions(usr_sesh.userId, arcadeid) - arcade = self.data.arcade.get_arcade(arcadeid) + perms = await self.data.arcade.get_manager_permissions(usr_sesh.userId, arcadeid) + arcade = await self.data.arcade.get_arcade(arcadeid) if perms is None: perms = 0 @@ -452,7 +452,7 @@ class FE_Arcade(FE_Base): class FE_Machine(FE_Base): - def render_GET(self, request: Request): + async def render_GET(self, request: Request): uri = request.uri.decode() template = self.environment.get_template("core/frontend/machine/index.jinja") diff --git a/example_config/core.yaml b/example_config/core.yaml index 600c66b..ac3a69e 100644 --- a/example_config/core.yaml +++ b/example_config/core.yaml @@ -18,7 +18,6 @@ title: loglevel: "info" reboot_start_time: "04:00" reboot_end_time: "05:00" - ssl_key: "cert/title.key" database: host: "localhost" diff --git a/read.py b/read.py index 05bbd1d..8a0ae72 100644 --- a/read.py +++ b/read.py @@ -6,6 +6,7 @@ import yaml from os import path import logging import coloredlogs +import asyncio from logging.handlers import TimedRotatingFileHandler from typing import List, Optional @@ -38,6 +39,9 @@ class BaseReader: ret.append(f"{root}/{dir}") return ret + + async def read(self) -> None: + pass if __name__ == "__main__": @@ -136,6 +140,8 @@ if __name__ == "__main__": for dir, mod in titles.items(): if args.game in mod.game_codes: handler = mod.reader(config, args.version, bin_arg, opt_arg, args.extra) - handler.read() + loop = asyncio.get_event_loop() + loop.run_until_complete(handler.read()) + logger.info("Done") diff --git a/titles/chuni/base.py b/titles/chuni/base.py index cffcb75..72d665e 100644 --- a/titles/chuni/base.py +++ b/titles/chuni/base.py @@ -38,20 +38,20 @@ class ChuniBase: return {"returnCode": 1} user_id = data["userId"] - login_bonus_presets = self.data.static.get_login_bonus_presets(self.version) + login_bonus_presets = await self.data.static.get_login_bonus_presets(self.version) for preset in login_bonus_presets: # check if a user already has some pogress and if not add the # login bonus entry - user_login_bonus = self.data.item.get_login_bonus( + user_login_bonus = await self.data.item.get_login_bonus( user_id, self.version, preset["presetId"] ) if user_login_bonus is None: - self.data.item.put_login_bonus( + await self.data.item.put_login_bonus( user_id, self.version, preset["presetId"] ) # yeah i'm lazy - user_login_bonus = self.data.item.get_login_bonus( + user_login_bonus = await self.data.item.get_login_bonus( user_id, self.version, preset["presetId"] ) @@ -67,7 +67,7 @@ class ChuniBase: bonus_count = user_login_bonus["bonusCount"] + 1 last_update_date = datetime.now() - all_login_boni = self.data.static.get_login_bonus( + all_login_boni = await self.data.static.get_login_bonus( self.version, preset["presetId"] ) @@ -91,13 +91,13 @@ class ChuniBase: is_finished = True # grab the item for the corresponding day - login_item = self.data.static.get_login_bonus_by_required_days( + login_item = await self.data.static.get_login_bonus_by_required_days( self.version, preset["presetId"], bonus_count ) if login_item is not None: # now add the present to the database so the # handle_get_user_item_api_request can grab them - self.data.item.put_item( + await self.data.item.put_item( user_id, { "itemId": login_item["presentId"], @@ -107,7 +107,7 @@ class ChuniBase: }, ) - self.data.item.put_login_bonus( + await self.data.item.put_login_bonus( user_id, self.version, preset["presetId"], @@ -124,7 +124,7 @@ class ChuniBase: return {"returnCode": 1} async def handle_get_game_charge_api_request(self, data: Dict) -> Dict: - game_charge_list = self.data.static.get_enabled_charges(self.version) + game_charge_list = await self.data.static.get_enabled_charges(self.version) if game_charge_list is None or len(game_charge_list) == 0: return {"length": 0, "gameChargeList": []} @@ -146,7 +146,7 @@ class ChuniBase: return {"length": len(charges), "gameChargeList": charges} async def handle_get_game_event_api_request(self, data: Dict) -> Dict: - game_events = self.data.static.get_enabled_events(self.version) + game_events = await self.data.static.get_enabled_events(self.version) if game_events is None or len(game_events) == 0: self.logger.warning("No enabled events, did you run the reader?") @@ -194,7 +194,7 @@ class ChuniBase: } async def handle_get_game_ranking_api_request(self, data: Dict) -> Dict: - rankings = self.data.score.get_rankings(self.version) + rankings = await self.data.score.get_rankings(self.version) return {"type": data["type"], "gameRankingList": rankings} async def handle_get_game_sale_api_request(self, data: Dict) -> Dict: @@ -241,7 +241,7 @@ class ChuniBase: "isAou": "false", } async def handle_get_user_activity_api_request(self, data: Dict) -> Dict: - user_activity_list = self.data.profile.get_profile_activity( + user_activity_list = await self.data.profile.get_profile_activity( data["userId"], data["kind"] ) @@ -262,7 +262,7 @@ class ChuniBase: } async def handle_get_user_character_api_request(self, data: Dict) -> Dict: - characters = self.data.item.get_characters(data["userId"]) + characters = await self.data.item.get_characters(data["userId"]) if characters is None: return { "userId": data["userId"], @@ -297,7 +297,7 @@ class ChuniBase: } async def handle_get_user_charge_api_request(self, data: Dict) -> Dict: - user_charge_list = self.data.profile.get_profile_charge(data["userId"]) + user_charge_list = await self.data.profile.get_profile_charge(data["userId"]) charge_list = [] for charge in user_charge_list: @@ -320,7 +320,7 @@ class ChuniBase: } async def handle_get_user_course_api_request(self, data: Dict) -> Dict: - user_course_list = self.data.score.get_courses(data["userId"]) + user_course_list = await self.data.score.get_courses(data["userId"]) if user_course_list is None: return { "userId": data["userId"], @@ -355,7 +355,7 @@ class ChuniBase: } async def handle_get_user_data_api_request(self, data: Dict) -> Dict: - p = self.data.profile.get_profile_data(data["userId"], self.version) + p = await self.data.profile.get_profile_data(data["userId"], self.version) if p is None: return {} @@ -367,7 +367,7 @@ class ChuniBase: return {"userId": data["userId"], "userData": profile} async def handle_get_user_data_ex_api_request(self, data: Dict) -> Dict: - p = self.data.profile.get_profile_data_ex(data["userId"], self.version) + p = await self.data.profile.get_profile_data_ex(data["userId"], self.version) if p is None: return {} @@ -379,7 +379,7 @@ class ChuniBase: return {"userId": data["userId"], "userDataEx": profile} async def handle_get_user_duel_api_request(self, data: Dict) -> Dict: - user_duel_list = self.data.item.get_duels(data["userId"]) + user_duel_list = await self.data.item.get_duels(data["userId"]) if user_duel_list is None: return {} @@ -397,7 +397,7 @@ class ChuniBase: } async def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict: - p = self.data.profile.get_rival(data["rivalId"]) + p = await self.data.profile.get_rival(data["rivalId"]) if p is None: return {} userRivalData = { @@ -416,7 +416,7 @@ class ChuniBase: user_rival_music_list = [] # Fetch all the rival music entries for the user - all_entries = self.data.score.get_rival_music(rival_id) + all_entries = await self.data.score.get_rival_music(rival_id) # Process the entries based on max_count and nextIndex for music in all_entries: @@ -467,7 +467,7 @@ class ChuniBase: # still needs to be implemented on WebUI # 1: Music, 2: User, 3: Character - fav_list = self.data.item.get_all_favorites( + fav_list = await self.data.item.get_all_favorites( data["userId"], self.version, fav_kind=int(data["kind"]) ) if fav_list is not None: @@ -492,7 +492,7 @@ class ChuniBase: async def handle_get_user_item_api_request(self, data: Dict) -> Dict: kind = int(int(data["nextIndex"]) / 10000000000) next_idx = int(int(data["nextIndex"]) % 10000000000) - user_item_list = self.data.item.get_items(data["userId"], kind) + user_item_list = await self.data.item.get_items(data["userId"], kind) if user_item_list is None or len(user_item_list) == 0: return { @@ -528,7 +528,7 @@ class ChuniBase: async def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict: user_id = data["userId"] - user_login_bonus = self.data.item.get_all_login_bonus(user_id, self.version) + user_login_bonus = await self.data.item.get_all_login_bonus(user_id, self.version) # ignore the loginBonus request if its disabled in config if user_login_bonus is None or not self.game_cfg.mods.use_login_bonus: return {"userId": user_id, "length": 0, "userLoginBonusList": []} @@ -553,7 +553,7 @@ class ChuniBase: } async def handle_get_user_map_api_request(self, data: Dict) -> Dict: - user_map_list = self.data.item.get_maps(data["userId"]) + user_map_list = await self.data.item.get_maps(data["userId"]) if user_map_list is None: return {} @@ -571,7 +571,7 @@ class ChuniBase: } async def handle_get_user_music_api_request(self, data: Dict) -> Dict: - music_detail = self.data.score.get_scores(data["userId"]) + music_detail = await self.data.score.get_scores(data["userId"]) if music_detail is None: return { "userId": data["userId"], @@ -630,7 +630,7 @@ class ChuniBase: } async def handle_get_user_option_api_request(self, data: Dict) -> Dict: - p = self.data.profile.get_profile_option(data["userId"]) + p = await self.data.profile.get_profile_option(data["userId"]) option = p._asdict() option.pop("id") @@ -639,7 +639,7 @@ class ChuniBase: return {"userId": data["userId"], "userGameOption": option} async def handle_get_user_option_ex_api_request(self, data: Dict) -> Dict: - p = self.data.profile.get_profile_option_ex(data["userId"]) + p = await self.data.profile.get_profile_option_ex(data["userId"]) option = p._asdict() option.pop("id") @@ -651,10 +651,10 @@ class ChuniBase: return bytes([ord(c) for c in src]).decode("utf-8") async def handle_get_user_preview_api_request(self, data: Dict) -> Dict: - profile = self.data.profile.get_profile_preview(data["userId"], self.version) + profile = await self.data.profile.get_profile_preview(data["userId"], self.version) if profile is None: return None - profile_character = self.data.item.get_character( + profile_character = await self.data.item.get_character( data["userId"], profile["characterId"] ) @@ -693,7 +693,7 @@ class ChuniBase: } async def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict: - recent_rating_list = self.data.profile.get_profile_recent_rating(data["userId"]) + recent_rating_list = await self.data.profile.get_profile_recent_rating(data["userId"]) if recent_rating_list is None: return { "userId": data["userId"], @@ -722,15 +722,15 @@ class ChuniBase: team_rank = 0 # Get user profile - profile = self.data.profile.get_profile_data(data["userId"], self.version) + profile = await self.data.profile.get_profile_data(data["userId"], self.version) if profile and profile["teamId"]: # Get team by id - team = self.data.profile.get_team_by_id(profile["teamId"]) + team = await self.data.profile.get_team_by_id(profile["teamId"]) if team: team_id = team["id"] team_name = team["teamName"] - team_rank = self.data.profile.get_team_rank(team["id"]) + team_rank = await self.data.profile.get_team_rank(team["id"]) # Don't return anything if no team name has been defined for defaults and there is no team set for the player if not profile["teamId"] and team_name == "": @@ -819,58 +819,58 @@ class ChuniBase: except Exception: pass - self.data.profile.put_profile_data( + await self.data.profile.put_profile_data( user_id, self.version, upsert["userData"][0] ) if "userDataEx" in upsert: - self.data.profile.put_profile_data_ex( + await self.data.profile.put_profile_data_ex( user_id, self.version, upsert["userDataEx"][0] ) if "userGameOption" in upsert: - self.data.profile.put_profile_option(user_id, upsert["userGameOption"][0]) + await self.data.profile.put_profile_option(user_id, upsert["userGameOption"][0]) if "userGameOptionEx" in upsert: - self.data.profile.put_profile_option_ex( + await self.data.profile.put_profile_option_ex( user_id, upsert["userGameOptionEx"][0] ) if "userRecentRatingList" in upsert: - self.data.profile.put_profile_recent_rating( + await self.data.profile.put_profile_recent_rating( user_id, upsert["userRecentRatingList"] ) if "userCharacterList" in upsert: for character in upsert["userCharacterList"]: - self.data.item.put_character(user_id, character) + await self.data.item.put_character(user_id, character) if "userMapList" in upsert: for map in upsert["userMapList"]: - self.data.item.put_map(user_id, map) + await self.data.item.put_map(user_id, map) if "userCourseList" in upsert: for course in upsert["userCourseList"]: - self.data.score.put_course(user_id, course) + await self.data.score.put_course(user_id, course) if "userDuelList" in upsert: for duel in upsert["userDuelList"]: - self.data.item.put_duel(user_id, duel) + await self.data.item.put_duel(user_id, duel) if "userItemList" in upsert: for item in upsert["userItemList"]: - self.data.item.put_item(user_id, item) + await self.data.item.put_item(user_id, item) if "userActivityList" in upsert: for activity in upsert["userActivityList"]: - self.data.profile.put_profile_activity(user_id, activity) + await self.data.profile.put_profile_activity(user_id, activity) if "userChargeList" in upsert: for charge in upsert["userChargeList"]: - self.data.profile.put_profile_charge(user_id, charge) + await self.data.profile.put_profile_charge(user_id, charge) if "userMusicDetailList" in upsert: for song in upsert["userMusicDetailList"]: - self.data.score.put_score(user_id, song) + await self.data.score.put_score(user_id, song) if "userPlaylogList" in upsert: for playlog in upsert["userPlaylogList"]: @@ -881,7 +881,7 @@ class ChuniBase: playlog["playedUserName2"] = self.read_wtf8(playlog["playedUserName2"]) if playlog["playedUserName3"] is not None: playlog["playedUserName3"] = self.read_wtf8(playlog["playedUserName3"]) - self.data.score.put_playlog(user_id, playlog, self.version) + await self.data.score.put_playlog(user_id, playlog, self.version) if "userTeamPoint" in upsert: team_points = upsert["userTeamPoint"] @@ -889,7 +889,7 @@ class ChuniBase: for tp in team_points: if tp["teamId"] != '65535': # Fetch the current team data - current_team = self.data.profile.get_team_by_id(tp["teamId"]) + current_team = await self.data.profile.get_team_by_id(tp["teamId"]) # Calculate the new teamPoint new_team_point = int(tp["teamPoint"]) + current_team["teamPoint"] @@ -900,24 +900,24 @@ class ChuniBase: } # Update the team data - self.data.profile.update_team(tp["teamId"], team_data) + await self.data.profile.update_team(tp["teamId"], team_data) except: pass # Probably a better way to catch if the team is not set yet (new profiles), but let's just pass if "userMapAreaList" in upsert: for map_area in upsert["userMapAreaList"]: - self.data.item.put_map_area(user_id, map_area) + await self.data.item.put_map_area(user_id, map_area) if "userOverPowerList" in upsert: for overpower in upsert["userOverPowerList"]: - self.data.profile.put_profile_overpower(user_id, overpower) + await self.data.profile.put_profile_overpower(user_id, overpower) if "userEmoneyList" in upsert: for emoney in upsert["userEmoneyList"]: - self.data.profile.put_profile_emoney(user_id, emoney) + await self.data.profile.put_profile_emoney(user_id, emoney) if "userLoginBonusList" in upsert: for login in upsert["userLoginBonusList"]: - self.data.item.put_login_bonus( + await self.data.item.put_login_bonus( user_id, self.version, login["presetId"], isWatched=True ) @@ -930,7 +930,7 @@ class ChuniBase: async def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict: # add tickets after they got bought, this makes sure the tickets are # still valid after an unsuccessful logout - self.data.profile.put_profile_charge(data["userId"], data["userCharge"]) + await self.data.profile.put_profile_charge(data["userId"], data["userCharge"]) return {"returnCode": "1"} async def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict: diff --git a/titles/chuni/new.py b/titles/chuni/new.py index 9902906..405c25b 100644 --- a/titles/chuni/new.py +++ b/titles/chuni/new.py @@ -102,7 +102,7 @@ class ChuniNew(ChuniBase): return {"returnCode": "1"} async def handle_get_user_map_area_api_request(self, data: Dict) -> Dict: - user_map_areas = self.data.item.get_map_areas(data["userId"]) + user_map_areas = await self.data.item.get_map_areas(data["userId"]) map_areas = [] for map_area in user_map_areas: @@ -117,10 +117,10 @@ class ChuniNew(ChuniBase): return {"userId": data["userId"], "symbolCharInfoList": []} async def handle_get_user_preview_api_request(self, data: Dict) -> Dict: - profile = self.data.profile.get_profile_preview(data["userId"], self.version) + profile = await self.data.profile.get_profile_preview(data["userId"], self.version) if profile is None: return None - profile_character = self.data.item.get_character( + profile_character = await self.data.item.get_character( data["userId"], profile["characterId"] ) @@ -165,7 +165,7 @@ class ChuniNew(ChuniBase): return data1 async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: - p = self.data.profile.get_profile_data(data["userId"], self.version) + p = await self.data.profile.get_profile_data(data["userId"], self.version) if p is None: return {} @@ -187,7 +187,7 @@ class ChuniNew(ChuniBase): """ returns all current active banners (gachas) """ - game_gachas = self.data.static.get_gachas(self.version) + game_gachas = await self.data.static.get_gachas(self.version) # clean the database rows game_gacha_list = [] @@ -217,7 +217,7 @@ class ChuniNew(ChuniBase): """ returns all valid cards for a given gachaId """ - game_gacha_cards = self.data.static.get_gacha_cards(data["gachaId"]) + game_gacha_cards = await self.data.static.get_gacha_cards(data["gachaId"]) game_gacha_card_list = [] for gacha_card in game_gacha_cards: @@ -238,7 +238,7 @@ class ChuniNew(ChuniBase): } async def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict: - p = self.data.profile.get_profile_data(data["userId"], self.version) + p = await self.data.profile.get_profile_data(data["userId"], self.version) if p is None: return {} @@ -263,7 +263,7 @@ class ChuniNew(ChuniBase): } async def handle_get_user_gacha_api_request(self, data: Dict) -> Dict: - user_gachas = self.data.item.get_user_gachas(data["userId"]) + user_gachas = await self.data.item.get_user_gachas(data["userId"]) if user_gachas is None: return {"userId": data["userId"], "length": 0, "userGachaList": []} @@ -282,7 +282,7 @@ class ChuniNew(ChuniBase): } async def handle_get_user_printed_card_api_request(self, data: Dict) -> Dict: - user_print_list = self.data.item.get_user_print_states( + user_print_list = await self.data.item.get_user_print_states( data["userId"], has_completed=True ) if user_print_list is None: @@ -319,7 +319,7 @@ class ChuniNew(ChuniBase): async def handle_get_user_card_print_error_api_request(self, data: Dict) -> Dict: user_id = data["userId"] - user_print_states = self.data.item.get_user_print_states( + user_print_states = await self.data.item.get_user_print_states( user_id, has_completed=False ) @@ -362,14 +362,14 @@ class ChuniNew(ChuniBase): # characterId should be returned if chara_id != -1: # get the - card = self.data.static.get_gacha_card_by_character(gacha_id, chara_id) + card = await self.data.static.get_gacha_card_by_character(gacha_id, chara_id) tmp = card._asdict() tmp.pop("id") rolled_cards.append(tmp) else: - gacha_cards = self.data.static.get_gacha_cards(gacha_id) + gacha_cards = await self.data.static.get_gacha_cards(gacha_id) # get the card id for each roll for _ in range(num_rolls): @@ -396,7 +396,7 @@ class ChuniNew(ChuniBase): user_data.pop("rankUpChallengeResults") user_data.pop("userEmoney") - self.data.profile.put_profile_data(user_id, self.version, user_data) + await self.data.profile.put_profile_data(user_id, self.version, user_data) # save the user gacha user_gacha = upsert["userGacha"] @@ -404,16 +404,16 @@ class ChuniNew(ChuniBase): user_gacha.pop("gachaId") user_gacha.pop("dailyGachaDate") - self.data.item.put_user_gacha(user_id, gacha_id, user_gacha) + await self.data.item.put_user_gacha(user_id, gacha_id, user_gacha) # save all user items if "userItemList" in upsert: for item in upsert["userItemList"]: - self.data.item.put_item(user_id, item) + await self.data.item.put_item(user_id, item) # add every gamegachaCard to database for card in upsert["gameGachaCardList"]: - self.data.item.put_user_print_state( + await self.data.item.put_user_print_state( user_id, hasCompleted=False, placeId=place_id, @@ -423,7 +423,7 @@ class ChuniNew(ChuniBase): # retrieve every game gacha card which has been added in order to get # the orderId for the next request - user_print_states = self.data.item.get_user_print_states_by_gacha( + user_print_states = await self.data.item.get_user_print_states_by_gacha( user_id, gacha_id, has_completed=False ) card_print_state_list = [] @@ -465,7 +465,7 @@ class ChuniNew(ChuniBase): ) # add the entry to the user print table with the random serialId - self.data.item.put_user_print_detail(user_id, serial_id, user_print_detail) + await self.data.item.put_user_print_detail(user_id, serial_id, user_print_detail) return { "returnCode": 1, @@ -482,10 +482,10 @@ class ChuniNew(ChuniBase): # save all user items if "userItemList" in data: for item in data["userItemList"]: - self.data.item.put_item(user_id, item) + await self.data.item.put_item(user_id, item) # set the card print state to success and use the orderId as the key - self.data.item.put_user_print_state( + await self.data.item.put_user_print_state( user_id, id=upsert["orderId"], hasCompleted=True ) @@ -497,7 +497,7 @@ class ChuniNew(ChuniBase): # set the card print state to success and use the orderId as the key for order_id in order_ids: - self.data.item.put_user_print_state(user_id, id=order_id, hasCompleted=True) + await self.data.item.put_user_print_state(user_id, id=order_id, hasCompleted=True) return {"returnCode": "1", "apiName": "CMUpsertUserPrintCancelApi"} @@ -508,11 +508,11 @@ class ChuniNew(ChuniBase): async def handle_begin_matching_api_request(self, data: Dict) -> Dict: room_id = 1 # check if there is a free matching room - matching_room = self.data.item.get_oldest_free_matching(self.version) + matching_room = await self.data.item.get_oldest_free_matching(self.version) if matching_room is None: # grab the latest roomId and add 1 for the new room - newest_matching = self.data.item.get_newest_matching(self.version) + newest_matching = await self.data.item.get_newest_matching(self.version) if newest_matching is not None: room_id = newest_matching["roomId"] + 1 @@ -522,12 +522,12 @@ class ChuniNew(ChuniBase): # create the new room with room_id and the current user id (host) # user id is required for the countdown later on - self.data.item.put_matching( + await self.data.item.put_matching( self.version, room_id, [new_member], user_id=new_member["userId"] ) # get the newly created matching room - matching_room = self.data.item.get_matching(self.version, room_id) + matching_room = await self.data.item.get_matching(self.version, room_id) else: # a room already exists, so just add the new member to it matching_member_list = matching_room["matchingMemberInfoList"] @@ -537,7 +537,7 @@ class ChuniNew(ChuniBase): matching_member_list.append(new_member) # add the updated room to the database, make sure to set isFull correctly! - self.data.item.put_matching( + await self.data.item.put_matching( self.version, matching_room["roomId"], matching_member_list, @@ -555,7 +555,7 @@ class ChuniNew(ChuniBase): return {"roomId": 1, "matchingWaitState": matching_wait} async def handle_end_matching_api_request(self, data: Dict) -> Dict: - matching_room = self.data.item.get_matching(self.version, data["roomId"]) + matching_room = await self.data.item.get_matching(self.version, data["roomId"]) members = matching_room["matchingMemberInfoList"] # only set the host user to role 1 every other to 0? @@ -564,7 +564,7 @@ class ChuniNew(ChuniBase): for m in members ] - self.data.item.put_matching( + await self.data.item.put_matching( self.version, matching_room["roomId"], members, @@ -585,7 +585,7 @@ class ChuniNew(ChuniBase): async def handle_remove_matching_member_api_request(self, data: Dict) -> Dict: # get all matching rooms, because Chuni only returns the userId # not the actual roomId - matching_rooms = self.data.item.get_all_matchings(self.version) + matching_rooms = await self.data.item.get_all_matchings(self.version) if matching_rooms is None: return {"returnCode": "1"} @@ -599,10 +599,10 @@ class ChuniNew(ChuniBase): # if the last user got removed, delete the matching room if len(new_members) <= 0: - self.data.item.delete_matching(self.version, room["roomId"]) + await self.data.item.delete_matching(self.version, room["roomId"]) else: # remove the user from the room - self.data.item.put_matching( + await self.data.item.put_matching( self.version, room["roomId"], new_members, @@ -615,7 +615,7 @@ class ChuniNew(ChuniBase): async def handle_get_matching_state_api_request(self, data: Dict) -> Dict: polling_interval = 1 # get the current active room - matching_room = self.data.item.get_matching(self.version, data["roomId"]) + matching_room = await self.data.item.get_matching(self.version, data["roomId"]) members = matching_room["matchingMemberInfoList"] rest_sec = matching_room["restMSec"] @@ -638,7 +638,7 @@ class ChuniNew(ChuniBase): current_member["userName"] = self.read_wtf8(current_member["userName"]) members[i] = current_member - self.data.item.put_matching( + await self.data.item.put_matching( self.version, data["roomId"], members, diff --git a/titles/chuni/read.py b/titles/chuni/read.py index 7100ae6..5082f7a 100644 --- a/titles/chuni/read.py +++ b/titles/chuni/read.py @@ -28,7 +28,7 @@ class ChuniReader(BaseReader): self.logger.error(f"Invalid chunithm version {version}") exit(1) - def read(self) -> None: + async def read(self) -> None: data_dirs = [] if self.bin_dir is not None: data_dirs += self.get_data_directories(self.bin_dir) @@ -38,13 +38,13 @@ class ChuniReader(BaseReader): for dir in data_dirs: self.logger.info(f"Read from {dir}") - self.read_events(f"{dir}/event") - self.read_music(f"{dir}/music") - self.read_charges(f"{dir}/chargeItem") - self.read_avatar(f"{dir}/avatarAccessory") - self.read_login_bonus(f"{dir}/") + await self.read_events(f"{dir}/event") + await self.read_music(f"{dir}/music") + await self.read_charges(f"{dir}/chargeItem") + await self.read_avatar(f"{dir}/avatarAccessory") + await self.read_login_bonus(f"{dir}/") - def read_login_bonus(self, root_dir: str) -> None: + async def read_login_bonus(self, root_dir: str) -> None: for root, dirs, files in walk(f"{root_dir}loginBonusPreset"): for dir in dirs: if path.exists(f"{root}/{dir}/LoginBonusPreset.xml"): @@ -60,7 +60,7 @@ class ChuniReader(BaseReader): True if xml_root.find("disableFlag").text == "false" else False ) - result = self.data.static.put_login_bonus_preset( + result = await self.data.static.put_login_bonus_preset( self.version, id, name, is_enabled ) @@ -98,7 +98,7 @@ class ChuniReader(BaseReader): bonus_root.find("loginBonusCategoryType").text ) - result = self.data.static.put_login_bonus( + result = await self.data.static.put_login_bonus( self.version, id, bonus_id, @@ -117,7 +117,7 @@ class ChuniReader(BaseReader): f"Failed to insert login bonus {bonus_id}" ) - def read_events(self, evt_dir: str) -> None: + async def read_events(self, evt_dir: str) -> None: for root, dirs, files in walk(evt_dir): for dir in dirs: if path.exists(f"{root}/{dir}/Event.xml"): @@ -132,7 +132,7 @@ class ChuniReader(BaseReader): for substances in xml_root.findall("substances"): event_type = substances.find("type").text - result = self.data.static.put_event( + result = await self.data.static.put_event( self.version, id, event_type, name ) if result is not None: @@ -140,7 +140,7 @@ class ChuniReader(BaseReader): else: self.logger.warning(f"Failed to insert event {id}") - def read_music(self, music_dir: str) -> None: + async def read_music(self, music_dir: str) -> None: for root, dirs, files in walk(music_dir): for dir in dirs: if path.exists(f"{root}/{dir}/Music.xml"): @@ -185,7 +185,7 @@ class ChuniReader(BaseReader): ) we_chara = None - result = self.data.static.put_music( + result = await self.data.static.put_music( self.version, song_id, chart_id, @@ -206,7 +206,7 @@ class ChuniReader(BaseReader): f"Failed to insert music {song_id} chart {chart_id}" ) - def read_charges(self, charge_dir: str) -> None: + async def read_charges(self, charge_dir: str) -> None: for root, dirs, files in walk(charge_dir): for dir in dirs: if path.exists(f"{root}/{dir}/ChargeItem.xml"): @@ -222,7 +222,7 @@ class ChuniReader(BaseReader): consumeType = xml_root.find("consumeType").text sellingAppeal = bool(xml_root.find("sellingAppeal").text) - result = self.data.static.put_charge( + result = await self.data.static.put_charge( self.version, id, name, @@ -236,7 +236,7 @@ class ChuniReader(BaseReader): else: self.logger.warning(f"Failed to insert charge {id}") - def read_avatar(self, avatar_dir: str) -> None: + async def read_avatar(self, avatar_dir: str) -> None: for root, dirs, files in walk(avatar_dir): for dir in dirs: if path.exists(f"{root}/{dir}/AvatarAccessory.xml"): @@ -254,7 +254,7 @@ class ChuniReader(BaseReader): for texture in xml_root.findall("texture"): texturePath = texture.find("path").text - result = self.data.static.put_avatar( + result = await self.data.static.put_avatar( self.version, id, name, category, iconPath, texturePath ) diff --git a/titles/chuni/schema/item.py b/titles/chuni/schema/item.py index dc2751d..5077e14 100644 --- a/titles/chuni/schema/item.py +++ b/titles/chuni/schema/item.py @@ -245,7 +245,7 @@ matching = Table( class ChuniItemData(BaseData): - def get_oldest_free_matching(self, version: int) -> Optional[Row]: + async def get_oldest_free_matching(self, version: int) -> Optional[Row]: sql = matching.select( and_( matching.c.version == version, @@ -253,46 +253,46 @@ class ChuniItemData(BaseData): ) ).order_by(matching.c.roomId.asc()) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_newest_matching(self, version: int) -> Optional[Row]: + async def get_newest_matching(self, version: int) -> Optional[Row]: sql = matching.select( and_( matching.c.version == version ) ).order_by(matching.c.roomId.desc()) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_all_matchings(self, version: int) -> Optional[List[Row]]: + async def get_all_matchings(self, version: int) -> Optional[List[Row]]: sql = matching.select( and_( matching.c.version == version ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_matching(self, version: int, room_id: int) -> Optional[Row]: + async def get_matching(self, version: int, room_id: int) -> Optional[Row]: sql = matching.select( and_(matching.c.version == version, matching.c.roomId == room_id) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_matching( + async def put_matching( self, version: int, room_id: int, @@ -314,22 +314,22 @@ class ChuniItemData(BaseData): restMSec=rest_sec, matchingMemberInfoList=matching_member_info_list ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def delete_matching(self, version: int, room_id: int): + async def delete_matching(self, version: int, room_id: int): sql = delete(matching).where( and_(matching.c.roomId == room_id, matching.c.version == version) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.lastrowid - def get_all_favorites( + async def get_all_favorites( self, user_id: int, version: int, fav_kind: int = 1 ) -> Optional[List[Row]]: sql = favorite.select( @@ -340,12 +340,12 @@ class ChuniItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_login_bonus( + async def put_login_bonus( self, user_id: int, version: int, preset_id: int, **login_bonus_data ) -> Optional[int]: sql = insert(login_bonus).values( @@ -354,12 +354,12 @@ class ChuniItemData(BaseData): conflict = sql.on_duplicate_key_update(presetId=preset_id, **login_bonus_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_all_login_bonus( + async def get_all_login_bonus( self, user_id: int, version: int, is_finished: bool = False ) -> Optional[List[Row]]: sql = login_bonus.select( @@ -370,12 +370,12 @@ class ChuniItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_login_bonus( + async def get_login_bonus( self, user_id: int, version: int, preset_id: int ) -> Optional[Row]: sql = login_bonus.select( @@ -386,12 +386,12 @@ class ChuniItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_character(self, user_id: int, character_data: Dict) -> Optional[int]: + async def put_character(self, user_id: int, character_data: Dict) -> Optional[int]: character_data["user"] = user_id character_data = self.fix_bools(character_data) @@ -399,30 +399,30 @@ class ChuniItemData(BaseData): sql = insert(character).values(**character_data) conflict = sql.on_duplicate_key_update(**character_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_character(self, user_id: int, character_id: int) -> Optional[Dict]: + async def get_character(self, user_id: int, character_id: int) -> Optional[Dict]: sql = select(character).where( and_(character.c.user == user_id, character.c.characterId == character_id) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_characters(self, user_id: int) -> Optional[List[Row]]: + async def get_characters(self, user_id: int) -> Optional[List[Row]]: sql = select(character).where(character.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_item(self, user_id: int, item_data: Dict) -> Optional[int]: + async def put_item(self, user_id: int, item_data: Dict) -> Optional[int]: item_data["user"] = user_id item_data = self.fix_bools(item_data) @@ -430,12 +430,12 @@ class ChuniItemData(BaseData): sql = insert(item).values(**item_data) conflict = sql.on_duplicate_key_update(**item_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_items(self, user_id: int, kind: int = None) -> Optional[List[Row]]: + async def get_items(self, user_id: int, kind: int = None) -> Optional[List[Row]]: if kind is None: sql = select(item).where(item.c.user == user_id) else: @@ -443,12 +443,12 @@ class ChuniItemData(BaseData): and_(item.c.user == user_id, item.c.itemKind == kind) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_duel(self, user_id: int, duel_data: Dict) -> Optional[int]: + async def put_duel(self, user_id: int, duel_data: Dict) -> Optional[int]: duel_data["user"] = user_id duel_data = self.fix_bools(duel_data) @@ -456,20 +456,20 @@ class ChuniItemData(BaseData): sql = insert(duel).values(**duel_data) conflict = sql.on_duplicate_key_update(**duel_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_duels(self, user_id: int) -> Optional[List[Row]]: + async def get_duels(self, user_id: int) -> Optional[List[Row]]: sql = select(duel).where(duel.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_map(self, user_id: int, map_data: Dict) -> Optional[int]: + async def put_map(self, user_id: int, map_data: Dict) -> Optional[int]: map_data["user"] = user_id map_data = self.fix_bools(map_data) @@ -477,20 +477,20 @@ class ChuniItemData(BaseData): sql = insert(map).values(**map_data) conflict = sql.on_duplicate_key_update(**map_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_maps(self, user_id: int) -> Optional[List[Row]]: + async def get_maps(self, user_id: int) -> Optional[List[Row]]: sql = select(map).where(map.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_map_area(self, user_id: int, map_area_data: Dict) -> Optional[int]: + async def put_map_area(self, user_id: int, map_area_data: Dict) -> Optional[int]: map_area_data["user"] = user_id map_area_data = self.fix_bools(map_area_data) @@ -498,28 +498,28 @@ class ChuniItemData(BaseData): sql = insert(map_area).values(**map_area_data) conflict = sql.on_duplicate_key_update(**map_area_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_map_areas(self, user_id: int) -> Optional[List[Row]]: + async def get_map_areas(self, user_id: int) -> Optional[List[Row]]: sql = select(map_area).where(map_area.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_user_gachas(self, aime_id: int) -> Optional[List[Row]]: + async def get_user_gachas(self, aime_id: int) -> Optional[List[Row]]: sql = gacha.select(gacha.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_user_gacha( + async def put_user_gacha( self, aime_id: int, gacha_id: int, gacha_data: Dict ) -> Optional[int]: sql = insert(gacha).values(user=aime_id, gachaId=gacha_id, **gacha_data) @@ -527,14 +527,14 @@ class ChuniItemData(BaseData): conflict = sql.on_duplicate_key_update( user=aime_id, gachaId=gacha_id, **gacha_data ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_user_gacha: Failed to insert! aime_id: {aime_id}") return None return result.lastrowid - def get_user_print_states( + async def get_user_print_states( self, aime_id: int, has_completed: bool = False ) -> Optional[List[Row]]: sql = print_state.select( @@ -544,12 +544,12 @@ class ChuniItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_user_print_states_by_gacha( + async def get_user_print_states_by_gacha( self, aime_id: int, gacha_id: int, has_completed: bool = False ) -> Optional[List[Row]]: sql = print_state.select( @@ -560,16 +560,16 @@ class ChuniItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_user_print_state(self, aime_id: int, **print_data) -> Optional[int]: + async def put_user_print_state(self, aime_id: int, **print_data) -> Optional[int]: sql = insert(print_state).values(user=aime_id, **print_data) conflict = sql.on_duplicate_key_update(user=aime_id, **print_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( @@ -578,7 +578,7 @@ class ChuniItemData(BaseData): return None return result.lastrowid - def put_user_print_detail( + async def put_user_print_detail( self, aime_id: int, serial_id: str, user_print_data: Dict ) -> Optional[int]: sql = insert(print_detail).values( @@ -586,7 +586,7 @@ class ChuniItemData(BaseData): ) conflict = sql.on_duplicate_key_update(user=aime_id, **user_print_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( diff --git a/titles/chuni/schema/profile.py b/titles/chuni/schema/profile.py index 43b5641..b5ac493 100644 --- a/titles/chuni/schema/profile.py +++ b/titles/chuni/schema/profile.py @@ -395,7 +395,7 @@ team = Table( class ChuniProfileData(BaseData): - def put_profile_data( + async def put_profile_data( self, aime_id: int, version: int, profile_data: Dict ) -> Optional[int]: profile_data["user"] = aime_id @@ -407,26 +407,26 @@ class ChuniProfileData(BaseData): sql = insert(profile).values(**profile_data) conflict = sql.on_duplicate_key_update(**profile_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_profile_data: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def get_profile_preview(self, aime_id: int, version: int) -> Optional[Row]: + async def get_profile_preview(self, aime_id: int, version: int) -> Optional[Row]: sql = ( select([profile, option]) .join(option, profile.c.user == option.c.user) .filter(and_(profile.c.user == aime_id, profile.c.version <= version)) ).order_by(profile.c.version.desc()) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_profile_data(self, aime_id: int, version: int) -> Optional[Row]: + async def get_profile_data(self, aime_id: int, version: int) -> Optional[Row]: sql = select(profile).where( and_( profile.c.user == aime_id, @@ -434,12 +434,12 @@ class ChuniProfileData(BaseData): ) ).order_by(profile.c.version.desc()) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_profile_data_ex( + async def put_profile_data_ex( self, aime_id: int, version: int, profile_ex_data: Dict ) -> Optional[int]: profile_ex_data["user"] = aime_id @@ -449,7 +449,7 @@ class ChuniProfileData(BaseData): sql = insert(profile_ex).values(**profile_ex_data) conflict = sql.on_duplicate_key_update(**profile_ex_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( @@ -458,7 +458,7 @@ class ChuniProfileData(BaseData): return None return result.lastrowid - def get_profile_data_ex(self, aime_id: int, version: int) -> Optional[Row]: + async def get_profile_data_ex(self, aime_id: int, version: int) -> Optional[Row]: sql = select(profile_ex).where( and_( profile_ex.c.user == aime_id, @@ -466,17 +466,17 @@ class ChuniProfileData(BaseData): ) ).order_by(profile_ex.c.version.desc()) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_profile_option(self, aime_id: int, option_data: Dict) -> Optional[int]: + async def put_profile_option(self, aime_id: int, option_data: Dict) -> Optional[int]: option_data["user"] = aime_id sql = insert(option).values(**option_data) conflict = sql.on_duplicate_key_update(**option_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( @@ -485,22 +485,22 @@ class ChuniProfileData(BaseData): return None return result.lastrowid - def get_profile_option(self, aime_id: int) -> Optional[Row]: + async def get_profile_option(self, aime_id: int) -> Optional[Row]: sql = select(option).where(option.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_profile_option_ex( + async def put_profile_option_ex( self, aime_id: int, option_ex_data: Dict ) -> Optional[int]: option_ex_data["user"] = aime_id sql = insert(option_ex).values(**option_ex_data) conflict = sql.on_duplicate_key_update(**option_ex_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( @@ -509,15 +509,15 @@ class ChuniProfileData(BaseData): return None return result.lastrowid - def get_profile_option_ex(self, aime_id: int) -> Optional[Row]: + async def get_profile_option_ex(self, aime_id: int) -> Optional[Row]: sql = select(option_ex).where(option_ex.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_profile_recent_rating( + async def put_profile_recent_rating( self, aime_id: int, recent_rating_data: List[Dict] ) -> Optional[int]: sql = insert(recent_rating).values( @@ -525,7 +525,7 @@ class ChuniProfileData(BaseData): ) conflict = sql.on_duplicate_key_update(recentRating=recent_rating_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_profile_recent_rating: Failed to update! aime_id: {aime_id}" @@ -533,15 +533,15 @@ class ChuniProfileData(BaseData): return None return result.lastrowid - def get_profile_recent_rating(self, aime_id: int) -> Optional[Row]: + async def get_profile_recent_rating(self, aime_id: int) -> Optional[Row]: sql = select(recent_rating).where(recent_rating.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_profile_activity(self, aime_id: int, activity_data: Dict) -> Optional[int]: + async def put_profile_activity(self, aime_id: int, activity_data: Dict) -> Optional[int]: # The game just uses "id" but we need to distinguish that from the db column "id" activity_data["user"] = aime_id activity_data["activityId"] = activity_data["id"] @@ -549,7 +549,7 @@ class ChuniProfileData(BaseData): sql = insert(activity).values(**activity_data) conflict = sql.on_duplicate_key_update(**activity_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( @@ -558,24 +558,24 @@ class ChuniProfileData(BaseData): return None return result.lastrowid - def get_profile_activity(self, aime_id: int, kind: int) -> Optional[List[Row]]: + async def get_profile_activity(self, aime_id: int, kind: int) -> Optional[List[Row]]: sql = ( select(activity) .where(and_(activity.c.user == aime_id, activity.c.kind == kind)) .order_by(activity.c.sortNumber.desc()) # to get the last played track ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_profile_charge(self, aime_id: int, charge_data: Dict) -> Optional[int]: + async def put_profile_charge(self, aime_id: int, charge_data: Dict) -> Optional[int]: charge_data["user"] = aime_id sql = insert(charge).values(**charge_data) conflict = sql.on_duplicate_key_update(**charge_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( @@ -584,40 +584,40 @@ class ChuniProfileData(BaseData): return None return result.lastrowid - def get_profile_charge(self, aime_id: int) -> Optional[List[Row]]: + async def get_profile_charge(self, aime_id: int) -> Optional[List[Row]]: sql = select(charge).where(charge.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def add_profile_region(self, aime_id: int, region_id: int) -> Optional[int]: + async def add_profile_region(self, aime_id: int, region_id: int) -> Optional[int]: pass - def get_profile_regions(self, aime_id: int) -> Optional[List[Row]]: + async def get_profile_regions(self, aime_id: int) -> Optional[List[Row]]: pass - def put_profile_emoney(self, aime_id: int, emoney_data: Dict) -> Optional[int]: + async def put_profile_emoney(self, aime_id: int, emoney_data: Dict) -> Optional[int]: emoney_data["user"] = aime_id sql = insert(emoney).values(**emoney_data) conflict = sql.on_duplicate_key_update(**emoney_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_profile_emoney(self, aime_id: int) -> Optional[List[Row]]: + async def get_profile_emoney(self, aime_id: int) -> Optional[List[Row]]: sql = select(emoney).where(emoney.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_profile_overpower( + async def put_profile_overpower( self, aime_id: int, overpower_data: Dict ) -> Optional[int]: overpower_data["user"] = aime_id @@ -625,31 +625,31 @@ class ChuniProfileData(BaseData): sql = insert(overpower).values(**overpower_data) conflict = sql.on_duplicate_key_update(**overpower_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_profile_overpower(self, aime_id: int) -> Optional[List[Row]]: + async def get_profile_overpower(self, aime_id: int) -> Optional[List[Row]]: sql = select(overpower).where(overpower.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_team_by_id(self, team_id: int) -> Optional[Row]: + async def get_team_by_id(self, team_id: int) -> Optional[Row]: sql = select(team).where(team.c.id == team_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_team_rank(self, team_id: int) -> int: + async def get_team_rank(self, team_id: int) -> int: # Normal ranking system, likely the one used in the real servers # Query all teams sorted by 'teamPoint' - result = self.execute( + result = await self.execute( select(team.c.id).order_by(team.c.teamPoint.desc()) ) @@ -666,13 +666,13 @@ class ChuniProfileData(BaseData): # RIP scaled team ranking. Gone, but forgotten # def get_team_rank_scaled(self, team_id: int) -> int: - def update_team(self, team_id: int, team_data: Dict) -> bool: + async def update_team(self, team_id: int, team_data: Dict) -> bool: team_data["id"] = team_id sql = insert(team).values(**team_data) conflict = sql.on_duplicate_key_update(**team_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn( @@ -680,16 +680,16 @@ class ChuniProfileData(BaseData): ) return False return True - def get_rival(self, rival_id: int) -> Optional[Row]: + async def get_rival(self, rival_id: int) -> Optional[Row]: sql = select(profile).where(profile.c.user == rival_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_overview(self) -> Dict: + async def get_overview(self) -> Dict: # Fetch and add up all the playcounts - playcount_sql = self.execute(select(profile.c.playCount)) + playcount_sql = await self.execute(select(profile.c.playCount)) if playcount_sql is None: self.logger.warn( diff --git a/titles/chuni/schema/score.py b/titles/chuni/schema/score.py index 7e41b8f..0a6424c 100644 --- a/titles/chuni/schema/score.py +++ b/titles/chuni/schema/score.py @@ -142,55 +142,55 @@ playlog = Table( class ChuniScoreData(BaseData): - def get_courses(self, aime_id: int) -> Optional[Row]: + async def get_courses(self, aime_id: int) -> Optional[Row]: sql = select(course).where(course.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_course(self, aime_id: int, course_data: Dict) -> Optional[int]: + async def put_course(self, aime_id: int, course_data: Dict) -> Optional[int]: course_data["user"] = aime_id course_data = self.fix_bools(course_data) sql = insert(course).values(**course_data) conflict = sql.on_duplicate_key_update(**course_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_scores(self, aime_id: int) -> Optional[Row]: + async def get_scores(self, aime_id: int) -> Optional[Row]: sql = select(best_score).where(best_score.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_score(self, aime_id: int, score_data: Dict) -> Optional[int]: + async def put_score(self, aime_id: int, score_data: Dict) -> Optional[int]: score_data["user"] = aime_id score_data = self.fix_bools(score_data) sql = insert(best_score).values(**score_data) conflict = sql.on_duplicate_key_update(**score_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_playlogs(self, aime_id: int) -> Optional[Row]: + async def get_playlogs(self, aime_id: int) -> Optional[Row]: sql = select(playlog).where(playlog.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_playlog(self, aime_id: int, playlog_data: Dict, version: int) -> Optional[int]: + async def put_playlog(self, aime_id: int, playlog_data: Dict, version: int) -> Optional[int]: # Calculate the ROM version that should be inserted into the DB, based on the version of the ggame being inserted # We only need from Version 10 (Plost) and back, as newer versions include romVersion in their upsert # This matters both for gameRankings, as well as a future DB update to keep version data separate @@ -216,12 +216,12 @@ class ChuniScoreData(BaseData): sql = insert(playlog).values(**playlog_data) conflict = sql.on_duplicate_key_update(**playlog_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_rankings(self, version: int) -> Optional[List[Dict]]: + async def get_rankings(self, version: int) -> Optional[List[Dict]]: # Calculates the ROM version that should be fetched for rankings, based on the game version being retrieved # This prevents tracks that are not accessible in your version from counting towards the 10 results romVer = { @@ -241,7 +241,7 @@ class ChuniScoreData(BaseData): 0: "1.00%" } sql = select([playlog.c.musicId.label('id'), func.count(playlog.c.musicId).label('point')]).where((playlog.c.level != 4) & (playlog.c.romVersion.like(romVer.get(version, "%")))).group_by(playlog.c.musicId).order_by(func.count(playlog.c.musicId).desc()).limit(10) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None @@ -249,10 +249,10 @@ class ChuniScoreData(BaseData): rows = result.fetchall() return [dict(row) for row in rows] - def get_rival_music(self, rival_id: int) -> Optional[List[Dict]]: + async def get_rival_music(self, rival_id: int) -> Optional[List[Dict]]: sql = select(best_score).where(best_score.c.user == rival_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() diff --git a/titles/chuni/schema/static.py b/titles/chuni/schema/static.py index fe32d41..ed67b5d 100644 --- a/titles/chuni/schema/static.py +++ b/titles/chuni/schema/static.py @@ -175,7 +175,7 @@ login_bonus = Table( class ChuniStaticData(BaseData): - def put_login_bonus( + async def put_login_bonus( self, version: int, preset_id: int, @@ -207,12 +207,12 @@ class ChuniStaticData(BaseData): loginBonusCategoryType=login_bonus_category_type, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_login_bonus( + async def get_login_bonus( self, version: int, preset_id: int, @@ -224,12 +224,12 @@ class ChuniStaticData(BaseData): ) ).order_by(login_bonus.c.needLoginDayCount.desc()) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_login_bonus_by_required_days( + async def get_login_bonus_by_required_days( self, version: int, preset_id: int, need_login_day_count: int ) -> Optional[Row]: sql = login_bonus.select( @@ -240,12 +240,12 @@ class ChuniStaticData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_login_bonus_preset( + async def put_login_bonus_preset( self, version: int, preset_id: int, preset_name: str, is_enabled: bool ) -> Optional[int]: sql = insert(login_bonus_preset).values( @@ -259,12 +259,12 @@ class ChuniStaticData(BaseData): presetName=preset_name, isEnabled=is_enabled ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_login_bonus_presets( + async def get_login_bonus_presets( self, version: int, is_enabled: bool = True ) -> Optional[List[Row]]: sql = login_bonus_preset.select( @@ -274,12 +274,12 @@ class ChuniStaticData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_event( + async def put_event( self, version: int, event_id: int, type: int, name: str ) -> Optional[int]: sql = insert(events).values( @@ -288,19 +288,19 @@ class ChuniStaticData(BaseData): conflict = sql.on_duplicate_key_update(name=name) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def update_event( + async def update_event( self, version: int, event_id: int, enabled: bool ) -> Optional[bool]: sql = events.update( and_(events.c.version == version, events.c.eventId == event_id) ).values(enabled=enabled) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.warning( f"update_event: failed to update event! version: {version}, event_id: {event_id}, enabled: {enabled}" @@ -315,35 +315,35 @@ class ChuniStaticData(BaseData): return None return event["enabled"] - def get_event(self, version: int, event_id: int) -> Optional[Row]: + async def get_event(self, version: int, event_id: int) -> Optional[Row]: sql = select(events).where( and_(events.c.version == version, events.c.eventId == event_id) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_enabled_events(self, version: int) -> Optional[List[Row]]: + async def get_enabled_events(self, version: int) -> Optional[List[Row]]: sql = select(events).where( and_(events.c.version == version, events.c.enabled == True) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_events(self, version: int) -> Optional[List[Row]]: + async def get_events(self, version: int) -> Optional[List[Row]]: sql = select(events).where(events.c.version == version) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_music( + async def put_music( self, version: int, song_id: int, @@ -376,12 +376,12 @@ class ChuniStaticData(BaseData): worldsEndTag=we_tag, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def put_charge( + async def put_charge( self, version: int, charge_id: int, @@ -406,38 +406,38 @@ class ChuniStaticData(BaseData): sellingAppeal=selling_appeal, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_enabled_charges(self, version: int) -> Optional[List[Row]]: + async def get_enabled_charges(self, version: int) -> Optional[List[Row]]: sql = select(charge).where( and_(charge.c.version == version, charge.c.enabled == True) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_charges(self, version: int) -> Optional[List[Row]]: + async def get_charges(self, version: int) -> Optional[List[Row]]: sql = select(charge).where(charge.c.version == version) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_music(self, version: int) -> Optional[List[Row]]: + async def get_music(self, version: int) -> Optional[List[Row]]: sql = music.select(music.c.version <= version) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_music_chart( + async def get_music_chart( self, version: int, song_id: int, chart_id: int ) -> Optional[List[Row]]: sql = select(music).where( @@ -448,21 +448,21 @@ class ChuniStaticData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_song(self, music_id: int) -> Optional[Row]: + async def get_song(self, music_id: int) -> Optional[Row]: sql = music.select(music.c.id == music_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_avatar( + async def put_avatar( self, version: int, avatarAccessoryId: int, @@ -487,12 +487,12 @@ class ChuniStaticData(BaseData): texturePath=texturePath, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def put_gacha( + async def put_gacha( self, version: int, gacha_id: int, @@ -513,33 +513,33 @@ class ChuniStaticData(BaseData): **gacha_data, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"Failed to insert gacha! gacha_id {gacha_id}") return None return result.lastrowid - def get_gachas(self, version: int) -> Optional[List[Dict]]: + async def get_gachas(self, version: int) -> Optional[List[Dict]]: sql = gachas.select(gachas.c.version <= version).order_by( gachas.c.gachaId.asc() ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_gacha(self, version: int, gacha_id: int) -> Optional[Dict]: + async def get_gacha(self, version: int, gacha_id: int) -> Optional[Dict]: sql = gachas.select( and_(gachas.c.version <= version, gachas.c.gachaId == gacha_id) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_gacha_card( + async def put_gacha_card( self, gacha_id: int, card_id: int, **gacha_card ) -> Optional[int]: sql = insert(gacha_cards).values(gachaId=gacha_id, cardId=card_id, **gacha_card) @@ -548,21 +548,21 @@ class ChuniStaticData(BaseData): gachaId=gacha_id, cardId=card_id, **gacha_card ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"Failed to insert gacha card! gacha_id {gacha_id}") return None return result.lastrowid - def get_gacha_cards(self, gacha_id: int) -> Optional[List[Dict]]: + async def get_gacha_cards(self, gacha_id: int) -> Optional[List[Dict]]: sql = gacha_cards.select(gacha_cards.c.gachaId == gacha_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_gacha_card_by_character( + async def get_gacha_card_by_character( self, gacha_id: int, chara_id: int ) -> Optional[Dict]: sql_sub = ( @@ -574,26 +574,26 @@ class ChuniStaticData(BaseData): and_(gacha_cards.c.gachaId == gacha_id, gacha_cards.c.cardId == sql_sub) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_card(self, version: int, card_id: int, **card_data) -> Optional[int]: + async def put_card(self, version: int, card_id: int, **card_data) -> Optional[int]: sql = insert(cards).values(version=version, cardId=card_id, **card_data) conflict = sql.on_duplicate_key_update(**card_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"Failed to insert card! card_id {card_id}") return None return result.lastrowid - def get_card(self, version: int, card_id: int) -> Optional[Dict]: + async def get_card(self, version: int, card_id: int) -> Optional[Dict]: sql = cards.select(and_(cards.c.version <= version, cards.c.cardId == card_id)) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() \ No newline at end of file diff --git a/titles/cm/read.py b/titles/cm/read.py index 376789d..ae90b52 100644 --- a/titles/cm/read.py +++ b/titles/cm/read.py @@ -50,7 +50,7 @@ class CardMakerReader(BaseReader): ): return f"{root}/{dir}" - def read(self) -> None: + async def read(self) -> None: static_datas = { "static_gachas.csv": "read_ongeki_gacha_csv", "static_gacha_cards.csv": "read_ongeki_gacha_card_csv", @@ -66,7 +66,7 @@ class CardMakerReader(BaseReader): for file, func in static_datas.items(): if os.path.exists(f"{self.bin_dir}/MU3/{file}"): read_csv = getattr(CardMakerReader, func) - read_csv(self, f"{self.bin_dir}/MU3/{file}") + await read_csv(self, f"{self.bin_dir}/MU3/{file}") else: self.logger.warning( f"Couldn't find {file} file in {self.bin_dir}, skipping" @@ -78,12 +78,12 @@ class CardMakerReader(BaseReader): # ONGEKI (MU3) cnnot easily access the bin data(A000.pac) # so only opt_dir will work for now for dir in data_dirs: - self.read_chuni_card(f"{dir}/CHU/card") - self.read_chuni_gacha(f"{dir}/CHU/gacha") - self.read_mai2_card(f"{dir}/MAI/card") - self.read_ongeki_gacha(f"{dir}/MU3/gacha") + await self.read_chuni_card(f"{dir}/CHU/card") + await self.read_chuni_gacha(f"{dir}/CHU/gacha") + await self.read_mai2_card(f"{dir}/MAI/card") + await self.read_ongeki_gacha(f"{dir}/MU3/gacha") - def read_chuni_card(self, base_dir: str) -> None: + async def read_chuni_card(self, base_dir: str) -> None: self.logger.info(f"Reading cards from {base_dir}...") version_ids = { @@ -114,7 +114,7 @@ class CardMakerReader(BaseReader): chain = int(troot.find("chain").text) skill_name = troot.find("skillName").text - self.chuni_data.static.put_card( + await self.chuni_data.static.put_card( version, card_id, charaName=chara_name, @@ -131,7 +131,7 @@ class CardMakerReader(BaseReader): self.logger.info(f"Added chuni card {card_id}") - def read_chuni_gacha(self, base_dir: str) -> None: + async def read_chuni_gacha(self, base_dir: str) -> None: self.logger.info(f"Reading gachas from {base_dir}...") version_ids = { @@ -158,7 +158,7 @@ class CardMakerReader(BaseReader): True if troot.find("ceilingType").text == "1" else False ) - self.chuni_data.static.put_gacha( + await self.chuni_data.static.put_gacha( version, gacha_id, name, @@ -181,7 +181,7 @@ class CardMakerReader(BaseReader): True if gacha_card.find("pickup").text == "1" else False ) - self.chuni_data.static.put_gacha_card( + await self.chuni_data.static.put_gacha_card( gacha_id, card_id, weight=weight, @@ -193,7 +193,7 @@ class CardMakerReader(BaseReader): f"Added chuni card {card_id} to gacha {gacha_id}" ) - def read_mai2_card(self, base_dir: str) -> None: + async def read_mai2_card(self, base_dir: str) -> None: self.logger.info(f"Reading cards from {base_dir}...") version_ids = { @@ -231,18 +231,18 @@ class CardMakerReader(BaseReader): False if re.search(r"\d{2}/\d{2}/\d{2}", name) else enabled ) - self.mai2_data.static.put_card( + await self.mai2_data.static.put_card( version, card_id, name, enabled=enabled ) self.logger.info(f"Added mai2 card {card_id}") - def read_ongeki_gacha_csv(self, file_path: str) -> None: + async def read_ongeki_gacha_csv(self, file_path: str) -> None: self.logger.info(f"Reading gachas from {file_path}...") with open(file_path, encoding="utf-8") as f: reader = csv.DictReader(f) for row in reader: - self.ongeki_data.static.put_gacha( + await self.ongeki_data.static.put_gacha( row["version"], row["gachaId"], row["gachaName"], @@ -254,13 +254,13 @@ class CardMakerReader(BaseReader): self.logger.info(f"Added ongeki gacha {row['gachaId']}") - def read_ongeki_gacha_card_csv(self, file_path: str) -> None: + async def read_ongeki_gacha_card_csv(self, file_path: str) -> None: self.logger.info(f"Reading gacha cards from {file_path}...") with open(file_path, encoding="utf-8") as f: reader = csv.DictReader(f) for row in reader: - self.ongeki_data.static.put_gacha_card( + await self.ongeki_data.static.put_gacha_card( row["gachaId"], row["cardId"], rarity=row["rarity"], @@ -271,7 +271,7 @@ class CardMakerReader(BaseReader): self.logger.info(f"Added ongeki card {row['cardId']} to gacha") - def read_ongeki_gacha(self, base_dir: str) -> None: + async def read_ongeki_gacha(self, base_dir: str) -> None: self.logger.info(f"Reading gachas from {base_dir}...") # assuming some GachaKinds based on the GachaType @@ -294,7 +294,7 @@ class CardMakerReader(BaseReader): # skip already existing gachas if ( - self.ongeki_data.static.get_gacha( + await self.ongeki_data.static.get_gacha( OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY, gacha_id ) is not None @@ -320,7 +320,7 @@ class CardMakerReader(BaseReader): is_ceiling = 1 max_select_point = 33 - self.ongeki_data.static.put_gacha( + await self.ongeki_data.static.put_gacha( version, gacha_id, name, diff --git a/titles/cxb/base.py b/titles/cxb/base.py index 691897d..cc4e50d 100644 --- a/titles/cxb/base.py +++ b/titles/cxb/base.py @@ -35,7 +35,7 @@ class CxbBase: return {"data": []} async def handle_auth_usercheck_request(self, data: Dict) -> Dict: - profile = self.data.profile.get_profile_index( + profile = await self.data.profile.get_profile_index( 0, data["usercheck"]["authid"], self.version ) if profile is not None: @@ -50,7 +50,7 @@ class CxbBase: return {"token": data["entry"]["authid"], "uid": data["entry"]["authid"]} async def handle_auth_login_request(self, data: Dict) -> Dict: - profile = self.data.profile.get_profile_index( + profile = await self.data.profile.get_profile_index( 0, data["login"]["authid"], self.version ) @@ -204,8 +204,8 @@ class CxbBase: uid = data["loadrange"]["uid"] self.logger.info(f"Load data for {uid}") - profile = self.data.profile.get_profile(uid, self.version) - songs = self.data.score.get_best_scores(uid) + profile = await self.data.profile.get_profile(uid, self.version) + songs = await self.data.score.get_best_scores(uid) data1 = [] index = [] @@ -271,7 +271,7 @@ class CxbBase: thread_ScoreData = Thread(target=CxbBase.task_generateScoreData(song, index, data1)) thread_ScoreData.start() - v_profile = self.data.profile.get_profile_index(0, uid, self.version) + v_profile = await self.data.profile.get_profile_index(0, uid, self.version) v_profile_data = v_profile["data"] for _, data in enumerate(profile): @@ -300,11 +300,11 @@ class CxbBase: for value in data["saveindex"]["data"]: if "playedUserId" in value[1]: - self.data.profile.put_profile( + await self.data.profile.put_profile( data["saveindex"]["uid"], self.version, value[0], value[1] ) if "mcode" not in value[1]: - self.data.profile.put_profile( + await self.data.profile.put_profile( data["saveindex"]["uid"], self.version, value[0], value[1] ) if "shopId" in value: @@ -335,7 +335,7 @@ class CxbBase: "index": value[0], } ) - self.data.score.put_best_score( + await self.data.score.put_best_score( data["saveindex"]["uid"], song_json["mcode"], self.version, @@ -360,32 +360,32 @@ class CxbBase: for index, value in enumerate(data["saveindex"]["data"]): if int(data["saveindex"]["index"][index]) == 101: - self.data.profile.put_profile( + await self.data.profile.put_profile( aimeId, self.version, data["saveindex"]["index"][index], value ) if ( int(data["saveindex"]["index"][index]) >= 700000 and int(data["saveindex"]["index"][index]) <= 701000 ): - self.data.profile.put_profile( + await self.data.profile.put_profile( aimeId, self.version, data["saveindex"]["index"][index], value ) if ( int(data["saveindex"]["index"][index]) >= 500 and int(data["saveindex"]["index"][index]) <= 510 ): - self.data.profile.put_profile( + await self.data.profile.put_profile( aimeId, self.version, data["saveindex"]["index"][index], value ) if "playedUserId" in value: - self.data.profile.put_profile( + await self.data.profile.put_profile( aimeId, self.version, data["saveindex"]["index"][index], json.loads(value), ) if "mcode" not in value and "normalCR" not in value: - self.data.profile.put_profile( + await self.data.profile.put_profile( aimeId, self.version, data["saveindex"]["index"][index], @@ -437,7 +437,7 @@ class CxbBase: } ) - self.data.score.put_best_score( + await self.data.score.put_best_score( aimeId, data1["mcode"], self.version, indexSongList[i], songCode[0] ) i += 1 @@ -446,7 +446,7 @@ class CxbBase: async def handle_action_sprankreq_request(self, data: Dict) -> Dict: uid = data["sprankreq"]["uid"] self.logger.info(f"Get best rankings for {uid}") - p = self.data.score.get_best_rankings(uid) + p = await self.data.score.get_best_rankings(uid) rankList: List[Dict[str, Any]] = [] @@ -492,7 +492,7 @@ class CxbBase: # REV S2 if "clear" in rid: try: - self.data.score.put_ranking( + await self.data.score.put_ranking( user_id=uid, rev_id=int(rid["rid"]), song_id=int(rid["sc"][1]), @@ -500,7 +500,7 @@ class CxbBase: clear=rid["clear"], ) except Exception: - self.data.score.put_ranking( + await self.data.score.put_ranking( user_id=uid, rev_id=int(rid["rid"]), song_id=0, @@ -510,7 +510,7 @@ class CxbBase: # REV else: try: - self.data.score.put_ranking( + await self.data.score.put_ranking( user_id=uid, rev_id=int(rid["rid"]), song_id=int(rid["sc"][1]), @@ -518,7 +518,7 @@ class CxbBase: clear=0, ) except Exception: - self.data.score.put_ranking( + await self.data.score.put_ranking( user_id=uid, rev_id=int(rid["rid"]), song_id=0, @@ -530,12 +530,12 @@ class CxbBase: async def handle_action_addenergy_request(self, data: Dict) -> Dict: uid = data["addenergy"]["uid"] self.logger.info(f"Add energy to user {uid}") - profile = self.data.profile.get_profile_index(0, uid, self.version) + profile = await self.data.profile.get_profile_index(0, uid, self.version) data1 = profile["data"] - p = self.data.item.get_energy(uid) + p = await self.data.item.get_energy(uid) if not p: - self.data.item.put_energy(uid, 5) + await self.data.item.put_energy(uid, 5) return { "class": data1["myClass"], @@ -548,7 +548,7 @@ class CxbBase: energy = p["energy"] newenergy = int(energy) + 5 - self.data.item.put_energy(uid, newenergy) + await self.data.item.put_energy(uid, newenergy) if int(energy) <= 995: array.append( diff --git a/titles/cxb/read.py b/titles/cxb/read.py index b71740d..9a2ae98 100644 --- a/titles/cxb/read.py +++ b/titles/cxb/read.py @@ -1,6 +1,5 @@ -from typing import Optional, Dict, List -from os import walk, path -import urllib +from typing import Optional +from os import path import csv from read import BaseReader @@ -8,7 +7,6 @@ from core.config import CoreConfig from titles.cxb.database import CxbData from titles.cxb.const import CxbConstants - class CxbReader(BaseReader): def __init__( self, @@ -29,17 +27,14 @@ class CxbReader(BaseReader): self.logger.error(f"Invalid project cxb version {version}") exit(1) - def read(self) -> None: - pull_bin_ram = True + async def read(self) -> None: + if path.exists(self.bin_dir): + await self.read_csv(self.bin_dir) + + else: + self.logger.warn(f"{self.bin_dir} does not exist, nothing to import") - if not path.exists(f"{self.bin_dir}"): - self.logger.warning(f"Couldn't find csv file in {self.bin_dir}, skipping") - pull_bin_ram = False - - if pull_bin_ram: - self.read_csv(f"{self.bin_dir}") - - def read_csv(self, bin_dir: str) -> None: + async def read_csv(self, bin_dir: str) -> None: self.logger.info(f"Read csv from {bin_dir}") try: @@ -55,7 +50,7 @@ class CxbReader(BaseReader): if not "N/A" in row["standard"]: self.logger.info(f"Added song {song_id} chart 0") - self.data.static.put_music( + await self.data.static.put_music( self.version, song_id, index, @@ -71,7 +66,7 @@ class CxbReader(BaseReader): ) if not "N/A" in row["hard"]: self.logger.info(f"Added song {song_id} chart 1") - self.data.static.put_music( + await self.data.static.put_music( self.version, song_id, index, @@ -83,7 +78,7 @@ class CxbReader(BaseReader): ) if not "N/A" in row["master"]: self.logger.info(f"Added song {song_id} chart 2") - self.data.static.put_music( + await self.data.static.put_music( self.version, song_id, index, @@ -97,7 +92,7 @@ class CxbReader(BaseReader): ) if not "N/A" in row["unlimited"]: self.logger.info(f"Added song {song_id} chart 3") - self.data.static.put_music( + await self.data.static.put_music( self.version, song_id, index, @@ -113,7 +108,7 @@ class CxbReader(BaseReader): ) if not "N/A" in row["easy"]: self.logger.info(f"Added song {song_id} chart 4") - self.data.static.put_music( + await self.data.static.put_music( self.version, song_id, index, diff --git a/titles/cxb/rev.py b/titles/cxb/rev.py index 1a7c5ac..757b7e8 100644 --- a/titles/cxb/rev.py +++ b/titles/cxb/rev.py @@ -25,7 +25,7 @@ class CxbRev(CxbBase): score_data = json.loads(data["putlog"]["data"]) userid = score_data["usid"] - self.data.score.put_playlog( + await self.data.score.put_playlog( userid, score_data["mcode"], score_data["difficulty"], diff --git a/titles/cxb/schema/item.py b/titles/cxb/schema/item.py index 022a036..9e6a904 100644 --- a/titles/cxb/schema/item.py +++ b/titles/cxb/schema/item.py @@ -19,12 +19,12 @@ energy = Table( class CxbItemData(BaseData): - def put_energy(self, user_id: int, rev_energy: int) -> Optional[int]: + async def put_energy(self, user_id: int, rev_energy: int) -> Optional[int]: sql = insert(energy).values(user=user_id, energy=rev_energy) conflict = sql.on_duplicate_key_update(energy=rev_energy) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"{__name__} failed to insert item! user: {user_id}, energy: {rev_energy}" @@ -33,10 +33,10 @@ class CxbItemData(BaseData): return result.lastrowid - def get_energy(self, user_id: int) -> Optional[Dict]: + async def get_energy(self, user_id: int) -> Optional[Dict]: sql = energy.select(and_(energy.c.user == user_id)) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() diff --git a/titles/cxb/schema/profile.py b/titles/cxb/schema/profile.py index 5c62f76..a3e6039 100644 --- a/titles/cxb/schema/profile.py +++ b/titles/cxb/schema/profile.py @@ -21,7 +21,7 @@ profile = Table( class CxbProfileData(BaseData): - def put_profile( + async def put_profile( self, user_id: int, version: int, index: int, data: JSON ) -> Optional[int]: sql = insert(profile).values( @@ -30,7 +30,7 @@ class CxbProfileData(BaseData): conflict = sql.on_duplicate_key_update(index=index, data=data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"{__name__} failed to update! user: {user_id}, index: {index}, data: {data}" @@ -39,7 +39,7 @@ class CxbProfileData(BaseData): return result.lastrowid - def get_profile(self, aime_id: int, version: int) -> Optional[List[Dict]]: + async def get_profile(self, aime_id: int, version: int) -> Optional[List[Dict]]: """ Given a game version and either a profile or aime id, return the profile """ @@ -47,12 +47,12 @@ class CxbProfileData(BaseData): and_(profile.c.version == version, profile.c.user == aime_id) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_profile_index( + async def get_profile_index( self, index: int, aime_id: int = None, version: int = None ) -> Optional[Dict]: """ @@ -72,7 +72,7 @@ class CxbProfileData(BaseData): ) return None - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() diff --git a/titles/cxb/schema/score.py b/titles/cxb/schema/score.py index b6f4f16..7be33d8 100644 --- a/titles/cxb/schema/score.py +++ b/titles/cxb/schema/score.py @@ -58,7 +58,7 @@ ranking = Table( class CxbScoreData(BaseData): - def put_best_score( + async def put_best_score( self, user_id: int, song_mcode: str, @@ -79,7 +79,7 @@ class CxbScoreData(BaseData): conflict = sql.on_duplicate_key_update(data=sql.inserted.data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"{__name__} failed to insert best score! profile: {user_id}, song: {song_mcode}, data: {data}" @@ -88,7 +88,7 @@ class CxbScoreData(BaseData): return result.lastrowid - def put_playlog( + async def put_playlog( self, user_id: int, song_mcode: str, @@ -125,7 +125,7 @@ class CxbScoreData(BaseData): combo=combo, ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"{__name__} failed to insert playlog! profile: {user_id}, song: {song_mcode}, chart: {chart_id}" @@ -134,7 +134,7 @@ class CxbScoreData(BaseData): return result.lastrowid - def put_ranking( + async def put_ranking( self, user_id: int, rev_id: int, song_id: int, score: int, clear: int ) -> Optional[int]: """ @@ -151,7 +151,7 @@ class CxbScoreData(BaseData): conflict = sql.on_duplicate_key_update(score=score) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"{__name__} failed to insert ranking log! profile: {user_id}, score: {score}, clear: {clear}" @@ -160,28 +160,28 @@ class CxbScoreData(BaseData): return result.lastrowid - def get_best_score(self, user_id: int, song_mcode: int) -> Optional[Dict]: + async def get_best_score(self, user_id: int, song_mcode: int) -> Optional[Dict]: sql = score.select( and_(score.c.user == user_id, score.c.song_mcode == song_mcode) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_best_scores(self, user_id: int) -> Optional[Dict]: + async def get_best_scores(self, user_id: int) -> Optional[Dict]: sql = score.select(score.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_best_rankings(self, user_id: int) -> Optional[List[Dict]]: + async def get_best_rankings(self, user_id: int) -> Optional[List[Dict]]: sql = ranking.select(ranking.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() diff --git a/titles/cxb/schema/static.py b/titles/cxb/schema/static.py index 6459e99..b863ef9 100644 --- a/titles/cxb/schema/static.py +++ b/titles/cxb/schema/static.py @@ -29,7 +29,7 @@ music = Table( class CxbStaticData(BaseData): - def put_music( + async def put_music( self, version: int, mcode: str, @@ -55,12 +55,12 @@ class CxbStaticData(BaseData): title=title, artist=artist, category=category, level=level ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_music( + async def get_music( self, version: int, song_id: Optional[int] = None ) -> Optional[List[Row]]: if song_id is None: @@ -73,12 +73,12 @@ class CxbStaticData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_music_chart( + async def get_music_chart( self, version: int, song_id: int, chart_id: int ) -> Optional[List[Row]]: sql = select(music).where( @@ -89,7 +89,7 @@ class CxbStaticData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() diff --git a/titles/diva/base.py b/titles/diva/base.py index eb7d11e..7fe3fb5 100644 --- a/titles/diva/base.py +++ b/titles/diva/base.py @@ -128,7 +128,7 @@ class DivaBase: async def handle_shop_catalog_request(self, data: Dict) -> Dict: catalog = "" - shopList = self.data.static.get_enabled_shops(self.version) + shopList = await self.data.static.get_enabled_shops(self.version) if not shopList: with open(r"titles/diva/data/ShopCatalog.dat", encoding="utf-8") as shop: lines = shop.readlines() @@ -164,8 +164,8 @@ class DivaBase: return response async def handle_buy_module_request(self, data: Dict) -> Dict: - profile = self.data.profile.get_profile(data["pd_id"], self.version) - module = self.data.static.get_enabled_shop(self.version, int(data["mdl_id"])) + profile = await self.data.profile.get_profile(data["pd_id"], self.version) + module = await self.data.static.get_enabled_shop(self.version, int(data["mdl_id"])) # make sure module is available to purchase if not module: @@ -177,11 +177,11 @@ class DivaBase: new_vcld_pts = profile["vcld_pts"] - int(data["mdl_price"]) - self.data.profile.update_profile(profile["user"], vcld_pts=new_vcld_pts) - self.data.module.put_module(data["pd_id"], self.version, data["mdl_id"]) + await self.data.profile.update_profile(profile["user"], vcld_pts=new_vcld_pts) + await self.data.module.put_module(data["pd_id"], self.version, data["mdl_id"]) # generate the mdl_have string - mdl_have = self.data.module.get_modules_have_string(data["pd_id"], self.version) + mdl_have = await self.data.module.get_modules_have_string(data["pd_id"], self.version) response = "&shp_rslt=1" response += f"&mdl_id={data['mdl_id']}" @@ -193,7 +193,7 @@ class DivaBase: async def handle_cstmz_itm_ctlg_request(self, data: Dict) -> Dict: catalog = "" - itemList = self.data.static.get_enabled_items(self.version) + itemList = await self.data.static.get_enabled_items(self.version) if not itemList: with open(r"titles/diva/data/ItemCatalog.dat", encoding="utf-8") as item: lines = item.readlines() @@ -229,8 +229,8 @@ class DivaBase: return response async def handle_buy_cstmz_itm_request(self, data: Dict) -> Dict: - profile = self.data.profile.get_profile(data["pd_id"], self.version) - item = self.data.static.get_enabled_item( + profile = await self.data.profile.get_profile(data["pd_id"], self.version) + item = await self.data.static.get_enabled_item( self.version, int(data["cstmz_itm_id"]) ) @@ -245,14 +245,14 @@ class DivaBase: new_vcld_pts = profile["vcld_pts"] - int(data["cstmz_itm_price"]) # save new Vocaloid Points balance - self.data.profile.update_profile(profile["user"], vcld_pts=new_vcld_pts) + await self.data.profile.update_profile(profile["user"], vcld_pts=new_vcld_pts) - self.data.customize.put_customize_item( + await self.data.customize.put_customize_item( data["pd_id"], self.version, data["cstmz_itm_id"] ) # generate the cstmz_itm_have string - cstmz_itm_have = self.data.customize.get_customize_items_have_string( + cstmz_itm_have = await self.data.customize.get_customize_items_have_string( data["pd_id"], self.version ) @@ -297,7 +297,7 @@ class DivaBase: async def handle_qst_inf_request(self, data: Dict) -> Dict: quest = "" - questList = self.data.static.get_enabled_quests(self.version) + questList = await self.data.static.get_enabled_quests(self.version) if not questList: with open(r"titles/diva/data/QuestInfo.dat", encoding="utf-8") as shop: lines = shop.readlines() @@ -381,8 +381,8 @@ class DivaBase: return f"" async def handle_pre_start_request(self, data: Dict) -> str: - profile = self.data.profile.get_profile(data["aime_id"], self.version) - profile_shop = self.data.item.get_shop(data["aime_id"], self.version) + profile = await self.data.profile.get_profile(data["aime_id"], self.version) + profile_shop = await self.data.item.get_shop(data["aime_id"], self.version) if profile is None: return f"&ps_result=-3" @@ -422,28 +422,28 @@ class DivaBase: return response async def handle_registration_request(self, data: Dict) -> Dict: - self.data.profile.create_profile( + await self.data.profile.create_profile( self.version, data["aime_id"], data["player_name"] ) return f"&cd_adm_result=1&pd_id={data['aime_id']}" async def handle_start_request(self, data: Dict) -> Dict: - profile = self.data.profile.get_profile(data["pd_id"], self.version) - profile_shop = self.data.item.get_shop(data["pd_id"], self.version) + profile = await self.data.profile.get_profile(data["pd_id"], self.version) + profile_shop = await self.data.item.get_shop(data["pd_id"], self.version) if profile is None: return mdl_have = "F" * 250 # generate the mdl_have string if "unlock_all_modules" is disabled if not self.game_config.mods.unlock_all_modules: - mdl_have = self.data.module.get_modules_have_string( + mdl_have = await self.data.module.get_modules_have_string( data["pd_id"], self.version ) cstmz_itm_have = "F" * 250 # generate the cstmz_itm_have string if "unlock_all_items" is disabled if not self.game_config.mods.unlock_all_items: - cstmz_itm_have = self.data.customize.get_customize_items_have_string( + cstmz_itm_have = await self.data.customize.get_customize_items_have_string( data["pd_id"], self.version ) @@ -524,7 +524,7 @@ class DivaBase: } # get clear status from user scores - pv_records = self.data.score.get_best_scores(data["pd_id"]) + pv_records = await self.data.score.get_best_scores(data["pd_id"]) clear_status = "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" if pv_records is not None: @@ -586,7 +586,7 @@ class DivaBase: return f"" async def handle_spend_credit_request(self, data: Dict) -> Dict: - profile = self.data.profile.get_profile(data["pd_id"], self.version) + profile = await self.data.profile.get_profile(data["pd_id"], self.version) if profile is None: return @@ -663,30 +663,30 @@ class DivaBase: return pv_result - def task_generateScoreData(self, data: Dict, pd_by_pv_id, song): + async def task_generateScoreData(self, data: Dict, pd_by_pv_id, song): if int(song) > 0: # the request do not send a edition so just perform a query best score and ranking for each edition. # 0=ORIGINAL, 1=EXTRA - pd_db_song_0 = self.data.score.get_best_user_score( + pd_db_song_0 = await self.data.score.get_best_user_score( data["pd_id"], int(song), data["difficulty"], edition=0 ) - pd_db_song_1 = self.data.score.get_best_user_score( + pd_db_song_1 = await self.data.score.get_best_user_score( data["pd_id"], int(song), data["difficulty"], edition=1 ) pd_db_ranking_0, pd_db_ranking_1 = None, None if pd_db_song_0: - pd_db_ranking_0 = self.data.score.get_global_ranking( + pd_db_ranking_0 = await self.data.score.get_global_ranking( data["pd_id"], int(song), data["difficulty"], edition=0 ) if pd_db_song_1: - pd_db_ranking_1 = self.data.score.get_global_ranking( + pd_db_ranking_1 = await self.data.score.get_global_ranking( data["pd_id"], int(song), data["difficulty"], edition=1 ) - pd_db_customize = self.data.pv_customize.get_pv_customize( + pd_db_customize = await self.data.pv_customize.get_pv_customize( data["pd_id"], int(song) ) @@ -712,7 +712,7 @@ class DivaBase: pd_by_pv_id = [] for song in song_id: - thread_ScoreData = Thread(target=self.task_generateScoreData(data, pd_by_pv_id, song)) + thread_ScoreData = Thread(target=await self.task_generateScoreData(data, pd_by_pv_id, song)) threads.append(thread_ScoreData) for x in threads: @@ -735,7 +735,7 @@ class DivaBase: return f"" async def handle_stage_result_request(self, data: Dict) -> Dict: - profile = self.data.profile.get_profile(data["pd_id"], self.version) + profile = await self.data.profile.get_profile(data["pd_id"], self.version) pd_song_list = data["stg_ply_pv_id"].split(",") pd_song_difficulty = data["stg_difficulty"].split(",") @@ -753,14 +753,14 @@ class DivaBase: for index, value in enumerate(pd_song_list): if "-1" not in pd_song_list[index]: - profile_pd_db_song = self.data.score.get_best_user_score( + profile_pd_db_song = await self.data.score.get_best_user_score( data["pd_id"], pd_song_list[index], pd_song_difficulty[index], pd_song_edition[index], ) if profile_pd_db_song is None: - self.data.score.put_best_score( + await self.data.score.put_best_score( data["pd_id"], self.version, pd_song_list[index], @@ -777,7 +777,7 @@ class DivaBase: pd_song_worst_cnt[index], pd_song_max_combo[index], ) - self.data.score.put_playlog( + await self.data.score.put_playlog( data["pd_id"], self.version, pd_song_list[index], @@ -795,7 +795,7 @@ class DivaBase: pd_song_max_combo[index], ) elif int(pd_song_max_score[index]) >= int(profile_pd_db_song["score"]): - self.data.score.put_best_score( + await self.data.score.put_best_score( data["pd_id"], self.version, pd_song_list[index], @@ -812,7 +812,7 @@ class DivaBase: pd_song_worst_cnt[index], pd_song_max_combo[index], ) - self.data.score.put_playlog( + await self.data.score.put_playlog( data["pd_id"], self.version, pd_song_list[index], @@ -830,7 +830,7 @@ class DivaBase: pd_song_max_combo[index], ) elif int(pd_song_max_score[index]) != int(profile_pd_db_song["score"]): - self.data.score.put_playlog( + await self.data.score.put_playlog( data["pd_id"], self.version, pd_song_list[index], @@ -851,7 +851,7 @@ class DivaBase: # Profile saving based on registration list # Calculate new level - best_scores = self.data.score.get_best_scores(data["pd_id"]) + best_scores = await self.data.score.get_best_scores(data["pd_id"]) total_atn_pnt = 0 for best_score in best_scores: @@ -865,7 +865,7 @@ class DivaBase: response += f"&lv_pnt_old={int(profile['lv_pnt'])}" # update the profile and commit changes to the db - self.data.profile.update_profile( + await self.data.profile.update_profile( profile["user"], lv_num=new_level, lv_pnt=new_level_pnt, @@ -914,15 +914,15 @@ class DivaBase: return response async def handle_end_request(self, data: Dict) -> Dict: - profile = self.data.profile.get_profile(data["pd_id"], self.version) + profile = await self.data.profile.get_profile(data["pd_id"], self.version) - self.data.profile.update_profile( + await self.data.profile.update_profile( profile["user"], my_qst_id=data["my_qst_id"], my_qst_sts=data["my_qst_sts"] ) return f"" async def handle_shop_exit_request(self, data: Dict) -> Dict: - self.data.item.put_shop( + await self.data.item.put_shop( data["pd_id"], self.version, data["mdl_eqp_cmn_ary"], @@ -930,7 +930,7 @@ class DivaBase: data["ms_itm_flg_cmn_ary"], ) if int(data["use_pv_mdl_eqp"]) == 1: - self.data.pv_customize.put_pv_customize( + await self.data.pv_customize.put_pv_customize( data["pd_id"], self.version, data["ply_pv_id"], @@ -939,7 +939,7 @@ class DivaBase: data["ms_itm_flg_pv_ary"], ) else: - self.data.pv_customize.put_pv_customize( + await self.data.pv_customize.put_pv_customize( data["pd_id"], self.version, data["ply_pv_id"], @@ -952,7 +952,7 @@ class DivaBase: return response async def handle_card_procedure_request(self, data: Dict) -> str: - profile = self.data.profile.get_profile(data["aime_id"], self.version) + profile = await self.data.profile.get_profile(data["aime_id"], self.version) if profile is None: return "&cd_adm_result=0" @@ -972,7 +972,7 @@ class DivaBase: return response async def handle_change_name_request(self, data: Dict) -> str: - profile = self.data.profile.get_profile(data["pd_id"], self.version) + profile = await self.data.profile.get_profile(data["pd_id"], self.version) # make sure user has enough Vocaloid Points if profile["vcld_pts"] < int(data["chg_name_price"]): @@ -980,7 +980,7 @@ class DivaBase: # update the vocaloid points and player name new_vcld_pts = profile["vcld_pts"] - int(data["chg_name_price"]) - self.data.profile.update_profile( + await self.data.profile.update_profile( profile["user"], player_name=data["player_name"], vcld_pts=new_vcld_pts ) @@ -992,14 +992,14 @@ class DivaBase: return response async def handle_change_passwd_request(self, data: Dict) -> str: - profile = self.data.profile.get_profile(data["pd_id"], self.version) + profile = await self.data.profile.get_profile(data["pd_id"], self.version) # TODO: return correct error number instead of 0 if data["passwd"] != profile["passwd"]: return "&cd_adm_result=0" # set password to true and update the saved password - self.data.profile.update_profile( + await self.data.profile.update_profile( profile["user"], passwd_stat=1, passwd=data["new_passwd"] ) diff --git a/titles/diva/read.py b/titles/diva/read.py index f143bb6..97c9481 100644 --- a/titles/diva/read.py +++ b/titles/diva/read.py @@ -28,7 +28,7 @@ class DivaReader(BaseReader): self.logger.error(f"Invalid project diva version {version}") exit(1) - def read(self) -> None: + async def read(self) -> None: pull_bin_ram = True pull_bin_rom = True pull_opt_rom = True @@ -48,14 +48,14 @@ class DivaReader(BaseReader): self.logger.warning("No option directory specified, skipping") if pull_bin_ram: - self.read_ram(f"{self.bin_dir}/ram") + await self.read_ram(f"{self.bin_dir}/ram") if pull_bin_rom: - self.read_rom(f"{self.bin_dir}/rom") + await self.read_rom(f"{self.bin_dir}/rom") if pull_opt_rom: for dir in opt_dirs: - self.read_rom(f"{dir}/rom") + await self.read_rom(f"{dir}/rom") - def read_ram(self, ram_root_dir: str) -> None: + async def read_ram(self, ram_root_dir: str) -> None: self.logger.info(f"Read RAM from {ram_root_dir}") if path.exists(f"{ram_root_dir}/databank"): @@ -91,7 +91,7 @@ class DivaReader(BaseReader): f"Added shop item {split[x+0]}" ) - self.data.static.put_shop( + await self.data.static.put_shop( self.version, split[x + 0], split[x + 2], @@ -109,7 +109,7 @@ class DivaReader(BaseReader): for x in range(0, len(split), 7): self.logger.info(f"Added item {split[x+0]}") - self.data.static.put_items( + await self.data.static.put_items( self.version, split[x + 0], split[x + 2], @@ -123,7 +123,7 @@ class DivaReader(BaseReader): elif file.startswith("QuestInfo") and len(split) >= 9: self.logger.info(f"Added quest {split[0]}") - self.data.static.put_quests( + await self.data.static.put_quests( self.version, split[0], split[6], @@ -141,7 +141,7 @@ class DivaReader(BaseReader): else: self.logger.warning(f"Databank folder not found in {ram_root_dir}, skipping") - def read_rom(self, rom_root_dir: str) -> None: + async def read_rom(self, rom_root_dir: str) -> None: self.logger.info(f"Read ROM from {rom_root_dir}") pv_list: Dict[str, Dict] = {} @@ -199,7 +199,7 @@ class DivaReader(BaseReader): diff = pv_data["difficulty"]["easy"]["0"]["level"].split("_") self.logger.info(f"Added song {song_id} chart 0") - self.data.static.put_music( + await self.data.static.put_music( self.version, song_id, 0, @@ -220,7 +220,7 @@ class DivaReader(BaseReader): diff = pv_data["difficulty"]["normal"]["0"]["level"].split("_") self.logger.info(f"Added song {song_id} chart 1") - self.data.static.put_music( + await self.data.static.put_music( self.version, song_id, 1, @@ -238,7 +238,7 @@ class DivaReader(BaseReader): diff = pv_data["difficulty"]["hard"]["0"]["level"].split("_") self.logger.info(f"Added song {song_id} chart 2") - self.data.static.put_music( + await self.data.static.put_music( self.version, song_id, 2, @@ -257,7 +257,7 @@ class DivaReader(BaseReader): diff = pv_data["difficulty"]["extreme"]["0"]["level"].split("_") self.logger.info(f"Added song {song_id} chart 3") - self.data.static.put_music( + await self.data.static.put_music( self.version, song_id, 3, @@ -275,7 +275,7 @@ class DivaReader(BaseReader): diff = pv_data["difficulty"]["extreme"]["1"]["level"].split("_") self.logger.info(f"Added song {song_id} chart 4") - self.data.static.put_music( + await self.data.static.put_music( self.version, song_id, 4, diff --git a/titles/diva/schema/customize.py b/titles/diva/schema/customize.py index 91480f5..838a7d2 100644 --- a/titles/diva/schema/customize.py +++ b/titles/diva/schema/customize.py @@ -25,10 +25,10 @@ customize = Table( class DivaCustomizeItemData(BaseData): - def put_customize_item(self, aime_id: int, version: int, item_id: int) -> None: + async def put_customize_item(self, aime_id: int, version: int, item_id: int) -> None: sql = insert(customize).values(version=version, user=aime_id, item_id=item_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"{__name__} Failed to insert diva profile customize item! aime id: {aime_id} item: {item_id}" @@ -36,7 +36,7 @@ class DivaCustomizeItemData(BaseData): return None return result.lastrowid - def get_customize_items(self, aime_id: int, version: int) -> Optional[List[Dict]]: + async def get_customize_items(self, aime_id: int, version: int) -> Optional[List[Dict]]: """ Given a game version and an aime id, return all the customize items, not used directly """ @@ -44,12 +44,12 @@ class DivaCustomizeItemData(BaseData): and_(customize.c.version == version, customize.c.user == aime_id) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_customize_items_have_string(self, aime_id: int, version: int) -> str: + async def get_customize_items_have_string(self, aime_id: int, version: int) -> str: """ Given a game version and an aime id, return the cstmz_itm_have hex string required for diva directly diff --git a/titles/diva/schema/item.py b/titles/diva/schema/item.py index 4d484ae..c09896b 100644 --- a/titles/diva/schema/item.py +++ b/titles/diva/schema/item.py @@ -26,7 +26,7 @@ shop = Table( class DivaItemData(BaseData): - def put_shop( + async def put_shop( self, aime_id: int, version: int, @@ -48,7 +48,7 @@ class DivaItemData(BaseData): ms_itm_flg_ary=ms_itm_flg_ary, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"{__name__} Failed to insert diva profile! aime id: {aime_id} array: {mdl_eqp_ary}" @@ -56,13 +56,13 @@ class DivaItemData(BaseData): return None return result.lastrowid - def get_shop(self, aime_id: int, version: int) -> Optional[List[Dict]]: + async def get_shop(self, aime_id: int, version: int) -> Optional[List[Dict]]: """ Given a game version and either a profile or aime id, return the profile """ sql = shop.select(and_(shop.c.version == version, shop.c.user == aime_id)) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() diff --git a/titles/diva/schema/module.py b/titles/diva/schema/module.py index 5872d68..a6286ee 100644 --- a/titles/diva/schema/module.py +++ b/titles/diva/schema/module.py @@ -23,10 +23,10 @@ module = Table( class DivaModuleData(BaseData): - def put_module(self, aime_id: int, version: int, module_id: int) -> None: + async def put_module(self, aime_id: int, version: int, module_id: int) -> None: sql = insert(module).values(version=version, user=aime_id, module_id=module_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"{__name__} Failed to insert diva profile module! aime id: {aime_id} module: {module_id}" @@ -34,18 +34,18 @@ class DivaModuleData(BaseData): return None return result.lastrowid - def get_modules(self, aime_id: int, version: int) -> Optional[List[Dict]]: + async def get_modules(self, aime_id: int, version: int) -> Optional[List[Dict]]: """ Given a game version and an aime id, return all the modules, not used directly """ sql = module.select(and_(module.c.version == version, module.c.user == aime_id)) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_modules_have_string(self, aime_id: int, version: int) -> str: + async def get_modules_have_string(self, aime_id: int, version: int) -> str: """ Given a game version and an aime id, return the mdl_have hex string required for diva directly diff --git a/titles/diva/schema/profile.py b/titles/diva/schema/profile.py index 7bd6bf0..f3d00ae 100644 --- a/titles/diva/schema/profile.py +++ b/titles/diva/schema/profile.py @@ -70,7 +70,7 @@ profile = Table( class DivaProfileData(BaseData): - def create_profile( + async def create_profile( self, version: int, aime_id: int, player_name: str ) -> Optional[int]: """ @@ -82,7 +82,7 @@ class DivaProfileData(BaseData): conflict = sql.on_duplicate_key_update(player_name=sql.inserted.player_name) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"{__name__} Failed to insert diva profile! aime id: {aime_id} username: {player_name}" @@ -90,21 +90,21 @@ class DivaProfileData(BaseData): return None return result.lastrowid - def update_profile(self, aime_id: int, **profile_args) -> None: + async def update_profile(self, aime_id: int, **profile_args) -> None: """ Given an aime_id update the profile corresponding to the arguments which are the diva_profile Columns """ sql = profile.update(profile.c.user == aime_id).values(**profile_args) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"update_profile: failed to update profile! profile: {aime_id}" ) return None - def get_profile(self, aime_id: int, version: int) -> Optional[List[Dict]]: + async def get_profile(self, aime_id: int, version: int) -> Optional[List[Dict]]: """ Given a game version and either a profile or aime id, return the profile """ @@ -112,7 +112,7 @@ class DivaProfileData(BaseData): and_(profile.c.version == version, profile.c.user == aime_id) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() diff --git a/titles/diva/schema/pv_customize.py b/titles/diva/schema/pv_customize.py index 1ca8909..c56378d 100644 --- a/titles/diva/schema/pv_customize.py +++ b/titles/diva/schema/pv_customize.py @@ -39,7 +39,7 @@ pv_customize = Table( class DivaPvCustomizeData(BaseData): - def put_pv_customize( + async def put_pv_customize( self, aime_id: int, version: int, @@ -64,7 +64,7 @@ class DivaPvCustomizeData(BaseData): ms_itm_flg_ary=ms_itm_flg_ary, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"{__name__} Failed to insert diva pv customize! aime id: {aime_id}" @@ -72,7 +72,7 @@ class DivaPvCustomizeData(BaseData): return None return result.lastrowid - def get_pv_customize(self, aime_id: int, pv_id: int) -> Optional[List[Dict]]: + async def get_pv_customize(self, aime_id: int, pv_id: int) -> Optional[List[Dict]]: """ Given either a profile or aime id, return a Pv Customize row """ @@ -80,7 +80,7 @@ class DivaPvCustomizeData(BaseData): and_(pv_customize.c.user == aime_id, pv_customize.c.pv_id == pv_id) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() diff --git a/titles/diva/schema/score.py b/titles/diva/schema/score.py index 2171659..e802a41 100644 --- a/titles/diva/schema/score.py +++ b/titles/diva/schema/score.py @@ -57,7 +57,7 @@ playlog = Table( class DivaScoreData(BaseData): - def put_best_score( + async def put_best_score( self, user_id: int, game_version: int, @@ -109,7 +109,7 @@ class DivaScoreData(BaseData): max_combo=max_combo, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"{__name__} failed to insert best score! profile: {user_id}, song: {song_id}" @@ -118,7 +118,7 @@ class DivaScoreData(BaseData): return result.lastrowid - def put_playlog( + async def put_playlog( self, user_id: int, game_version: int, @@ -157,7 +157,7 @@ class DivaScoreData(BaseData): max_combo=max_combo, ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"{__name__} failed to insert playlog! profile: {user_id}, song: {song_id}, chart: {difficulty}" @@ -166,7 +166,7 @@ class DivaScoreData(BaseData): return result.lastrowid - def get_best_user_score( + async def get_best_user_score( self, user_id: int, pv_id: int, difficulty: int, edition: int ) -> Optional[Row]: sql = score.select( @@ -178,12 +178,12 @@ class DivaScoreData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_top3_scores( + async def get_top3_scores( self, pv_id: int, difficulty: int, edition: int ) -> Optional[List[Row]]: sql = ( @@ -198,12 +198,12 @@ class DivaScoreData(BaseData): .limit(3) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_global_ranking( + async def get_global_ranking( self, user_id: int, pv_id: int, difficulty: int, edition: int ) -> Optional[List[Row]]: # get the subquery max score of a user with pv_id, difficulty and @@ -227,15 +227,15 @@ class DivaScoreData(BaseData): score.c.edition == edition, ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_best_scores(self, user_id: int) -> Optional[List[Row]]: + async def get_best_scores(self, user_id: int) -> Optional[List[Row]]: sql = score.select(score.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() diff --git a/titles/diva/schema/static.py b/titles/diva/schema/static.py index 02ee0ec..e6aa207 100644 --- a/titles/diva/schema/static.py +++ b/titles/diva/schema/static.py @@ -83,7 +83,7 @@ items = Table( class DivaStaticData(BaseData): - def put_quests( + async def put_quests( self, version: int, questId: int, @@ -111,22 +111,22 @@ class DivaStaticData(BaseData): conflict = sql.on_duplicate_key_update(name=name) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_enabled_quests(self, version: int) -> Optional[List[Row]]: + async def get_enabled_quests(self, version: int) -> Optional[List[Row]]: sql = select(quests).where( and_(quests.c.version == version, quests.c.quest_enable == True) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_shop( + async def put_shop( self, version: int, shopId: int, @@ -150,12 +150,12 @@ class DivaStaticData(BaseData): conflict = sql.on_duplicate_key_update(name=name) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_enabled_shop(self, version: int, shopId: int) -> Optional[Row]: + async def get_enabled_shop(self, version: int, shopId: int) -> Optional[Row]: sql = select(shop).where( and_( shop.c.version == version, @@ -164,22 +164,22 @@ class DivaStaticData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_enabled_shops(self, version: int) -> Optional[List[Row]]: + async def get_enabled_shops(self, version: int) -> Optional[List[Row]]: sql = select(shop).where( and_(shop.c.version == version, shop.c.enabled == True) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_items( + async def put_items( self, version: int, itemId: int, @@ -203,12 +203,12 @@ class DivaStaticData(BaseData): conflict = sql.on_duplicate_key_update(name=name) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_enabled_item(self, version: int, itemId: int) -> Optional[Row]: + async def get_enabled_item(self, version: int, itemId: int) -> Optional[Row]: sql = select(items).where( and_( items.c.version == version, @@ -217,22 +217,22 @@ class DivaStaticData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_enabled_items(self, version: int) -> Optional[List[Row]]: + async def get_enabled_items(self, version: int) -> Optional[List[Row]]: sql = select(items).where( and_(items.c.version == version, items.c.enabled == True) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_music( + async def put_music( self, version: int, song: int, @@ -271,12 +271,12 @@ class DivaStaticData(BaseData): date=date, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_music( + async def get_music( self, version: int, song_id: Optional[int] = None ) -> Optional[List[Row]]: if song_id is None: @@ -289,12 +289,12 @@ class DivaStaticData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_music_chart( + async def get_music_chart( self, version: int, song_id: int, chart_id: int ) -> Optional[List[Row]]: sql = select(music).where( @@ -305,7 +305,7 @@ class DivaStaticData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() diff --git a/titles/idac/frontend.py b/titles/idac/frontend.py index 8845b35..b5ac3ad 100644 --- a/titles/idac/frontend.py +++ b/titles/idac/frontend.py @@ -37,7 +37,7 @@ class IDACFrontend(FE_Base): 34: "full_tune_fragments", } - def generate_all_tables_json(self, user_id: int): + async def generate_all_tables_json(self, user_id: int): json_export = {} idac_tables = { @@ -73,7 +73,7 @@ class IDACFrontend(FE_Base): sql = sql.where(table.c.version == self.version) # lol use the profile connection for items, dirty hack - result = self.data.profile.execute(sql) + result = await self.data.profile.execute(sql) data_list = result.fetchall() # add the list to the json export with the correct table name @@ -86,7 +86,7 @@ class IDACFrontend(FE_Base): return json.dumps(json_export, indent=4, default=str, ensure_ascii=False) - def render_GET(self, request: Request) -> bytes: + async def render_GET(self, request: Request) -> bytes: uri: str = request.uri.decode() template = self.environment.get_template( @@ -103,7 +103,7 @@ class IDACFrontend(FE_Base): return redirectTo(b"/game/idac", request) # set the file name, content type and size to download the json - content = self.generate_all_tables_json(user_id).encode("utf-8") + content = await self.generate_all_tables_json(user_id).encode("utf-8") request.responseHeaders.addRawHeader( b"content-type", b"application/octet-stream" ) @@ -119,9 +119,9 @@ class IDACFrontend(FE_Base): profile_data, tickets, rank = None, None, None if user_id > 0: - profile_data = self.data.profile.get_profile(user_id, self.version) - ticket_data = self.data.item.get_tickets(user_id) - rank = self.data.profile.get_profile_rank(user_id, self.version) + profile_data = await self.data.profile.get_profile(user_id, self.version) + ticket_data = await self.data.item.get_tickets(user_id) + rank = await self.data.profile.get_profile_rank(user_id, self.version) tickets = { self.ticket_names[ticket["ticket_id"]]: ticket["ticket_cnt"] @@ -137,6 +137,3 @@ class IDACFrontend(FE_Base): sesh=vars(usr_sesh), active_page="idac", ).encode("utf-16") - - def render_POST(self, request: Request) -> bytes: - pass diff --git a/titles/idac/index.py b/titles/idac/index.py index d6ac62a..d923946 100644 --- a/titles/idac/index.py +++ b/titles/idac/index.py @@ -124,7 +124,7 @@ class IDACServlet(BaseServlet): resp = None try: handler = getattr(self.versions[internal_ver], func_to_find) - resp = handler(req_data, header_application) + resp = await handler(req_data, header_application) except Exception as e: traceback.print_exc() diff --git a/titles/idac/read.py b/titles/idac/read.py index 8798e9b..3b7e034 100644 --- a/titles/idac/read.py +++ b/titles/idac/read.py @@ -33,7 +33,7 @@ class IDACReader(BaseReader): self.logger.error(f"Invalid Initial D THE ARCADE version {version}") exit(1) - def read(self) -> None: + async def read(self) -> None: if self.bin_dir is None and self.opt_dir is None: self.logger.error( ( @@ -59,9 +59,9 @@ class IDACReader(BaseReader): ) exit(1) - self.read_idac_profile(self.opt_dir) + await self.read_idac_profile(self.opt_dir) - def read_idac_profile(self, file_path: str) -> None: + async def read_idac_profile(self, file_path: str) -> None: self.logger.info(f"Reading profile from {file_path}...") # read it as binary to avoid encoding issues @@ -88,14 +88,14 @@ class IDACReader(BaseReader): self.logger.info("Exiting...") exit(0) - user_id = self.data.user.create_user() + user_id = await self.data.user.create_user() if user_id is None: self.logger.error("Failed to register user!") user_id = -1 else: - card_id = self.data.card.create_card(user_id, access_code) + card_id = await self.data.card.create_card(user_id, access_code) if card_id is None: self.logger.error("Failed to register card!") @@ -150,7 +150,7 @@ class IDACReader(BaseReader): # lol use the profile connection for items, dirty hack conflict = sql.on_duplicate_key_update(**data) - result = self.data.profile.execute(conflict) + result = await self.data.profile.execute(conflict) if result is None: self.logger.error(f"Failed to insert data into table {name}") diff --git a/titles/idac/schema/item.py b/titles/idac/schema/item.py index 80ee7ba..d617cd9 100644 --- a/titles/idac/schema/item.py +++ b/titles/idac/schema/item.py @@ -297,7 +297,7 @@ timetrial_event = Table( class IDACItemData(BaseData): - def get_random_user_car(self, aime_id: int, version: int) -> Optional[List[Row]]: + async def get_random_user_car(self, aime_id: int, version: int) -> Optional[List[Row]]: sql = ( select(car) .where(and_(car.c.user == aime_id, car.c.version == version)) @@ -305,20 +305,20 @@ class IDACItemData(BaseData): .limit(1) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_random_car(self, version: int) -> Optional[List[Row]]: + async def get_random_car(self, version: int) -> Optional[List[Row]]: sql = select(car).where(car.c.version == version).order_by(func.rand()).limit(1) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_car( + async def get_car( self, aime_id: int, version: int, style_car_id: int ) -> Optional[List[Row]]: sql = select(car).where( @@ -329,12 +329,12 @@ class IDACItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_cars( + async def get_cars( self, version: int, aime_id: int, only_pickup: bool = False ) -> Optional[List[Row]]: if only_pickup: @@ -350,106 +350,106 @@ class IDACItemData(BaseData): and_(car.c.user == aime_id, car.c.version == version) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_ticket(self, aime_id: int, ticket_id: int) -> Optional[Row]: + async def get_ticket(self, aime_id: int, ticket_id: int) -> Optional[Row]: sql = select(ticket).where( ticket.c.user == aime_id, ticket.c.ticket_id == ticket_id ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_tickets(self, aime_id: int) -> Optional[List[Row]]: + async def get_tickets(self, aime_id: int) -> Optional[List[Row]]: sql = select(ticket).where(ticket.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_story(self, aime_id: int, chapter_id: int) -> Optional[Row]: + async def get_story(self, aime_id: int, chapter_id: int) -> Optional[Row]: sql = select(story).where( and_(story.c.user == aime_id, story.c.chapter == chapter_id) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_stories(self, aime_id: int) -> Optional[List[Row]]: + async def get_stories(self, aime_id: int) -> Optional[List[Row]]: sql = select(story).where(story.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_story_episodes(self, aime_id: int, chapter_id: int) -> Optional[List[Row]]: + async def get_story_episodes(self, aime_id: int, chapter_id: int) -> Optional[List[Row]]: sql = select(episode).where( and_(episode.c.user == aime_id, episode.c.chapter == chapter_id) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_story_episode(self, aime_id: int, episode_id: int) -> Optional[Row]: + async def get_story_episode(self, aime_id: int, episode_id: int) -> Optional[Row]: sql = select(episode).where( and_(episode.c.user == aime_id, episode.c.episode == episode_id) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_story_episode_difficulties( + async def get_story_episode_difficulties( self, aime_id: int, episode_id: int ) -> Optional[List[Row]]: sql = select(difficulty).where( and_(difficulty.c.user == aime_id, difficulty.c.episode == episode_id) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_courses(self, aime_id: int) -> Optional[List[Row]]: + async def get_courses(self, aime_id: int) -> Optional[List[Row]]: sql = select(course).where(course.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_course(self, aime_id: int, course_id: int) -> Optional[Row]: + async def get_course(self, aime_id: int, course_id: int) -> Optional[Row]: sql = select(course).where( and_(course.c.user == aime_id, course.c.course_id == course_id) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_time_trial_courses(self, version: int) -> Optional[List[Row]]: + async def get_time_trial_courses(self, version: int) -> Optional[List[Row]]: sql = select(trial.c.course_id).where(trial.c.version == version).distinct() - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_time_trial_user_best_time_by_course_car( + async def get_time_trial_user_best_time_by_course_car( self, version: int, aime_id: int, course_id: int, style_car_id: int ) -> Optional[Row]: sql = select(trial).where( @@ -461,12 +461,12 @@ class IDACItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_time_trial_user_best_courses( + async def get_time_trial_user_best_courses( self, version: int, aime_id: int ) -> Optional[List[Row]]: # get for a given aime_id the best time for each course @@ -491,12 +491,12 @@ class IDACItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_time_trial_best_cars_by_course( + async def get_time_trial_best_cars_by_course( self, version: int, course_id: int, aime_id: Optional[int] = None ) -> Optional[List[Row]]: subquery = ( @@ -527,12 +527,12 @@ class IDACItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_time_trial_ranking_by_course( + async def get_time_trial_ranking_by_course( self, version: int, course_id: int, @@ -568,12 +568,12 @@ class IDACItemData(BaseData): if limit is not None: sql = sql.limit(limit) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_time_trial_best_ranking_by_course( + async def get_time_trial_best_ranking_by_course( self, version: int, aime_id: int, course_id: int ) -> Optional[Row]: sql = ( @@ -589,12 +589,12 @@ class IDACItemData(BaseData): .limit(1) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_challenge( + async def get_challenge( self, aime_id: int, vs_type: int, play_difficulty: int ) -> Optional[Row]: sql = select(challenge).where( @@ -605,20 +605,20 @@ class IDACItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_challenges(self, aime_id: int) -> Optional[List[Row]]: + async def get_challenges(self, aime_id: int) -> Optional[List[Row]]: sql = select(challenge).where(challenge.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_best_challenges_by_vs_type( + async def get_best_challenges_by_vs_type( self, aime_id: int, story_type: int = 4 ) -> Optional[List[Row]]: subquery = ( @@ -653,20 +653,20 @@ class IDACItemData(BaseData): .order_by(challenge.c.vs_type) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_theory_courses(self, aime_id: int) -> Optional[List[Row]]: + async def get_theory_courses(self, aime_id: int) -> Optional[List[Row]]: sql = select(theory_course).where(theory_course.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_theory_course_by_powerhouse_lv( + async def get_theory_course_by_powerhouse_lv( self, aime_id: int, course_id: int, powerhouse_lv: int, count: int = 3 ) -> Optional[List[Row]]: sql = ( @@ -682,40 +682,40 @@ class IDACItemData(BaseData): .limit(count) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_theory_course(self, aime_id: int, course_id: int) -> Optional[List[Row]]: + async def get_theory_course(self, aime_id: int, course_id: int) -> Optional[List[Row]]: sql = select(theory_course).where( and_( theory_course.c.user == aime_id, theory_course.c.course_id == course_id ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_theory_partners(self, aime_id: int) -> Optional[List[Row]]: + async def get_theory_partners(self, aime_id: int) -> Optional[List[Row]]: sql = select(theory_partner).where(theory_partner.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_theory_running(self, aime_id: int) -> Optional[List[Row]]: + async def get_theory_running(self, aime_id: int) -> Optional[List[Row]]: sql = select(theory_running).where(theory_running.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_theory_running_by_course( + async def get_theory_running_by_course( self, aime_id: int, course_id: int ) -> Optional[Row]: sql = select(theory_running).where( @@ -725,32 +725,32 @@ class IDACItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_vs_infos(self, aime_id: int) -> Optional[List[Row]]: + async def get_vs_infos(self, aime_id: int) -> Optional[List[Row]]: sql = select(vs_info).where(vs_info.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_stamps(self, aime_id: int) -> Optional[List[Row]]: + async def get_stamps(self, aime_id: int) -> Optional[List[Row]]: sql = select(stamp).where( and_( stamp.c.user == aime_id, ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_timetrial_event(self, aime_id: int, timetrial_event_id: int) -> Optional[Row]: + async def get_timetrial_event(self, aime_id: int, timetrial_event_id: int) -> Optional[Row]: sql = select(timetrial_event).where( and_( timetrial_event.c.user == aime_id, @@ -758,49 +758,49 @@ class IDACItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_car(self, aime_id: int, version: int, car_data: Dict) -> Optional[int]: + async def put_car(self, aime_id: int, version: int, car_data: Dict) -> Optional[int]: car_data["user"] = aime_id car_data["version"] = version sql = insert(car).values(**car_data) conflict = sql.on_duplicate_key_update(**car_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn(f"put_car: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def put_ticket(self, aime_id: int, ticket_data: Dict) -> Optional[int]: + async def put_ticket(self, aime_id: int, ticket_data: Dict) -> Optional[int]: ticket_data["user"] = aime_id sql = insert(ticket).values(**ticket_data) conflict = sql.on_duplicate_key_update(**ticket_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn(f"put_ticket: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def put_story(self, aime_id: int, story_data: Dict) -> Optional[int]: + async def put_story(self, aime_id: int, story_data: Dict) -> Optional[int]: story_data["user"] = aime_id sql = insert(story).values(**story_data) conflict = sql.on_duplicate_key_update(**story_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn(f"put_story: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def put_story_episode_play_status( + async def put_story_episode_play_status( self, aime_id: int, chapter_id: int, play_status: int = 1 ) -> Optional[int]: sql = ( @@ -809,7 +809,7 @@ class IDACItemData(BaseData): .values(play_status=play_status) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.warn( f"put_story_episode_play_status: Failed to update! aime_id: {aime_id}" @@ -817,7 +817,7 @@ class IDACItemData(BaseData): return None return result.lastrowid - def put_story_episode( + async def put_story_episode( self, aime_id: int, chapter_id: int, episode_data: Dict ) -> Optional[int]: episode_data["user"] = aime_id @@ -825,14 +825,14 @@ class IDACItemData(BaseData): sql = insert(episode).values(**episode_data) conflict = sql.on_duplicate_key_update(**episode_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn(f"put_story_episode: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def put_story_episode_difficulty( + async def put_story_episode_difficulty( self, aime_id: int, episode_id: int, difficulty_data: Dict ) -> Optional[int]: difficulty_data["user"] = aime_id @@ -840,7 +840,7 @@ class IDACItemData(BaseData): sql = insert(difficulty).values(**difficulty_data) conflict = sql.on_duplicate_key_update(**difficulty_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn( @@ -849,19 +849,19 @@ class IDACItemData(BaseData): return None return result.lastrowid - def put_course(self, aime_id: int, course_data: Dict) -> Optional[int]: + async def put_course(self, aime_id: int, course_data: Dict) -> Optional[int]: course_data["user"] = aime_id sql = insert(course).values(**course_data) conflict = sql.on_duplicate_key_update(**course_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn(f"put_course: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def put_time_trial( + async def put_time_trial( self, version: int, aime_id: int, time_trial_data: Dict ) -> Optional[int]: time_trial_data["user"] = aime_id @@ -869,47 +869,47 @@ class IDACItemData(BaseData): sql = insert(trial).values(**time_trial_data) conflict = sql.on_duplicate_key_update(**time_trial_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn(f"put_time_trial: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def put_challenge(self, aime_id: int, challenge_data: Dict) -> Optional[int]: + async def put_challenge(self, aime_id: int, challenge_data: Dict) -> Optional[int]: challenge_data["user"] = aime_id sql = insert(challenge).values(**challenge_data) conflict = sql.on_duplicate_key_update(**challenge_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn(f"put_challenge: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def put_theory_course( + async def put_theory_course( self, aime_id: int, theory_course_data: Dict ) -> Optional[int]: theory_course_data["user"] = aime_id sql = insert(theory_course).values(**theory_course_data) conflict = sql.on_duplicate_key_update(**theory_course_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn(f"put_theory_course: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def put_theory_partner( + async def put_theory_partner( self, aime_id: int, theory_partner_data: Dict ) -> Optional[int]: theory_partner_data["user"] = aime_id sql = insert(theory_partner).values(**theory_partner_data) conflict = sql.on_duplicate_key_update(**theory_partner_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn( @@ -918,14 +918,14 @@ class IDACItemData(BaseData): return None return result.lastrowid - def put_theory_running( + async def put_theory_running( self, aime_id: int, theory_running_data: Dict ) -> Optional[int]: theory_running_data["user"] = aime_id sql = insert(theory_running).values(**theory_running_data) conflict = sql.on_duplicate_key_update(**theory_running_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn( @@ -934,26 +934,26 @@ class IDACItemData(BaseData): return None return result.lastrowid - def put_vs_info(self, aime_id: int, vs_info_data: Dict) -> Optional[int]: + async def put_vs_info(self, aime_id: int, vs_info_data: Dict) -> Optional[int]: vs_info_data["user"] = aime_id sql = insert(vs_info).values(**vs_info_data) conflict = sql.on_duplicate_key_update(**vs_info_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn(f"put_vs_info: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def put_stamp( + async def put_stamp( self, aime_id: int, stamp_data: Dict ) -> Optional[int]: stamp_data["user"] = aime_id sql = insert(stamp).values(**stamp_data) conflict = sql.on_duplicate_key_update(**stamp_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn( @@ -962,7 +962,7 @@ class IDACItemData(BaseData): return None return result.lastrowid - def put_timetrial_event( + async def put_timetrial_event( self, aime_id: int, time_trial_event_id: int, point: int ) -> Optional[int]: timetrial_event_data = { @@ -973,7 +973,7 @@ class IDACItemData(BaseData): sql = insert(timetrial_event).values(**timetrial_event_data) conflict = sql.on_duplicate_key_update(**timetrial_event_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn( diff --git a/titles/idac/schema/profile.py b/titles/idac/schema/profile.py index 5e363ca..bb6593b 100644 --- a/titles/idac/schema/profile.py +++ b/titles/idac/schema/profile.py @@ -253,7 +253,7 @@ class IDACProfileData(BaseData): ) self.date_time_format_short = "%Y-%m-%d" - def get_profile(self, aime_id: int, version: int) -> Optional[Row]: + async def get_profile(self, aime_id: int, version: int) -> Optional[Row]: sql = select(profile).where( and_( profile.c.user == aime_id, @@ -261,12 +261,12 @@ class IDACProfileData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_different_random_profiles( + async def get_different_random_profiles( self, aime_id: int, version: int, count: int = 9 ) -> Optional[Row]: sql = ( @@ -281,36 +281,36 @@ class IDACProfileData(BaseData): .limit(count) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_profile_config(self, aime_id: int) -> Optional[Row]: + async def get_profile_config(self, aime_id: int) -> Optional[Row]: sql = select(config).where( and_( config.c.user == aime_id, ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_profile_avatar(self, aime_id: int) -> Optional[Row]: + async def get_profile_avatar(self, aime_id: int) -> Optional[Row]: sql = select(avatar).where( and_( avatar.c.user == aime_id, ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_profile_rank(self, aime_id: int, version: int) -> Optional[Row]: + async def get_profile_rank(self, aime_id: int, version: int) -> Optional[Row]: sql = select(rank).where( and_( rank.c.user == aime_id, @@ -318,12 +318,12 @@ class IDACProfileData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_profile_stock(self, aime_id: int, version: int) -> Optional[Row]: + async def get_profile_stock(self, aime_id: int, version: int) -> Optional[Row]: sql = select(stock).where( and_( stock.c.user == aime_id, @@ -331,12 +331,12 @@ class IDACProfileData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_profile_theory(self, aime_id: int, version: int) -> Optional[Row]: + async def get_profile_theory(self, aime_id: int, version: int) -> Optional[Row]: sql = select(theory).where( and_( theory.c.user == aime_id, @@ -344,12 +344,12 @@ class IDACProfileData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_profile( + async def put_profile( self, aime_id: int, version: int, profile_data: Dict ) -> Optional[int]: profile_data["user"] = aime_id @@ -357,19 +357,19 @@ class IDACProfileData(BaseData): sql = insert(profile).values(**profile_data) conflict = sql.on_duplicate_key_update(**profile_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn(f"put_profile: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def put_profile_config(self, aime_id: int, config_data: Dict) -> Optional[int]: + async def put_profile_config(self, aime_id: int, config_data: Dict) -> Optional[int]: config_data["user"] = aime_id sql = insert(config).values(**config_data) conflict = sql.on_duplicate_key_update(**config_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn( @@ -378,12 +378,12 @@ class IDACProfileData(BaseData): return None return result.lastrowid - def put_profile_avatar(self, aime_id: int, avatar_data: Dict) -> Optional[int]: + async def put_profile_avatar(self, aime_id: int, avatar_data: Dict) -> Optional[int]: avatar_data["user"] = aime_id sql = insert(avatar).values(**avatar_data) conflict = sql.on_duplicate_key_update(**avatar_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn( @@ -392,7 +392,7 @@ class IDACProfileData(BaseData): return None return result.lastrowid - def put_profile_rank( + async def put_profile_rank( self, aime_id: int, version: int, rank_data: Dict ) -> Optional[int]: rank_data["user"] = aime_id @@ -400,14 +400,14 @@ class IDACProfileData(BaseData): sql = insert(rank).values(**rank_data) conflict = sql.on_duplicate_key_update(**rank_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn(f"put_profile_rank: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def put_profile_stock( + async def put_profile_stock( self, aime_id: int, version: int, stock_data: Dict ) -> Optional[int]: stock_data["user"] = aime_id @@ -415,14 +415,14 @@ class IDACProfileData(BaseData): sql = insert(stock).values(**stock_data) conflict = sql.on_duplicate_key_update(**stock_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn(f"put_profile_stock: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def put_profile_theory( + async def put_profile_theory( self, aime_id: int, version: int, theory_data: Dict ) -> Optional[int]: theory_data["user"] = aime_id @@ -430,7 +430,7 @@ class IDACProfileData(BaseData): sql = insert(theory).values(**theory_data) conflict = sql.on_duplicate_key_update(**theory_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warn( diff --git a/titles/idac/season2.py b/titles/idac/season2.py index 6523177..f25296c 100644 --- a/titles/idac/season2.py +++ b/titles/idac/season2.py @@ -53,7 +53,7 @@ class IDACSeason2(IDACBase): "timetrial_event_id" ) - def handle_alive_get_request(self, data: Dict, headers: Dict): + async def handle_alive_get_request(self, data: Dict, headers: Dict): return { "status_code": "0", # 1 = success, 0 = failed @@ -76,7 +76,7 @@ class IDACSeason2(IDACBase): output[key] = value return output - def handle_boot_getconfigdata_request(self, data: Dict, headers: Dict): + async def handle_boot_getconfigdata_request(self, data: Dict, headers: Dict): """ category: 1 = D Coin @@ -297,10 +297,10 @@ class IDACSeason2(IDACBase): "timetrial_event_data": self.timetrial_event, } - def handle_boot_bookkeep_request(self, data: Dict, headers: Dict): + async def handle_boot_bookkeep_request(self, data: Dict, headers: Dict): pass - def handle_boot_getgachadata_request(self, data: Dict, headers: Dict): + async def handle_boot_getgachadata_request(self, data: Dict, headers: Dict): """ Reward category types: 9: Face @@ -350,7 +350,7 @@ class IDACSeason2(IDACBase): return avatar_gacha_data - def handle_boot_gettimereleasedata_request(self, data: Dict, headers: Dict): + async def handle_boot_gettimereleasedata_request(self, data: Dict, headers: Dict): """ timerelease chapter: 1 = Story: 1, 2, 3, 4, 5, 6, 7, 8, 9, 19 (Chapter 10), (29 Chapter 11 lol?) @@ -385,12 +385,12 @@ class IDACSeason2(IDACBase): return time_release_data - def handle_advertise_getrankingdata_request(self, data: Dict, headers: Dict): + async def handle_advertise_getrankingdata_request(self, data: Dict, headers: Dict): best_data = [] for last_update in data.get("last_update_date"): course_id = last_update.get("course_id") - ranking = self.data.item.get_time_trial_ranking_by_course( + ranking = await self.data.item.get_time_trial_ranking_by_course( self.version, course_id ) ranking_data = [] @@ -398,8 +398,8 @@ class IDACSeason2(IDACBase): user_id = rank["user"] # get the username, country and store from the profile - profile = self.data.profile.get_profile(user_id, self.version) - arcade = self.data.arcade.get_arcade(profile["store"]) + profile = await self.data.profile.get_profile(user_id, self.version) + arcade = await self.data.arcade.get_arcade(profile["store"]) if arcade is None: arcade = {} @@ -444,7 +444,7 @@ class IDACSeason2(IDACBase): "rank_management_flag": 0, } - def handle_login_checklock_request(self, data: Dict, headers: Dict): + async def handle_login_checklock_request(self, data: Dict, headers: Dict): user_id = data["id"] access_code = data["accesscode"] is_new_player = 0 @@ -454,7 +454,7 @@ class IDACSeason2(IDACBase): lock_result = 1 # check if an IDAC profile already exists - p = self.data.profile.get_profile(user_id, self.version) + p = await self.data.profile.get_profile(user_id, self.version) is_new_player = 1 if p is None else 0 else: lock_result = 0 @@ -474,35 +474,35 @@ class IDACSeason2(IDACBase): "server_status": 1, } - def handle_login_unlock_request(self, data: Dict, headers: Dict): + async def handle_login_unlock_request(self, data: Dict, headers: Dict): return { "status_code": "0", "lock_result": 1, } - def handle_login_relock_request(self, data: Dict, headers: Dict): + async def handle_login_relock_request(self, data: Dict, headers: Dict): return { "status_code": "0", "lock_result": 1, "lock_date": int(datetime.now().timestamp()), } - def handle_login_guestplay_request(self, data: Dict, headers: Dict): + async def handle_login_guestplay_request(self, data: Dict, headers: Dict): # TODO pass - def _generate_story_data(self, user_id: int) -> Dict: - stories = self.data.item.get_stories(user_id) + async def _generate_story_data(self, user_id: int) -> Dict: + stories = await self.data.item.get_stories(user_id) story_data = [] for s in stories: chapter_id = s["chapter"] - episodes = self.data.item.get_story_episodes(user_id, chapter_id) + episodes = await self.data.item.get_story_episodes(user_id, chapter_id) episode_data = [] for e in episodes: episode_id = e["episode"] - difficulties = self.data.item.get_story_episode_difficulties( + difficulties = await self.data.item.get_story_episode_difficulties( user_id, episode_id ) @@ -537,9 +537,9 @@ class IDACSeason2(IDACBase): return story_data - def _generate_special_data(self, user_id: int) -> Dict: + async def _generate_special_data(self, user_id: int) -> Dict: # 4 = special mode - specials = self.data.item.get_best_challenges_by_vs_type(user_id, story_type=4) + specials = await self.data.item.get_best_challenges_by_vs_type(user_id, story_type=4) special_data = [] for s in specials: @@ -556,9 +556,9 @@ class IDACSeason2(IDACBase): return special_data - def _generate_challenge_data(self, user_id: int) -> Dict: + async def _generate_challenge_data(self, user_id: int) -> Dict: # challenge mode (Bunta challenge only right now) - challenges = self.data.item.get_best_challenges_by_vs_type( + challenges = await self.data.item.get_best_challenges_by_vs_type( user_id, story_type=3 ) @@ -578,24 +578,24 @@ class IDACSeason2(IDACBase): return challenge_data - def _save_stock_data(self, user_id: int, stock_data: Dict): + async def _save_stock_data(self, user_id: int, stock_data: Dict): updated_stock_data = {} for k, v in stock_data.items(): if v != "": updated_stock_data[k] = v if updated_stock_data: - self.data.profile.put_profile_stock( + await self.data.profile.put_profile_stock( user_id, self.version, updated_stock_data ) - def handle_user_getdata_request(self, data: Dict, headers: Dict): + async def handle_user_getdata_request(self, data: Dict, headers: Dict): user_id = int(headers["session"]) # get the user's profile, can never be None - p = self.data.profile.get_profile(user_id, self.version) + p = await self.data.profile.get_profile(user_id, self.version) user_data = p._asdict() - arcade = self.data.arcade.get_arcade(user_data["store"]) + arcade = await self.data.arcade.get_arcade(user_data["store"]) del user_data["id"] del user_data["user"] @@ -608,7 +608,7 @@ class IDACSeason2(IDACBase): user_data["create_date"] = int(user_data["create_date"].timestamp()) # get the user's rank - r = self.data.profile.get_profile_rank(user_id, self.version) + r = await self.data.profile.get_profile_rank(user_id, self.version) rank_data = r._asdict() del rank_data["id"] del rank_data["user"] @@ -618,27 +618,27 @@ class IDACSeason2(IDACBase): user_data["mode_rank_data"] = rank_data # get the user's avatar - a = self.data.profile.get_profile_avatar(user_id) + a = await self.data.profile.get_profile_avatar(user_id) avatar_data = a._asdict() del avatar_data["id"] del avatar_data["user"] # get the user's stock - s = self.data.profile.get_profile_stock(user_id, self.version) + s = await self.data.profile.get_profile_stock(user_id, self.version) stock_data = s._asdict() del stock_data["id"] del stock_data["user"] del stock_data["version"] # get the user's config - c = self.data.profile.get_profile_config(user_id) + c = await self.data.profile.get_profile_config(user_id) config_data = c._asdict() del config_data["id"] del config_data["user"] config_data["id"] = config_data.pop("config_id") # get the user's ticket - tickets: list = self.data.item.get_tickets(user_id) + tickets: list = await self.data.item.get_tickets(user_id) """ ticket_id: @@ -658,7 +658,7 @@ class IDACSeason2(IDACBase): ) # get the user's course, required for the "course proeficiency" - courses = self.data.item.get_courses(user_id) + courses = await self.data.item.get_courses(user_id) course_data = [] for course in courses: course_data.append( @@ -673,7 +673,7 @@ class IDACSeason2(IDACBase): # get the profile theory data theory_data = {} - theory = self.data.profile.get_profile_theory(user_id, self.version) + theory = await self.data.profile.get_profile_theory(user_id, self.version) if theory is not None: theory_data = theory._asdict() del theory_data["id"] @@ -682,7 +682,7 @@ class IDACSeason2(IDACBase): # get the users theory course data theory_course_data = [] - theory_courses = self.data.item.get_theory_courses(user_id) + theory_courses = await self.data.item.get_theory_courses(user_id) for course in theory_courses: tmp = course._asdict() del tmp["id"] @@ -693,7 +693,7 @@ class IDACSeason2(IDACBase): # get the users theory partner data theory_partner_data = [] - theory_partners = self.data.item.get_theory_partners(user_id) + theory_partners = await self.data.item.get_theory_partners(user_id) for partner in theory_partners: tmp = partner._asdict() del tmp["id"] @@ -703,7 +703,7 @@ class IDACSeason2(IDACBase): # get the users theory running pram data theory_running_pram_data = [] - theory_running = self.data.item.get_theory_running(user_id) + theory_running = await self.data.item.get_theory_running(user_id) for running in theory_running: tmp = running._asdict() del tmp["id"] @@ -713,7 +713,7 @@ class IDACSeason2(IDACBase): # get the users vs info data vs_info_data = [] - vs_info = self.data.item.get_vs_infos(user_id) + vs_info = await self.data.item.get_vs_infos(user_id) for vs in vs_info: vs_info_data.append( { @@ -737,7 +737,7 @@ class IDACSeason2(IDACBase): ) # get the user's car - cars = self.data.item.get_cars(self.version, user_id, only_pickup=True) + cars = await self.data.item.get_cars(self.version, user_id, only_pickup=True) fulltune_count = 0 total_car_parts_count = 0 car_data = [] @@ -761,7 +761,7 @@ class IDACSeason2(IDACBase): user_data["have_car_cnt"] = len(car_data) # get the user's play stamps - stamps = self.data.item.get_stamps(user_id) + stamps = await self.data.item.get_stamps(user_id) stamp_event_data = [] for stamp in stamps: tmp = stamp._asdict() @@ -790,7 +790,7 @@ class IDACSeason2(IDACBase): tmp["weekly_bonus"] = 0 # update the play stamp in the database - self.data.item.put_stamp(user_id, tmp) + await self.data.item.put_stamp(user_id, tmp) del tmp["create_date_daily"] del tmp["create_date_weekly"] @@ -798,7 +798,7 @@ class IDACSeason2(IDACBase): # get the user's timetrial event data timetrial_event_data = {} - timetrial = self.data.item.get_timetrial_event(user_id, self.timetrial_event_id) + timetrial = await self.data.item.get_timetrial_event(user_id, self.timetrial_event_id) if timetrial is not None: timetrial_event_data = { "timetrial_event_id": timetrial["timetrial_event_id"], @@ -810,7 +810,7 @@ class IDACSeason2(IDACBase): "user_base_data": user_data, "avatar_data": avatar_data, "pick_up_car_data": car_data, - "story_data": self._generate_story_data(user_id), + "story_data": await self._generate_story_data(user_id), "vsinfo_data": vs_info_data, "stock_data": stock_data, "mission_data": { @@ -878,21 +878,21 @@ class IDACSeason2(IDACBase): "theory_course_data": theory_course_data, "theory_partner_data": theory_partner_data, "theory_running_pram_data": theory_running_pram_data, - "special_mode_data": self._generate_special_data(user_id), - "challenge_mode_data": self._generate_challenge_data(user_id), + "special_mode_data": await self._generate_special_data(user_id), + "challenge_mode_data": await self._generate_challenge_data(user_id), "season_rewards_data": [], "timetrial_event_data": timetrial_event_data, "special_mode_hint_data": {"story_type": 0, "hint_display_flag": 0}, } - def handle_timetrial_getbestrecordpreta_request(self, data: Dict, headers: Dict): + async def handle_timetrial_getbestrecordpreta_request(self, data: Dict, headers: Dict): user_id = headers["session"] for car_id in data["car_ids"]: pass course_mybest_data = [] - courses = self.data.item.get_time_trial_user_best_courses(self.version, user_id) + courses = await self.data.item.get_time_trial_user_best_courses(self.version, user_id) for course in courses: course_mybest_data.append( { @@ -920,10 +920,10 @@ class IDACSeason2(IDACBase): ) course_pickup_car_best_data = [] - courses = self.data.item.get_time_trial_courses(self.version) + courses = await self.data.item.get_time_trial_courses(self.version) for course in courses: car_list = [] - best_cars = self.data.item.get_time_trial_best_cars_by_course( + best_cars = await self.data.item.get_time_trial_best_cars_by_course( self.version, course["course_id"], user_id ) @@ -960,7 +960,7 @@ class IDACSeason2(IDACBase): "course_pickup_car_best_data": course_pickup_car_best_data, } - def handle_timetrial_getbestrecordprerace_request(self, data: Dict, headers: Dict): + async def handle_timetrial_getbestrecordprerace_request(self, data: Dict, headers: Dict): user_id = headers["session"] course_id = data["course_id"] @@ -969,7 +969,7 @@ class IDACSeason2(IDACBase): style_car_id = car["style_car_id"] # Not sure if this is actually correct - ranking = self.data.item.get_time_trial_ranking_by_course( + ranking = await self.data.item.get_time_trial_ranking_by_course( self.version, course_id ) course_best_data = [] @@ -977,8 +977,8 @@ class IDACSeason2(IDACBase): car_user_id = rank["user"] # get the username, country and store from the profile - profile = self.data.profile.get_profile(car_user_id, self.version) - arcade = self.data.arcade.get_arcade(profile["store"]) + profile = await self.data.profile.get_profile(car_user_id, self.version) + arcade = await self.data.arcade.get_arcade(profile["store"]) if arcade is None: arcade = {} @@ -1007,7 +1007,7 @@ class IDACSeason2(IDACBase): } ) - best_cars = self.data.item.get_time_trial_best_cars_by_course( + best_cars = await self.data.item.get_time_trial_best_cars_by_course( self.version, course_id ) @@ -1015,8 +1015,8 @@ class IDACSeason2(IDACBase): for i, rank in enumerate(best_cars): car_user_id = rank["user"] # get the username, country and store from the profile - profile = self.data.profile.get_profile(car_user_id, self.version) - arcade = self.data.arcade.get_arcade(profile["store"]) + profile = await self.data.profile.get_profile(car_user_id, self.version) + arcade = await self.data.arcade.get_arcade(profile["store"]) if arcade is None: arcade = {} @@ -1051,7 +1051,7 @@ class IDACSeason2(IDACBase): "course_best_data": course_best_data, } - def handle_user_createaccount_request(self, data: Dict, headers: Dict): + async def handle_user_createaccount_request(self, data: Dict, headers: Dict): user_id = headers["session"] car_data: Dict = data.pop("car_obj") @@ -1070,45 +1070,45 @@ class IDACSeason2(IDACBase): data["store"] = headers.get("a_store", 0) data["country"] = headers.get("a_country", 0) data["asset_version"] = headers.get("asset_version", 1) - self.data.profile.put_profile(user_id, self.version, data) + await self.data.profile.put_profile(user_id, self.version, data) # save rank data in database - self.data.profile.put_profile_rank(user_id, self.version, rank_data) + await self.data.profile.put_profile_rank(user_id, self.version, rank_data) # save stock data in database - self._save_stock_data(user_id, stock_data) + await self._save_stock_data(user_id, stock_data) # save tickets in database for ticket in takeover_ticket_list: - self.data.item.put_ticket(user_id, ticket) + await self.data.item.put_ticket(user_id, ticket) config_data["config_id"] = config_data.pop("id") - self.data.profile.put_profile_config(user_id, config_data) - self.data.profile.put_profile_avatar(user_id, avatar_data) + await self.data.profile.put_profile_config(user_id, config_data) + await self.data.profile.put_profile_avatar(user_id, avatar_data) # save car data and car parts in database car_data["parts_list"] = parts_data - self.data.item.put_car(user_id, self.version, car_data) + await self.data.item.put_car(user_id, self.version, car_data) return {"status_code": "0"} - def handle_user_updatelogin_request(self, data: Dict, headers: Dict): + async def handle_user_updatelogin_request(self, data: Dict, headers: Dict): pass - def handle_timetrial_getcarbest_request(self, data: Dict, headers: Dict): + async def handle_timetrial_getcarbest_request(self, data: Dict, headers: Dict): pass - def handle_factory_avatargacharesult_request(self, data: Dict, headers: Dict): + async def handle_factory_avatargacharesult_request(self, data: Dict, headers: Dict): user_id = headers["session"] stock_data: Dict = data.pop("stock_obj") use_ticket_cnt = data["use_ticket_cnt"] # save stock data in database - self._save_stock_data(user_id, stock_data) + await self._save_stock_data(user_id, stock_data) # get the user's ticket - tickets: list = self.data.item.get_tickets(user_id) + tickets: list = await self.data.item.get_tickets(user_id) ticket_list = [] for ticket in tickets: # avatar tickets @@ -1119,7 +1119,7 @@ class IDACSeason2(IDACBase): } # update the ticket in the database - self.data.item.put_ticket(user_id, ticket_data) + await self.data.item.put_ticket(user_id, ticket_data) ticket_list.append(ticket_data) continue @@ -1133,15 +1133,15 @@ class IDACSeason2(IDACBase): return {"status_code": "0", "ticket_data": ticket_list} - def handle_factory_savefavoritecar_request(self, data: Dict, headers: Dict): + async def handle_factory_savefavoritecar_request(self, data: Dict, headers: Dict): user_id = headers["session"] # save favorite cars in database for car in data["pickup_on_car_ids"]: - self.data.item.put_car(user_id, self.version, car) + await self.data.item.put_car(user_id, self.version, car) for car in data["pickup_off_car_ids"]: - self.data.item.put_car( + await self.data.item.put_car( user_id, self.version, {"style_car_id": car["style_car_id"], "pickup_seq": 0}, @@ -1149,7 +1149,7 @@ class IDACSeason2(IDACBase): return {"status_code": "0"} - def handle_factory_updatemultiplecustomizeresult_request( + async def handle_factory_updatemultiplecustomizeresult_request( self, data: Dict, headers: Dict ): user_id = headers["session"] @@ -1162,15 +1162,15 @@ class IDACSeason2(IDACBase): # save tickets in database for ticket in ticket_data: - self.data.item.put_ticket(user_id, ticket) + await self.data.item.put_ticket(user_id, ticket) for car in car_list: # save car data and car parts in database - self.data.item.put_car(user_id, self.version, car) + await self.data.item.put_car(user_id, self.version, car) return {"status_code": "0"} - def handle_factory_updatecustomizeresult_request(self, data: Dict, headers: Dict): + async def handle_factory_updatecustomizeresult_request(self, data: Dict, headers: Dict): user_id = headers["session"] parts_data: List = data.pop("parts_list") @@ -1178,18 +1178,18 @@ class IDACSeason2(IDACBase): # save tickets in database for ticket in ticket_data: - self.data.item.put_ticket(user_id, ticket) + await self.data.item.put_ticket(user_id, ticket) # save car data in database data["parts_list"] = parts_data - self.data.item.put_car(user_id, self.version, data) + await self.data.item.put_car(user_id, self.version, data) return {"status_code": "0"} - def handle_factory_getcardata_request(self, data: Dict, headers: Dict): + async def handle_factory_getcardata_request(self, data: Dict, headers: Dict): user_id = headers["session"] - cars = self.data.item.get_cars(self.version, user_id) + cars = await self.data.item.get_cars(self.version, user_id) car_data = [] for car in cars: tmp = car._asdict() @@ -1204,10 +1204,10 @@ class IDACSeason2(IDACBase): "car_data": car_data, } - def handle_factory_renamebefore_request(self, data: Dict, headers: Dict): + async def handle_factory_renamebefore_request(self, data: Dict, headers: Dict): pass - def handle_factory_buycarresult_request(self, data: Dict, headers: Dict): + async def handle_factory_buycarresult_request(self, data: Dict, headers: Dict): user_id = headers["session"] parts_data: List = data.pop("parts_list") @@ -1224,7 +1224,7 @@ class IDACSeason2(IDACBase): if car["style_car_id"] == style_car_id: pickup_seq = car["pickup_seq"] else: - self.data.item.put_car(user_id, self.version, car) + await self.data.item.put_car(user_id, self.version, car) data["pickup_seq"] = pickup_seq @@ -1232,7 +1232,7 @@ class IDACSeason2(IDACBase): total_cash = data.pop("total_cash") # save the new cash in database - self.data.profile.put_profile( + await self.data.profile.put_profile( user_id, self.version, {"total_cash": total_cash, "cash": cash} ) @@ -1240,10 +1240,10 @@ class IDACSeason2(IDACBase): use_ticket = data.pop("use_ticket") if use_ticket: # get the user's tickets, full tune ticket id is 25 - ticket = self.data.item.get_ticket(user_id, ticket_id=25) + ticket = await self.data.item.get_ticket(user_id, ticket_id=25) # update the ticket in the database - self.data.item.put_ticket( + await self.data.item.put_ticket( user_id, { "ticket_id": ticket["ticket_id"], @@ -1256,17 +1256,17 @@ class IDACSeason2(IDACBase): # save car data and car parts in database data["parts_list"] = parts_data - self.data.item.put_car(user_id, self.version, data) + await self.data.item.put_car(user_id, self.version, data) for car in pickup_off_list: - self.data.item.put_car( + await self.data.item.put_car( user_id, self.version, {"style_car_id": car["style_car_id"], "pickup_seq": 0}, ) # get the user's car - cars = self.data.item.get_cars(self.version, user_id) + cars = await self.data.item.get_cars(self.version, user_id) fulltune_count = 0 total_car_parts_count = 0 for car in cars: @@ -1278,7 +1278,7 @@ class IDACSeason2(IDACBase): # total_car_parts_count += car["total_car_parts_count"] # get the user's ticket - tickets = self.data.item.get_tickets(user_id) + tickets = await self.data.item.get_tickets(user_id) ticket_data = [] for ticket in tickets: ticket_data.append( @@ -1297,54 +1297,54 @@ class IDACSeason2(IDACBase): "car_style_count": [], } - def handle_factory_renameresult_request(self, data: Dict, headers: Dict): + async def handle_factory_renameresult_request(self, data: Dict, headers: Dict): user_id = headers["session"] new_username = data.get("username") # save new username in database if new_username: - self.data.profile.put_profile(user_id, self.version, data) + await self.data.profile.put_profile(user_id, self.version, data) return {"status_code": "0"} - def handle_factory_updatecustomizeavatar_request(self, data: Dict, headers: Dict): + async def handle_factory_updatecustomizeavatar_request(self, data: Dict, headers: Dict): user_id = headers["session"] avatar_data: Dict = data.pop("avatar_obj") stock_data: Dict = data.pop("stock_obj") # update the stock data in database - self._save_stock_data(user_id, stock_data) + await self._save_stock_data(user_id, stock_data) # save avatar data and avatar parts in database - self.data.profile.put_profile_avatar(user_id, avatar_data) + await self.data.profile.put_profile_avatar(user_id, avatar_data) return {"status_code": "0"} - def handle_factory_updatecustomizeuser_request(self, data: Dict, headers: Dict): + async def handle_factory_updatecustomizeuser_request(self, data: Dict, headers: Dict): user_id = headers["session"] stock_data: Dict = data.pop("stock_obj") # update the stock data in database - self._save_stock_data(user_id, stock_data) + await self._save_stock_data(user_id, stock_data) # update profile data and config in database - self.data.profile.put_profile(user_id, self.version, data) + await self.data.profile.put_profile(user_id, self.version, data) return {"status_code": "0"} - def handle_user_updatestampinfo_request(self, data: Dict, headers: Dict): + async def handle_user_updatestampinfo_request(self, data: Dict, headers: Dict): user_id = headers["session"] stamp_event_data = data.pop("stamp_event_data") for stamp in stamp_event_data: - self.data.item.put_stamp(user_id, stamp) + await self.data.item.put_stamp(user_id, stamp) return {"status_code": "0"} - def handle_user_updatetimetrialresult_request(self, data: Dict, headers: Dict): + async def handle_user_updatetimetrialresult_request(self, data: Dict, headers: Dict): user_id = headers["session"] stock_data: Dict = data.pop("stock_obj") @@ -1357,22 +1357,22 @@ class IDACSeason2(IDACBase): event_point = data.pop("event_point") # save stock data in database - self._save_stock_data(user_id, stock_data) + await self._save_stock_data(user_id, stock_data) # save tickets in database for ticket in ticket_data: - self.data.item.put_ticket(user_id, ticket) + await self.data.item.put_ticket(user_id, ticket) # save mode rank data in database rank_data.update(reward_dist_data) - self.data.profile.put_profile_rank(user_id, self.version, rank_data) + await self.data.profile.put_profile_rank(user_id, self.version, rank_data) # get the profile data, update total_play and daily_play, and save it - profile = self.data.profile.get_profile(user_id, self.version) + profile = await self.data.profile.get_profile(user_id, self.version) total_play = profile["total_play"] + 1 # update profile - self.data.profile.put_profile( + await self.data.profile.put_profile( user_id, self.version, { @@ -1392,7 +1392,7 @@ class IDACSeason2(IDACBase): # get the use_count and story_use_count of the used car style_car_id = data.get("style_car_id") car_mileage = data.pop("car_mileage") - used_car = self.data.item.get_car(user_id, self.version, style_car_id)._asdict() + used_car = await self.data.item.get_car(user_id, self.version, style_car_id)._asdict() # increase the use_count and story_use_count of the used car used_car["use_count"] += 1 @@ -1400,7 +1400,7 @@ class IDACSeason2(IDACBase): used_car["car_mileage"] = car_mileage # save the used car in database - self.data.item.put_car(user_id, self.version, used_car) + await self.data.item.put_car(user_id, self.version, used_car) # skill_level_exp is the "course proeficiency" and is saved # in the course table @@ -1409,12 +1409,12 @@ class IDACSeason2(IDACBase): skill_level_exp = data.pop("skill_level_exp") # get the course data - course = self.data.item.get_course(user_id, course_id) + course = await self.data.item.get_course(user_id, course_id) if course: # update run_counts run_counts = course["run_counts"] + 1 - self.data.item.put_course( + await self.data.item.put_course( user_id, { "course_id": course_id, @@ -1426,12 +1426,12 @@ class IDACSeason2(IDACBase): goal_time = data.get("goal_time") # grab the ranking data and count the numbers of rows with a faster time # than the current goal_time - course_rank = self.data.item.get_time_trial_ranking_by_course( + course_rank = await self.data.item.get_time_trial_ranking_by_course( self.version, course_id, limit=None ) course_rank = len([r for r in course_rank if r["goal_time"] < goal_time]) + 1 - car_course_rank = self.data.item.get_time_trial_ranking_by_course( + car_course_rank = await self.data.item.get_time_trial_ranking_by_course( self.version, course_id, style_car_id, limit=None ) car_course_rank = ( @@ -1442,7 +1442,7 @@ class IDACSeason2(IDACBase): if data.get("goal_time") > 0: # get the current best goal time best_time_trial = ( - self.data.item.get_time_trial_user_best_time_by_course_car( + await self.data.item.get_time_trial_user_best_time_by_course_car( self.version, user_id, course_id, style_car_id ) ) @@ -1453,10 +1453,10 @@ class IDACSeason2(IDACBase): ): # now finally save the time trial with updated timestamp data["play_dt"] = datetime.now() - self.data.item.put_time_trial(self.version, user_id, data) + await self.data.item.put_time_trial(self.version, user_id, data) # update the timetrial event points - self.data.item.put_timetrial_event( + await self.data.item.put_timetrial_event( user_id, self.timetrial_event_id, event_point ) @@ -1473,7 +1473,7 @@ class IDACSeason2(IDACBase): }, } - def handle_user_updatestoryresult_request(self, data: Dict, headers: Dict): + async def handle_user_updatestoryresult_request(self, data: Dict, headers: Dict): user_id = headers["session"] stock_data: Dict = data.pop("stock_obj") @@ -1484,15 +1484,15 @@ class IDACSeason2(IDACBase): # stamp_event_data = data.pop("stamp_event_data") # save stock data in database - self._save_stock_data(user_id, stock_data) + await self._save_stock_data(user_id, stock_data) # save tickets in database for ticket in ticket_data: - self.data.item.put_ticket(user_id, ticket) + await self.data.item.put_ticket(user_id, ticket) # save mode rank data in database rank_data.update(reward_dist_data) - self.data.profile.put_profile_rank(user_id, self.version, rank_data) + await self.data.profile.put_profile_rank(user_id, self.version, rank_data) # save the current story progress in database max_loop = data.get("chapter_loop_max") @@ -1503,7 +1503,7 @@ class IDACSeason2(IDACBase): play_status = data.get("play_status") # get the current loop from the database - story_data = self.data.item.get_story(user_id, chapter_id) + story_data = await self.data.item.get_story(user_id, chapter_id) # 1 = active, 2+ = cleared? loop_count = 1 if story_data: @@ -1517,13 +1517,13 @@ class IDACSeason2(IDACBase): # if the episode has already been cleared, set the play_status to 2 # so it won't be set to unplayed (play_status = 1) - episode_data = self.data.item.get_story_episode(user_id, episode_id) + episode_data = await self.data.item.get_story_episode(user_id, episode_id) if episode_data: if play_status < episode_data["play_status"]: play_status = 2 # save the current episode progress in database - self.data.item.put_story_episode( + await self.data.item.put_story_episode( user_id, chapter_id, { @@ -1537,9 +1537,9 @@ class IDACSeason2(IDACBase): loop_count += 1 # for the current chapter set all episode play_status back to 1 - self.data.item.put_story_episode_play_status(user_id, chapter_id, 1) + await self.data.item.put_story_episode_play_status(user_id, chapter_id, 1) - self.data.item.put_story( + await self.data.item.put_story( user_id, { "story_type": data.get("story_type"), @@ -1549,7 +1549,7 @@ class IDACSeason2(IDACBase): ) # save the current episode difficulty progress in database - self.data.item.put_story_episode_difficulty( + await self.data.item.put_story_episode_difficulty( user_id, episode_id, { @@ -1564,7 +1564,7 @@ class IDACSeason2(IDACBase): # get the use_count and story_use_count of the used car style_car_id = data.get("style_car_id") car_mileage = data.get("car_mileage") - used_car = self.data.item.get_car(user_id, self.version, style_car_id)._asdict() + used_car = await self.data.item.get_car(user_id, self.version, style_car_id)._asdict() # increase the use_count and story_use_count of the used car used_car["use_count"] += 1 @@ -1572,14 +1572,14 @@ class IDACSeason2(IDACBase): used_car["car_mileage"] = car_mileage # save the used car in database - self.data.item.put_car(user_id, self.version, used_car) + await self.data.item.put_car(user_id, self.version, used_car) # get the profile data, update total_play and daily_play, and save it - profile = self.data.profile.get_profile(user_id, self.version) + profile = await self.data.profile.get_profile(user_id, self.version) total_play = profile["total_play"] + 1 # save user profile in database - self.data.profile.put_profile( + await self.data.profile.put_profile( user_id, self.version, { @@ -1598,12 +1598,12 @@ class IDACSeason2(IDACBase): return { "status_code": "0", - "story_data": self._generate_story_data(user_id), + "story_data": await self._generate_story_data(user_id), "car_use_count": [], "maker_use_count": [], } - def handle_user_updatespecialmoderesult_request(self, data: Dict, headers: Dict): + async def handle_user_updatespecialmoderesult_request(self, data: Dict, headers: Dict): user_id = headers["session"] stock_data: Dict = data.pop("stock_obj") @@ -1617,11 +1617,11 @@ class IDACSeason2(IDACBase): # get the vs use count from database and update it style_car_id = data.pop("style_car_id") - car_data = self.data.item.get_car(user_id, self.version, style_car_id) + car_data = await self.data.item.get_car(user_id, self.version, style_car_id) story_use_count = car_data["story_use_count"] + 1 # save car data in database - self.data.item.put_car( + await self.data.item.put_car( user_id, self.version, { @@ -1632,11 +1632,11 @@ class IDACSeason2(IDACBase): ) # get the profile data, update total_play and daily_play, and save it - profile = self.data.profile.get_profile(user_id, self.version) + profile = await self.data.profile.get_profile(user_id, self.version) total_play = profile["total_play"] + 1 # save user profile in database - self.data.profile.put_profile( + await self.data.profile.put_profile( user_id, self.version, { @@ -1654,18 +1654,18 @@ class IDACSeason2(IDACBase): ) # save stock data in database - self._save_stock_data(user_id, stock_data) + await self._save_stock_data(user_id, stock_data) # save ticket data in database for ticket in ticket_data: - self.data.item.put_ticket(user_id, ticket) + await self.data.item.put_ticket(user_id, ticket) # save mode_rank and reward_dist data in database rank_data.update(reward_dist_data) - self.data.profile.put_profile_rank(user_id, self.version, rank_data) + await self.data.profile.put_profile_rank(user_id, self.version, rank_data) # finally save the special mode with story_type=4 in database - self.data.item.put_challenge(user_id, data) + await self.data.item.put_challenge(user_id, data) return { "status_code": "0", @@ -1674,7 +1674,7 @@ class IDACSeason2(IDACBase): "maker_use_count": [], } - def handle_user_updatechallengemoderesult_request(self, data: Dict, headers: Dict): + async def handle_user_updatechallengemoderesult_request(self, data: Dict, headers: Dict): user_id = headers["session"] stock_data: Dict = data.pop("stock_obj") @@ -1685,11 +1685,11 @@ class IDACSeason2(IDACBase): # get the vs use count from database and update it style_car_id = data.get("style_car_id") - car_data = self.data.item.get_car(user_id, self.version, style_car_id) + car_data = await self.data.item.get_car(user_id, self.version, style_car_id) story_use_count = car_data["story_use_count"] + 1 # save car data in database - self.data.item.put_car( + await self.data.item.put_car( user_id, self.version, { @@ -1700,11 +1700,11 @@ class IDACSeason2(IDACBase): ) # get the profile data, update total_play and daily_play, and save it - profile = self.data.profile.get_profile(user_id, self.version) + profile = await self.data.profile.get_profile(user_id, self.version) total_play = profile["total_play"] + 1 # save user profile in database - self.data.profile.put_profile( + await self.data.profile.put_profile( user_id, self.version, { @@ -1722,18 +1722,18 @@ class IDACSeason2(IDACBase): ) # save stock data in database - self._save_stock_data(user_id, stock_data) + await self._save_stock_data(user_id, stock_data) # save ticket data in database for ticket in ticket_data: - self.data.item.put_ticket(user_id, ticket) + await self.data.item.put_ticket(user_id, ticket) # save mode_rank and reward_dist data in database rank_data.update(reward_dist_data) - self.data.profile.put_profile_rank(user_id, self.version, rank_data) + await self.data.profile.put_profile_rank(user_id, self.version, rank_data) # get the challenge mode data from database - challenge_data = self.data.item.get_challenge( + challenge_data = await self.data.item.get_challenge( user_id, data.get("vs_type"), data.get("play_difficulty") ) @@ -1743,7 +1743,7 @@ class IDACSeason2(IDACBase): data["play_count"] = play_count # finally save the challenge mode with story_type=3 in database - self.data.item.put_challenge(user_id, data) + await self.data.item.put_challenge(user_id, data) return { "status_code": "0", @@ -1752,11 +1752,11 @@ class IDACSeason2(IDACBase): "maker_use_count": [], } - def _generate_time_trial_data(self, season_id: int, user_id: int) -> List[Dict]: + async def _generate_time_trial_data(self, season_id: int, user_id: int) -> List[Dict]: # get the season time trial data from database timetrial_data = [] - courses = self.data.item.get_courses(user_id) + courses = await self.data.item.get_courses(user_id) if courses is None or len(courses) == 0: return {"status_code": "0", "timetrial_data": timetrial_data} @@ -1766,7 +1766,7 @@ class IDACSeason2(IDACBase): skill_level_exp = course["skill_level_exp"] # get the best time for the current course for the current user - best_trial = self.data.item.get_time_trial_best_ranking_by_course( + best_trial = await self.data.item.get_time_trial_best_ranking_by_course( season_id, user_id, course_id ) if not best_trial: @@ -1774,7 +1774,7 @@ class IDACSeason2(IDACBase): goal_time = best_trial["goal_time"] # get the rank for the current course - course_rank = self.data.item.get_time_trial_ranking_by_course( + course_rank = await self.data.item.get_time_trial_ranking_by_course( season_id, course_id, limit=None ) course_rank = ( @@ -1794,12 +1794,12 @@ class IDACSeason2(IDACBase): return timetrial_data - def handle_user_getpastseasontadata_request(self, data: Dict, headers: Dict): + async def handle_user_getpastseasontadata_request(self, data: Dict, headers: Dict): user_id = headers["session"] season_id = data.get("season_id") # so to get the season 1 data just subtract 1 from the season id - past_timetrial_data = self._generate_time_trial_data(season_id - 1, user_id) + past_timetrial_data = await self._generate_time_trial_data(season_id - 1, user_id) # TODO: get the current season timetrial data somehow, because after requesting # GetPastSeasonTAData the game will NOT request GetTAData?! @@ -1809,10 +1809,10 @@ class IDACSeason2(IDACBase): "past_season_timetrial_data": past_timetrial_data, } - def handle_user_gettadata_request(self, data: Dict, headers: Dict): + async def handle_user_gettadata_request(self, data: Dict, headers: Dict): user_id = headers["session"] - timetrial_data = self._generate_time_trial_data(self.version, user_id) + timetrial_data = await self._generate_time_trial_data(self.version, user_id) # TODO: get the past season timetrial data somehow, because after requesting # GetTAData the game will NOT request GetPastSeasonTAData?! @@ -1822,17 +1822,17 @@ class IDACSeason2(IDACBase): # "past_season_timetrial_data": timetrial_data, } - def handle_user_updatecartune_request(self, data: Dict, headers: Dict): + async def handle_user_updatecartune_request(self, data: Dict, headers: Dict): user_id = headers["session"] # full tune ticket use_ticket = data.pop("use_ticket") if use_ticket: # get the user's tickets, full tune ticket id is 25 - ticket = self.data.item.get_ticket(user_id, ticket_id=25) + ticket = await self.data.item.get_ticket(user_id, ticket_id=25) # update the ticket in the database - self.data.item.put_ticket( + await self.data.item.put_ticket( user_id, { "ticket_id": ticket["ticket_id"], @@ -1843,22 +1843,22 @@ class IDACSeason2(IDACBase): # also set the tune_level to 16 (fully tuned) data["tune_level"] = 16 - self.data.item.put_car(user_id, self.version, data) + await self.data.item.put_car(user_id, self.version, data) return { "status_code": "0", - "story_data": self._generate_story_data(user_id), + "story_data": await self._generate_story_data(user_id), "car_use_count": [], "maker_use_count": [], } - def handle_log_saveplaylog_request(self, data: Dict, headers: Dict): + async def handle_log_saveplaylog_request(self, data: Dict, headers: Dict): pass - def handle_log_saveendlog_request(self, data: Dict, headers: Dict): + async def handle_log_saveendlog_request(self, data: Dict, headers: Dict): pass - def handle_user_updatemoderesult_request(self, data: Dict, headers: Dict): + async def handle_user_updatemoderesult_request(self, data: Dict, headers: Dict): user_id = headers["session"] config_data: Dict = data.pop("config_obj") @@ -1872,30 +1872,30 @@ class IDACSeason2(IDACBase): tips_list = data.pop("tips_list") # save stock data in database - self._save_stock_data(user_id, stock_data) + await self._save_stock_data(user_id, stock_data) # save tickets in database for ticket in ticket_data: - self.data.item.put_ticket(user_id, ticket) + await self.data.item.put_ticket(user_id, ticket) # save rank dist data in database - self.data.profile.put_profile_rank(user_id, self.version, reward_dist_data) + await self.data.profile.put_profile_rank(user_id, self.version, reward_dist_data) # update profile data and config in database - self.data.profile.put_profile(user_id, self.version, data) + await self.data.profile.put_profile(user_id, self.version, data) config_data["config_id"] = config_data.pop("id") - self.data.profile.put_profile_config(user_id, config_data) + await self.data.profile.put_profile_config(user_id, config_data) return {"status_code": "0", "server_status": 1} - def _generate_theory_rival_data( + async def _generate_theory_rival_data( self, user_list: list, course_id: int, req_user_id: int ) -> list: rival_data = [] for user_id in user_list: # if not enough players are available just use the data from the req_user if user_id == -1: - profile = self.data.profile.get_profile(req_user_id, self.version) + profile = await self.data.profile.get_profile(req_user_id, self.version) profile = profile._asdict() # set the name to CPU profile["username"] = f"CPU" @@ -1908,9 +1908,9 @@ class IDACSeason2(IDACBase): profile["stamp_key_assign_3"] = 3 profile["mytitle_id"] = 0 else: - profile = self.data.profile.get_profile(user_id, self.version) + profile = await self.data.profile.get_profile(user_id, self.version) - rank = self.data.profile.get_profile_rank(profile["user"], self.version) + rank = await self.data.profile.get_profile_rank(profile["user"], self.version) avatars = [ { @@ -1978,21 +1978,21 @@ class IDACSeason2(IDACBase): if user_id == -1: # get a random avatar from the list and some random car from all users avatar = choice(avatars) - car = self.data.item.get_random_car(self.version) + car = await self.data.item.get_random_car(self.version) else: - avatar = self.data.profile.get_profile_avatar(profile["user"]) - car = self.data.item.get_random_user_car(profile["user"], self.version) + avatar = await self.data.profile.get_profile_avatar(profile["user"]) + car = await self.data.item.get_random_user_car(profile["user"], self.version) parts_list = [] for part in car["parts_list"]: parts_list.append(part["parts"]) - course = self.data.item.get_theory_course(profile["user"], course_id) + course = await self.data.item.get_theory_course(profile["user"], course_id) powerhose_lv = 0 if course: powerhose_lv = course["powerhouse_lv"] - theory_running = self.data.item.get_theory_running_by_course( + theory_running = await self.data.item.get_theory_running_by_course( profile["user"], course_id ) @@ -2011,13 +2011,13 @@ class IDACSeason2(IDACBase): # get the time trial ranking medal eval_id = 0 - time_trial = self.data.item.get_time_trial_best_ranking_by_course( + time_trial = await self.data.item.get_time_trial_best_ranking_by_course( self.version, profile["user"], course_id ) if time_trial: eval_id = time_trial["eval_id"] - arcade = self.data.arcade.get_arcade(profile["store"]) + arcade = await self.data.arcade.get_arcade(profile["store"]) if arcade is None: arcade = {} arcade["name"] = self.core_cfg.server.name @@ -2079,7 +2079,7 @@ class IDACSeason2(IDACBase): return rival_data - def handle_theory_matching_request(self, data: Dict, headers: Dict): + async def handle_theory_matching_request(self, data: Dict, headers: Dict): user_id = headers["session"] course_id = data.pop("course_id") @@ -2094,7 +2094,7 @@ class IDACSeason2(IDACBase): powerhose_lv = data.pop("powerhouse_lv") # get random profiles for auto match - profiles = self.data.profile.get_different_random_profiles( + profiles = await self.data.profile.get_different_random_profiles( user_id, self.version, count=count_auto_match ) @@ -2103,10 +2103,10 @@ class IDACSeason2(IDACBase): while len(user_list) < count_auto_match: user_list.append(-1) - auto_match = self._generate_theory_rival_data(user_list, course_id, user_id) + auto_match = await self._generate_theory_rival_data(user_list, course_id, user_id) # get profiles with the same powerhouse_lv for power match - theory_courses = self.data.item.get_theory_course_by_powerhouse_lv( + theory_courses = await self.data.item.get_theory_course_by_powerhouse_lv( user_id, course_id, powerhose_lv, count=count_power_match ) user_list = [course["user"] for course in theory_courses] @@ -2115,7 +2115,7 @@ class IDACSeason2(IDACBase): while len(user_list) < count_power_match: user_list.append(-1) - power_match = self._generate_theory_rival_data(user_list, course_id, user_id) + power_match = await self._generate_theory_rival_data(user_list, course_id, user_id) return { "status_code": "0", @@ -2126,7 +2126,7 @@ class IDACSeason2(IDACBase): }, } - def handle_user_updatetheoryresult_request(self, data: Dict, headers: Dict): + async def handle_user_updatetheoryresult_request(self, data: Dict, headers: Dict): user_id = headers["session"] stock_data: Dict = data.pop("stock_obj") @@ -2136,15 +2136,15 @@ class IDACSeason2(IDACBase): driver_debut_data: Dict = data.pop("driver_debut_obj") # save stock data in database - self._save_stock_data(user_id, stock_data) + await self._save_stock_data(user_id, stock_data) # save tickets in database for ticket in ticket_data: - self.data.item.put_ticket(user_id, ticket) + await self.data.item.put_ticket(user_id, ticket) # save rank dist data in database rank_data.update(reward_dist_data) - self.data.profile.put_profile_rank(user_id, self.version, rank_data) + await self.data.profile.put_profile_rank(user_id, self.version, rank_data) # save the profile theory data in database play_count = 1 @@ -2152,7 +2152,7 @@ class IDACSeason2(IDACBase): win_count = 0 win_count_multi = 0 - theory_data = self.data.profile.get_profile_theory(user_id, self.version) + theory_data = await self.data.profile.get_profile_theory(user_id, self.version) if theory_data: play_count = theory_data["play_count"] + 1 play_count_multi = theory_data["play_count_multi"] + 1 @@ -2170,7 +2170,7 @@ class IDACSeason2(IDACBase): win_count += 1 win_count_multi += 1 - self.data.profile.put_profile_theory( + await self.data.profile.put_profile_theory( user_id, self.version, { @@ -2190,7 +2190,7 @@ class IDACSeason2(IDACBase): ) # save theory course in database - self.data.item.put_theory_course( + await self.data.item.put_theory_course( user_id, { "course_id": data.get("course_id"), @@ -2205,7 +2205,7 @@ class IDACSeason2(IDACBase): ) # save the theory partner in database - self.data.item.put_theory_partner( + await self.data.item.put_theory_partner( user_id, { "partner_id": data.get("partner_id"), @@ -2215,7 +2215,7 @@ class IDACSeason2(IDACBase): ) # save the theory running in database? - self.data.item.put_theory_running( + await self.data.item.put_theory_running( user_id, { "course_id": data.get("course_id"), @@ -2230,7 +2230,7 @@ class IDACSeason2(IDACBase): # get the use_count and theory_use_count of the used car style_car_id = data.get("style_car_id") car_mileage = data.get("car_mileage") - used_car = self.data.item.get_car(user_id, self.version, style_car_id)._asdict() + used_car = await self.data.item.get_car(user_id, self.version, style_car_id)._asdict() # increase the use_count and theory_use_count of the used car used_car["use_count"] += 1 @@ -2238,14 +2238,14 @@ class IDACSeason2(IDACBase): used_car["car_mileage"] = car_mileage # save the used car in database - self.data.item.put_car(user_id, self.version, used_car) + await self.data.item.put_car(user_id, self.version, used_car) # get the profile data, update total_play and daily_play, and save it - profile = self.data.profile.get_profile(user_id, self.version) + profile = await self.data.profile.get_profile(user_id, self.version) total_play = profile["total_play"] + 1 # save the profile in database - self.data.profile.put_profile( + await self.data.profile.put_profile( user_id, self.version, { @@ -2273,16 +2273,16 @@ class IDACSeason2(IDACBase): "win_count_multi": win_count_multi, } - def handle_timetrial_getbestrecordprebattle_request( + async def handle_timetrial_getbestrecordprebattle_request( self, data: Dict, headers: Dict ): user_id = headers["session"] course_pickup_car_best_data = [] - courses = self.data.item.get_time_trial_courses(self.version) + courses = await self.data.item.get_time_trial_courses(self.version) for course in courses: car_list = [] - best_cars = self.data.item.get_time_trial_best_cars_by_course( + best_cars = await self.data.item.get_time_trial_best_cars_by_course( self.version, course["course_id"], user_id ) @@ -2318,36 +2318,36 @@ class IDACSeason2(IDACBase): "course_pickup_car_best_data": course_pickup_car_best_data, } - def handle_user_updateonlinebattle_request(self, data: Dict, headers: Dict): + async def handle_user_updateonlinebattle_request(self, data: Dict, headers: Dict): return { "status_code": "0", "bothwin_penalty": 1, } - def handle_user_updateonlinebattleresult_request(self, data: Dict, headers: Dict): + async def handle_user_updateonlinebattleresult_request(self, data: Dict, headers: Dict): user_id = headers["session"] stock_data: Dict = data.pop("stock_obj") # save stock data in database - self._save_stock_data(user_id, stock_data) + await self._save_stock_data(user_id, stock_data) ticket_data: List = data.pop("ticket_data") for ticket in ticket_data: - self.data.item.put_ticket(user_id, ticket) + await self.data.item.put_ticket(user_id, ticket) reward_dist_data: Dict = data.pop("reward_dist_obj") rank_data: Dict = data.pop("mode_rank_obj") # save rank dist data in database rank_data.update(reward_dist_data) - self.data.profile.put_profile_rank(user_id, self.version, rank_data) + await self.data.profile.put_profile_rank(user_id, self.version, rank_data) driver_debut_data = data.pop("driver_debut_obj") # get the use_count and net_vs_use_count of the used car style_car_id = data.get("style_car_id") car_mileage = data.pop("car_mileage") - used_car = self.data.item.get_car(user_id, self.version, style_car_id)._asdict() + used_car = await self.data.item.get_car(user_id, self.version, style_car_id)._asdict() # increase the use_count and net_vs_use_count of the used car used_car["use_count"] += 1 @@ -2355,14 +2355,14 @@ class IDACSeason2(IDACBase): used_car["car_mileage"] = car_mileage # save the used car in database - self.data.item.put_car(user_id, self.version, used_car) + await self.data.item.put_car(user_id, self.version, used_car) # get the profile data, update total_play and daily_play, and save it - profile = self.data.profile.get_profile(user_id, self.version) + profile = await self.data.profile.get_profile(user_id, self.version) total_play = profile["total_play"] + 1 # save the profile in database - self.data.profile.put_profile( + await self.data.profile.put_profile( user_id, self.version, { @@ -2379,7 +2379,7 @@ class IDACSeason2(IDACBase): }, ) - self.data.item.put_vs_info(user_id, data) + await self.data.item.put_vs_info(user_id, data) vs_info = { "battle_mode": 0, @@ -2416,7 +2416,7 @@ class IDACSeason2(IDACBase): "maker_use_count": [], } - def handle_user_updatestorebattleresult_request(self, data: Dict, headers: Dict): + async def handle_user_updatestorebattleresult_request(self, data: Dict, headers: Dict): user_id = headers["session"] stock_data: Dict = data.pop("stock_obj") @@ -2431,20 +2431,20 @@ class IDACSeason2(IDACBase): gift_id = data.pop("gift_id") # save stock data in database - self._save_stock_data(user_id, stock_data) + await self._save_stock_data(user_id, stock_data) # save tickets in database for ticket in ticket_data: - self.data.item.put_ticket(user_id, ticket) + await self.data.item.put_ticket(user_id, ticket) # save rank dist data in database rank_data.update(reward_dist_data) - self.data.profile.put_profile_rank(user_id, self.version, rank_data) + await self.data.profile.put_profile_rank(user_id, self.version, rank_data) # get the use_count and net_vs_use_count of the used car style_car_id = data.get("style_car_id") car_mileage = data.pop("car_mileage") - used_car = self.data.item.get_car(user_id, self.version, style_car_id)._asdict() + used_car = await self.data.item.get_car(user_id, self.version, style_car_id)._asdict() # increase the use_count and net_vs_use_count of the used car used_car["use_count"] += 1 @@ -2452,14 +2452,14 @@ class IDACSeason2(IDACBase): used_car["car_mileage"] = car_mileage # save the used car in database - self.data.item.put_car(user_id, self.version, used_car) + await self.data.item.put_car(user_id, self.version, used_car) # get the profile data, update total_play and daily_play, and save it - profile = self.data.profile.get_profile(user_id, self.version) + profile = await self.data.profile.get_profile(user_id, self.version) total_play = profile["total_play"] + 1 # save the profile in database - self.data.profile.put_profile( + await self.data.profile.put_profile( user_id, self.version, { @@ -2477,7 +2477,7 @@ class IDACSeason2(IDACBase): ) # save vs_info in database - self.data.item.put_vs_info(user_id, data) + await self.data.item.put_vs_info(user_id, data) vs_info = { "battle_mode": 0, diff --git a/titles/idz/userdb.py b/titles/idz/userdb.py index cd6ea9c..47a9102 100644 --- a/titles/idz/userdb.py +++ b/titles/idz/userdb.py @@ -81,7 +81,7 @@ class IDZUserDB: self.logger.debug("Connection closed") return - await self.dataReceived(data, reader, writer) + await self.data.Received(data, reader, writer) await writer.drain() except ConnectionResetError as e: diff --git a/titles/mai2/base.py b/titles/mai2/base.py index a9ddcab..5a5edb9 100644 --- a/titles/mai2/base.py +++ b/titles/mai2/base.py @@ -82,7 +82,7 @@ class Mai2Base: return {"length": 0, "gameTournamentInfoList": []} async def handle_get_game_event_api_request(self, data: Dict) -> Dict: - events = self.data.static.get_enabled_events(self.version) + events = await self.data.static.get_enabled_events(self.version) events_lst = [] if events is None or not events: self.logger.warning("No enabled events, did you run the reader?") @@ -112,7 +112,7 @@ class Mai2Base: return {"length": 0, "musicIdList": []} async def handle_get_game_charge_api_request(self, data: Dict) -> Dict: - game_charge_list = self.data.static.get_enabled_tickets(self.version, 1) + game_charge_list = await self.data.static.get_enabled_tickets(self.version, 1) if game_charge_list is None: return {"length": 0, "gameChargeList": []} @@ -143,8 +143,8 @@ class Mai2Base: return {"returnCode": 1, "apiName": "UpsertClientTestmodeApi"} async def handle_get_user_preview_api_request(self, data: Dict) -> Dict: - p = self.data.profile.get_profile_detail(data["userId"], self.version, False) - w = self.data.profile.get_web_option(data["userId"], self.version) + p = await self.data.profile.get_profile_detail(data["userId"], self.version, False) + w = await self.data.profile.get_web_option(data["userId"], self.version) if p is None or w is None: return {} # Register profile = p._asdict() @@ -170,15 +170,15 @@ class Mai2Base: } async def handle_user_login_api_request(self, data: Dict) -> Dict: - profile = self.data.profile.get_profile_detail(data["userId"], self.version) - consec = self.data.profile.get_consec_login(data["userId"], self.version) + profile = await self.data.profile.get_profile_detail(data["userId"], self.version) + consec = await self.data.profile.get_consec_login(data["userId"], self.version) if profile is not None: lastLoginDate = profile["lastLoginDate"] loginCt = profile["playCount"] if "regionId" in data: - self.data.profile.put_profile_region(data["userId"], data["regionId"]) + await self.data.profile.put_profile_region(data["userId"], data["regionId"]) else: loginCt = 0 lastLoginDate = "2017-12-05 07:00:00.0" @@ -193,11 +193,11 @@ class Mai2Base: if lastlogindate_ < today_midnight: consec_ct = consec['logins'] + 1 - self.data.profile.add_consec_login(data["userId"], self.version) + await self.data.profile.add_consec_login(data["userId"], self.version) elif lastlogindate_ < yesterday_midnight: consec_ct = 1 - self.data.profile.reset_consec_login(data["userId"], self.version) + await self.data.profile.reset_consec_login(data["userId"], self.version) else: consec_ct = consec['logins'] @@ -214,7 +214,7 @@ class Mai2Base: user_id = data["userId"] playlog = data["userPlaylog"] - self.data.score.put_playlog(user_id, playlog) + await self.data.score.put_playlog(user_id, playlog) return {"returnCode": 1, "apiName": "UploadUserPlaylogApi"} @@ -224,7 +224,7 @@ class Mai2Base: # remove the ".0" from the date string, festival only? charge["purchaseDate"] = charge["purchaseDate"].replace(".0", "") - self.data.item.put_charge( + await self.data.item.put_charge( user_id, charge["chargeId"], charge["stock"], @@ -246,64 +246,64 @@ class Mai2Base: upsert["userData"][0].pop("accessCode") upsert["userData"][0].pop("userId") - self.data.profile.put_profile_detail( + await self.data.profile.put_profile_detail( user_id, self.version, upsert["userData"][0], False ) if "userWebOption" in upsert and len(upsert["userWebOption"]) > 0: upsert["userWebOption"][0]["isNetMember"] = True - self.data.profile.put_web_option( + await self.data.profile.put_web_option( user_id, self.version, upsert["userWebOption"][0] ) if "userGradeStatusList" in upsert and len(upsert["userGradeStatusList"]) > 0: - self.data.profile.put_grade_status( + await self.data.profile.put_grade_status( user_id, upsert["userGradeStatusList"][0] ) if "userBossList" in upsert and len(upsert["userBossList"]) > 0: - self.data.profile.put_boss_list( + await self.data.profile.put_boss_list( user_id, upsert["userBossList"][0] ) if "userPlaylogList" in upsert and len(upsert["userPlaylogList"]) > 0: for playlog in upsert["userPlaylogList"]: - self.data.score.put_playlog( + await self.data.score.put_playlog( user_id, playlog, False ) if "userExtend" in upsert and len(upsert["userExtend"]) > 0: - self.data.profile.put_profile_extend( + await self.data.profile.put_profile_extend( user_id, self.version, upsert["userExtend"][0] ) if "userGhost" in upsert: for ghost in upsert["userGhost"]: - self.data.profile.put_profile_ghost(user_id, self.version, ghost) + await self.data.profile.put_profile_ghost(user_id, self.version, ghost) if "userRecentRatingList" in upsert: - self.data.profile.put_recent_rating(user_id, upsert["userRecentRatingList"]) + await self.data.profile.put_recent_rating(user_id, upsert["userRecentRatingList"]) if "userOption" in upsert and len(upsert["userOption"]) > 0: upsert["userOption"][0].pop("userId") - self.data.profile.put_profile_option( + await self.data.profile.put_profile_option( user_id, self.version, upsert["userOption"][0], False ) if "userRatingList" in upsert and len(upsert["userRatingList"]) > 0: - self.data.profile.put_profile_rating( + await self.data.profile.put_profile_rating( user_id, self.version, upsert["userRatingList"][0] ) if "userActivityList" in upsert and len(upsert["userActivityList"]) > 0: for act in upsert["userActivityList"]: - self.data.profile.put_profile_activity(user_id, act) + await self.data.profile.put_profile_activity(user_id, act) if "userChargeList" in upsert and len(upsert["userChargeList"]) > 0: for charge in upsert["userChargeList"]: # remove the ".0" from the date string, festival only? charge["purchaseDate"] = charge["purchaseDate"].replace(".0", "") - self.data.item.put_charge( + await self.data.item.put_charge( user_id, charge["chargeId"], charge["stock"], @@ -313,14 +313,14 @@ class Mai2Base: if "userCharacterList" in upsert and len(upsert["userCharacterList"]) > 0: for char in upsert["userCharacterList"]: - self.data.item.put_character_( + await self.data.item.put_character_( user_id, char ) if "userItemList" in upsert and len(upsert["userItemList"]) > 0: for item in upsert["userItemList"]: - self.data.item.put_item( + await self.data.item.put_item( user_id, int(item["itemKind"]), item["itemId"], @@ -330,7 +330,7 @@ class Mai2Base: if "userLoginBonusList" in upsert and len(upsert["userLoginBonusList"]) > 0: for login_bonus in upsert["userLoginBonusList"]: - self.data.item.put_login_bonus( + await self.data.item.put_login_bonus( user_id, login_bonus["bonusId"], login_bonus["point"], @@ -340,7 +340,7 @@ class Mai2Base: if "userMapList" in upsert and len(upsert["userMapList"]) > 0: for map in upsert["userMapList"]: - self.data.item.put_map( + await self.data.item.put_map( user_id, map["mapId"], map["distance"], @@ -351,15 +351,15 @@ class Mai2Base: if "userMusicDetailList" in upsert and len(upsert["userMusicDetailList"]) > 0: for music in upsert["userMusicDetailList"]: - self.data.score.put_best_score(user_id, music, False) + await self.data.score.put_best_score(user_id, music, False) if "userCourseList" in upsert and len(upsert["userCourseList"]) > 0: for course in upsert["userCourseList"]: - self.data.score.put_course(user_id, course) + await self.data.score.put_course(user_id, course) if "userFavoriteList" in upsert and len(upsert["userFavoriteList"]) > 0: for fav in upsert["userFavoriteList"]: - self.data.item.put_favorite(user_id, fav["kind"], fav["itemIdList"]) + await self.data.item.put_favorite(user_id, fav["kind"], fav["itemIdList"]) if ( "userFriendSeasonRankingList" in upsert @@ -371,7 +371,7 @@ class Mai2Base: fsr["recordDate"], f"{Mai2Constants.DATE_TIME_FORMAT}.0" ), ) - self.data.item.put_friend_season_ranking(user_id, fsr) + await self.data.item.put_friend_season_ranking(user_id, fsr) return {"returnCode": 1, "apiName": "UpsertUserAllApi"} @@ -379,7 +379,7 @@ class Mai2Base: return {"returnCode": 1} async def handle_get_user_data_api_request(self, data: Dict) -> Dict: - profile = self.data.profile.get_profile_detail(data["userId"], self.version, False) + profile = await self.data.profile.get_profile_detail(data["userId"], self.version, False) if profile is None: return @@ -391,7 +391,7 @@ class Mai2Base: return {"userId": data["userId"], "userData": profile_dict} async def handle_get_user_extend_api_request(self, data: Dict) -> Dict: - extend = self.data.profile.get_profile_extend(data["userId"], self.version) + extend = await self.data.profile.get_profile_extend(data["userId"], self.version) if extend is None: return @@ -403,7 +403,7 @@ class Mai2Base: return {"userId": data["userId"], "userExtend": extend_dict} async def handle_get_user_option_api_request(self, data: Dict) -> Dict: - options = self.data.profile.get_profile_option(data["userId"], self.version, False) + options = await self.data.profile.get_profile_option(data["userId"], self.version, False) if options is None: return @@ -415,7 +415,7 @@ class Mai2Base: return {"userId": data["userId"], "userOption": options_dict} async def handle_get_user_card_api_request(self, data: Dict) -> Dict: - user_cards = self.data.item.get_cards(data["userId"]) + user_cards = await self.data.item.get_cards(data["userId"]) if user_cards is None: return {"userId": data["userId"], "nextIndex": 0, "userCardList": []} @@ -449,7 +449,7 @@ class Mai2Base: } async def handle_get_user_charge_api_request(self, data: Dict) -> Dict: - user_charges = self.data.item.get_charges(data["userId"]) + user_charges = await self.data.item.get_charges(data["userId"]) if user_charges is None: return {"userId": data["userId"], "length": 0, "userChargeList": []} @@ -477,7 +477,7 @@ class Mai2Base: return { "userId": data.get("userId", 0), "length": 0, "userPresentEventList": []} async def handle_get_user_boss_api_request(self, data: Dict) -> Dict: - b = self.data.profile.get_boss_list(data["userId"]) + b = await self.data.profile.get_boss_list(data["userId"]) if b is None: return { "userId": data.get("userId", 0), "userBossData": {}} boss_lst = b._asdict() @@ -489,7 +489,7 @@ class Mai2Base: async def handle_get_user_item_api_request(self, data: Dict) -> Dict: kind = int(data["nextIndex"] / 10000000000) next_idx = int(data["nextIndex"] % 10000000000) - user_item_list = self.data.item.get_items(data["userId"], kind) + user_item_list = await self.data.item.get_items(data["userId"], kind) items: List[Dict[str, Any]] = [] for i in range(next_idx, len(user_item_list)): @@ -515,7 +515,7 @@ class Mai2Base: } async def handle_get_user_character_api_request(self, data: Dict) -> Dict: - characters = self.data.item.get_characters(data["userId"]) + characters = await self.data.item.get_characters(data["userId"]) chara_list = [] for chara in characters: @@ -529,7 +529,7 @@ class Mai2Base: return {"userId": data["userId"], "userCharacterList": chara_list} async def handle_get_user_favorite_api_request(self, data: Dict) -> Dict: - favorites = self.data.item.get_favorites(data["userId"], data["itemKind"]) + favorites = await self.data.item.get_favorites(data["userId"], data["itemKind"]) if favorites is None: return @@ -546,7 +546,7 @@ class Mai2Base: return {"userId": data["userId"], "userFavoriteData": userFavs} async def handle_get_user_ghost_api_request(self, data: Dict) -> Dict: - ghost = self.data.profile.get_profile_ghost(data["userId"], self.version) + ghost = await self.data.profile.get_profile_ghost(data["userId"], self.version) if ghost is None: return @@ -558,7 +558,7 @@ class Mai2Base: return {"userId": data["userId"], "userGhost": ghost_dict} async def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict: - rating = self.data.profile.get_recent_rating(data["userId"]) + rating = await self.data.profile.get_recent_rating(data["userId"]) if rating is None: return @@ -568,7 +568,7 @@ class Mai2Base: return {"userId": data["userId"], "length": len(lst), "userRecentRatingList": lst} async def handle_get_user_rating_api_request(self, data: Dict) -> Dict: - rating = self.data.profile.get_profile_rating(data["userId"], self.version) + rating = await self.data.profile.get_profile_rating(data["userId"], self.version) if rating is None: return @@ -583,8 +583,8 @@ class Mai2Base: """ kind 1 is playlist, kind 2 is music list """ - playlist = self.data.profile.get_profile_activity(data["userId"], 1) - musiclist = self.data.profile.get_profile_activity(data["userId"], 2) + playlist = await self.data.profile.get_profile_activity(data["userId"], 1) + musiclist = await self.data.profile.get_profile_activity(data["userId"], 2) if playlist is None or musiclist is None: return @@ -608,7 +608,7 @@ class Mai2Base: return {"userActivity": {"playList": plst, "musicList": mlst}} async def handle_get_user_course_api_request(self, data: Dict) -> Dict: - user_courses = self.data.score.get_courses(data["userId"]) + user_courses = await self.data.score.get_courses(data["userId"]) if user_courses is None: return {"userId": data["userId"], "nextIndex": 0, "userCourseList": []} @@ -626,7 +626,7 @@ class Mai2Base: return {"length": 0, "userPortraitList": []} async def handle_get_user_friend_season_ranking_api_request(self, data: Dict) -> Dict: - friend_season_ranking = self.data.item.get_friend_season_ranking(data["userId"]) + friend_season_ranking = await self.data.item.get_friend_season_ranking(data["userId"]) if friend_season_ranking is None: return { "userId": data["userId"], @@ -662,7 +662,7 @@ class Mai2Base: } async def handle_get_user_map_api_request(self, data: Dict) -> Dict: - maps = self.data.item.get_maps(data["userId"]) + maps = await self.data.item.get_maps(data["userId"]) if maps is None: return { "userId": data["userId"], @@ -695,7 +695,7 @@ class Mai2Base: } async def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict: - login_bonuses = self.data.item.get_login_bonuses(data["userId"]) + login_bonuses = await self.data.item.get_login_bonuses(data["userId"]) if login_bonuses is None: return { "userId": data["userId"], @@ -731,7 +731,7 @@ class Mai2Base: return {"userId": data["userId"], "length": 0, "userRegionList": []} async def handle_get_user_web_option_api_request(self, data: Dict) -> Dict: - w = self.data.profile.get_web_option(data["userId"], self.version) + w = await self.data.profile.get_web_option(data["userId"], self.version) if w is None: return {"userId": data["userId"], "userWebOption": {}} @@ -746,7 +746,7 @@ class Mai2Base: return {"userId": data["userId"], "length": 0, "userSurvivalList": []} async def handle_get_user_grade_api_request(self, data: Dict) -> Dict: - g = self.data.profile.get_grade_status(data["userId"]) + g = await self.data.profile.get_grade_status(data["userId"]) if g is None: return {"userId": data["userId"], "userGradeStatus": {}, "length": 0, "userGradeList": []} grade_stat = g._asdict() @@ -766,7 +766,7 @@ class Mai2Base: self.logger.warning("handle_get_user_music_api_request: Could not find userid in data, or userId is 0") return {} - songs = self.data.score.get_best_scores(user_id, is_dx=False) + songs = await self.data.score.get_best_scores(user_id, is_dx=False) if songs is None: self.logger.debug("handle_get_user_music_api_request: get_best_scores returned None!") return { diff --git a/titles/mai2/dx.py b/titles/mai2/dx.py index 80a3bc2..f978eeb 100644 --- a/titles/mai2/dx.py +++ b/titles/mai2/dx.py @@ -34,8 +34,8 @@ class Mai2DX(Mai2Base): } async def handle_get_user_preview_api_request(self, data: Dict) -> Dict: - p = self.data.profile.get_profile_detail(data["userId"], self.version) - o = self.data.profile.get_profile_option(data["userId"], self.version) + p = await self.data.profile.get_profile_detail(data["userId"], self.version) + o = await self.data.profile.get_profile_option(data["userId"], self.version) if p is None or o is None: return {} # Register profile = p._asdict() @@ -73,7 +73,7 @@ class Mai2DX(Mai2Base): user_id = data["userId"] playlog = data["userPlaylog"] - self.data.score.put_playlog(user_id, playlog) + await self.data.score.put_playlog(user_id, playlog) return {"returnCode": 1, "apiName": "UploadUserPlaylogApi"} @@ -83,7 +83,7 @@ class Mai2DX(Mai2Base): # remove the ".0" from the date string, festival only? charge["purchaseDate"] = charge["purchaseDate"].replace(".0", "") - self.data.item.put_charge( + await self.data.item.put_charge( user_id, charge["chargeId"], charge["stock"], @@ -104,39 +104,39 @@ class Mai2DX(Mai2Base): if "userData" in upsert and len(upsert["userData"]) > 0: upsert["userData"][0]["isNetMember"] = 1 upsert["userData"][0].pop("accessCode") - self.data.profile.put_profile_detail( + await self.data.profile.put_profile_detail( user_id, self.version, upsert["userData"][0] ) if "userExtend" in upsert and len(upsert["userExtend"]) > 0: - self.data.profile.put_profile_extend( + await self.data.profile.put_profile_extend( user_id, self.version, upsert["userExtend"][0] ) if "userGhost" in upsert: for ghost in upsert["userGhost"]: - self.data.profile.put_profile_ghost(user_id, self.version, ghost) + await self.data.profile.put_profile_ghost(user_id, self.version, ghost) if "userOption" in upsert and len(upsert["userOption"]) > 0: - self.data.profile.put_profile_option( + await self.data.profile.put_profile_option( user_id, self.version, upsert["userOption"][0] ) if "userRatingList" in upsert and len(upsert["userRatingList"]) > 0: - self.data.profile.put_profile_rating( + await self.data.profile.put_profile_rating( user_id, self.version, upsert["userRatingList"][0] ) if "userActivityList" in upsert and len(upsert["userActivityList"]) > 0: for k, v in upsert["userActivityList"][0].items(): for act in v: - self.data.profile.put_profile_activity(user_id, act) + await self.data.profile.put_profile_activity(user_id, act) if "userChargeList" in upsert and len(upsert["userChargeList"]) > 0: for charge in upsert["userChargeList"]: # remove the ".0" from the date string, festival only? charge["purchaseDate"] = charge["purchaseDate"].replace(".0", "") - self.data.item.put_charge( + await self.data.item.put_charge( user_id, charge["chargeId"], charge["stock"], @@ -150,7 +150,7 @@ class Mai2DX(Mai2Base): if "userCharacterList" in upsert and len(upsert["userCharacterList"]) > 0: for char in upsert["userCharacterList"]: - self.data.item.put_character( + await self.data.item.put_character( user_id, char["characterId"], char["level"], @@ -160,7 +160,7 @@ class Mai2DX(Mai2Base): if "userItemList" in upsert and len(upsert["userItemList"]) > 0: for item in upsert["userItemList"]: - self.data.item.put_item( + await self.data.item.put_item( user_id, int(item["itemKind"]), item["itemId"], @@ -170,7 +170,7 @@ class Mai2DX(Mai2Base): if "userLoginBonusList" in upsert and len(upsert["userLoginBonusList"]) > 0: for login_bonus in upsert["userLoginBonusList"]: - self.data.item.put_login_bonus( + await self.data.item.put_login_bonus( user_id, login_bonus["bonusId"], login_bonus["point"], @@ -180,7 +180,7 @@ class Mai2DX(Mai2Base): if "userMapList" in upsert and len(upsert["userMapList"]) > 0: for map in upsert["userMapList"]: - self.data.item.put_map( + await self.data.item.put_map( user_id, map["mapId"], map["distance"], @@ -191,15 +191,15 @@ class Mai2DX(Mai2Base): if "userMusicDetailList" in upsert and len(upsert["userMusicDetailList"]) > 0: for music in upsert["userMusicDetailList"]: - self.data.score.put_best_score(user_id, music) + await self.data.score.put_best_score(user_id, music) if "userCourseList" in upsert and len(upsert["userCourseList"]) > 0: for course in upsert["userCourseList"]: - self.data.score.put_course(user_id, course) + await self.data.score.put_course(user_id, course) if "userFavoriteList" in upsert and len(upsert["userFavoriteList"]) > 0: for fav in upsert["userFavoriteList"]: - self.data.item.put_favorite(user_id, fav["kind"], fav["itemIdList"]) + await self.data.item.put_favorite(user_id, fav["kind"], fav["itemIdList"]) if ( "userFriendSeasonRankingList" in upsert @@ -211,12 +211,12 @@ class Mai2DX(Mai2Base): fsr["recordDate"], f"{Mai2Constants.DATE_TIME_FORMAT}.0" ), ) - self.data.item.put_friend_season_ranking(user_id, fsr) + await self.data.item.put_friend_season_ranking(user_id, fsr) return {"returnCode": 1, "apiName": "UpsertUserAllApi"} async def handle_get_user_data_api_request(self, data: Dict) -> Dict: - profile = self.data.profile.get_profile_detail(data["userId"], self.version) + profile = await self.data.profile.get_profile_detail(data["userId"], self.version) if profile is None: return @@ -228,7 +228,7 @@ class Mai2DX(Mai2Base): return {"userId": data["userId"], "userData": profile_dict} async def handle_get_user_extend_api_request(self, data: Dict) -> Dict: - extend = self.data.profile.get_profile_extend(data["userId"], self.version) + extend = await self.data.profile.get_profile_extend(data["userId"], self.version) if extend is None: return @@ -240,7 +240,7 @@ class Mai2DX(Mai2Base): return {"userId": data["userId"], "userExtend": extend_dict} async def handle_get_user_option_api_request(self, data: Dict) -> Dict: - options = self.data.profile.get_profile_option(data["userId"], self.version) + options = await self.data.profile.get_profile_option(data["userId"], self.version) if options is None: return @@ -252,7 +252,7 @@ class Mai2DX(Mai2Base): return {"userId": data["userId"], "userOption": options_dict} async def handle_get_user_card_api_request(self, data: Dict) -> Dict: - user_cards = self.data.item.get_cards(data["userId"]) + user_cards = await self.data.item.get_cards(data["userId"]) if user_cards is None: return {"userId": data["userId"], "nextIndex": 0, "userCardList": []} @@ -286,7 +286,7 @@ class Mai2DX(Mai2Base): } async def handle_get_user_charge_api_request(self, data: Dict) -> Dict: - user_charges = self.data.item.get_charges(data["userId"]) + user_charges = await self.data.item.get_charges(data["userId"]) if user_charges is None: return {"userId": data["userId"], "length": 0, "userChargeList": []} @@ -313,7 +313,7 @@ class Mai2DX(Mai2Base): async def handle_get_user_item_api_request(self, data: Dict) -> Dict: kind = int(data["nextIndex"] / 10000000000) next_idx = int(data["nextIndex"] % 10000000000) - user_item_list = self.data.item.get_items(data["userId"], kind) + user_item_list = await self.data.item.get_items(data["userId"], kind) items: List[Dict[str, Any]] = [] for i in range(next_idx, len(user_item_list)): @@ -339,7 +339,7 @@ class Mai2DX(Mai2Base): } async def handle_get_user_character_api_request(self, data: Dict) -> Dict: - characters = self.data.item.get_characters(data["userId"]) + characters = await self.data.item.get_characters(data["userId"]) chara_list = [] for chara in characters: @@ -351,7 +351,7 @@ class Mai2DX(Mai2Base): return {"userId": data["userId"], "userCharacterList": chara_list} async def handle_get_user_favorite_api_request(self, data: Dict) -> Dict: - favorites = self.data.item.get_favorites(data["userId"], data["itemKind"]) + favorites = await self.data.item.get_favorites(data["userId"], data["itemKind"]) if favorites is None: return @@ -368,7 +368,7 @@ class Mai2DX(Mai2Base): return {"userId": data["userId"], "userFavoriteData": userFavs} async def handle_get_user_ghost_api_request(self, data: Dict) -> Dict: - ghost = self.data.profile.get_profile_ghost(data["userId"], self.version) + ghost = await self.data.profile.get_profile_ghost(data["userId"], self.version) if ghost is None: return @@ -380,7 +380,7 @@ class Mai2DX(Mai2Base): return {"userId": data["userId"], "userGhost": ghost_dict} async def handle_get_user_rating_api_request(self, data: Dict) -> Dict: - rating = self.data.profile.get_profile_rating(data["userId"], self.version) + rating = await self.data.profile.get_profile_rating(data["userId"], self.version) if rating is None: return @@ -395,8 +395,8 @@ class Mai2DX(Mai2Base): """ kind 1 is playlist, kind 2 is music list """ - playlist = self.data.profile.get_profile_activity(data["userId"], 1) - musiclist = self.data.profile.get_profile_activity(data["userId"], 2) + playlist = await self.data.profile.get_profile_activity(data["userId"], 1) + musiclist = await self.data.profile.get_profile_activity(data["userId"], 2) if playlist is None or musiclist is None: return @@ -420,7 +420,7 @@ class Mai2DX(Mai2Base): return {"userActivity": {"playList": plst, "musicList": mlst}} async def handle_get_user_course_api_request(self, data: Dict) -> Dict: - user_courses = self.data.score.get_courses(data["userId"]) + user_courses = await self.data.score.get_courses(data["userId"]) if user_courses is None: return {"userId": data["userId"], "nextIndex": 0, "userCourseList": []} @@ -438,7 +438,7 @@ class Mai2DX(Mai2Base): return {"length": 0, "userPortraitList": []} async def handle_get_user_friend_season_ranking_api_request(self, data: Dict) -> Dict: - friend_season_ranking = self.data.item.get_friend_season_ranking(data["userId"]) + friend_season_ranking = await self.data.item.get_friend_season_ranking(data["userId"]) if friend_season_ranking is None: return { "userId": data["userId"], @@ -474,7 +474,7 @@ class Mai2DX(Mai2Base): } async def handle_get_user_map_api_request(self, data: Dict) -> Dict: - maps = self.data.item.get_maps(data["userId"]) + maps = await self.data.item.get_maps(data["userId"]) if maps is None: return { "userId": data["userId"], @@ -507,7 +507,7 @@ class Mai2DX(Mai2Base): } async def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict: - login_bonuses = self.data.item.get_login_bonuses(data["userId"]) + login_bonuses = await self.data.item.get_login_bonuses(data["userId"]) if login_bonuses is None: return { "userId": data["userId"], @@ -588,7 +588,7 @@ class Mai2DX(Mai2Base): self.logger.warning("handle_get_user_music_api_request: Could not find userid in data, or userId is 0") return {} - songs = self.data.score.get_best_scores(user_id) + songs = await self.data.score.get_best_scores(user_id) if songs is None: self.logger.debug("handle_get_user_music_api_request: get_best_scores returned None!") return { diff --git a/titles/mai2/read.py b/titles/mai2/read.py index cc4f678..d9450ac 100644 --- a/titles/mai2/read.py +++ b/titles/mai2/read.py @@ -35,7 +35,7 @@ class Mai2Reader(BaseReader): self.logger.error(f"Invalid maimai DX version {version}") exit(1) - def read(self) -> None: + async def read(self) -> None: data_dirs = [] if self.version >= Mai2Constants.VER_MAIMAI_DX: if self.bin_dir is not None: @@ -46,10 +46,10 @@ class Mai2Reader(BaseReader): for dir in data_dirs: self.logger.info(f"Read from {dir}") - self.get_events(f"{dir}/event") - self.disable_events(f"{dir}/information", f"{dir}/scoreRanking") - self.read_music(f"{dir}/music") - self.read_tickets(f"{dir}/ticket") + await self.get_events(f"{dir}/event") + await self.disable_events(f"{dir}/information", f"{dir}/scoreRanking") + await self.read_music(f"{dir}/music") + await self.read_tickets(f"{dir}/ticket") else: if not os.path.exists(f"{self.bin_dir}/tables"): @@ -70,16 +70,16 @@ class Mai2Reader(BaseReader): txt_table = self.load_table_raw(f"{self.bin_dir}/tables", "mmtextout_jp.bin", key) score_table = self.load_table_raw(f"{self.bin_dir}/tables", "mmScore.bin", key) - self.read_old_events(evt_table) - self.read_old_music(score_table, txt_table) + await self.read_old_events(evt_table) + await self.read_old_music(score_table, txt_table) if self.opt_dir is not None: evt_table = self.load_table_raw(f"{self.opt_dir}/tables", "mmEvent.bin", key) txt_table = self.load_table_raw(f"{self.opt_dir}/tables", "mmtextout_jp.bin", key) score_table = self.load_table_raw(f"{self.opt_dir}/tables", "mmScore.bin", key) - self.read_old_events(evt_table) - self.read_old_music(score_table, txt_table) + await self.read_old_events(evt_table) + await self.read_old_music(score_table, txt_table) return @@ -179,7 +179,7 @@ class Mai2Reader(BaseReader): self.logger.warning("Failed load table content, skipping") return - def get_events(self, base_dir: str) -> None: + async def get_events(self, base_dir: str) -> None: self.logger.info(f"Reading events from {base_dir}...") for root, dirs, files in os.walk(base_dir): @@ -192,12 +192,12 @@ class Mai2Reader(BaseReader): id = int(troot.find("name").find("id").text) event_type = int(troot.find("infoType").text) - self.data.static.put_game_event( + await self.data.static.put_game_event( self.version, event_type, id, name ) self.logger.info(f"Added event {id}...") - def disable_events( + async def disable_events( self, base_information_dir: str, base_score_ranking_dir: str ) -> None: self.logger.info(f"Reading disabled events from {base_information_dir}...") @@ -210,7 +210,7 @@ class Mai2Reader(BaseReader): event_id = int(troot.find("name").find("id").text) - self.data.static.toggle_game_event( + await self.data.static.toggle_game_event( self.version, event_id, toggle=False ) self.logger.info(f"Disabled event {event_id}...") @@ -223,7 +223,7 @@ class Mai2Reader(BaseReader): event_id = int(troot.find("eventName").find("id").text) - self.data.static.toggle_game_event( + await self.data.static.toggle_game_event( self.version, event_id, toggle=False ) self.logger.info(f"Disabled event {event_id}...") @@ -252,10 +252,10 @@ class Mai2Reader(BaseReader): 22091518, 22091519, ]: - self.data.static.toggle_game_event(self.version, event_id, toggle=False) + await self.data.static.toggle_game_event(self.version, event_id, toggle=False) self.logger.info(f"Disabled event {event_id}...") - def read_music(self, base_dir: str) -> None: + async def read_music(self, base_dir: str) -> None: self.logger.info(f"Reading music from {base_dir}...") for root, dirs, files in os.walk(base_dir): @@ -285,7 +285,7 @@ class Mai2Reader(BaseReader): dif.find("notesDesigner").find("str").text ) - self.data.static.put_game_music( + await self.data.static.put_game_music( self.version, song_id, chart_id, @@ -302,7 +302,7 @@ class Mai2Reader(BaseReader): f"Added music id {song_id} chart {chart_id}" ) - def read_tickets(self, base_dir: str) -> None: + async def read_tickets(self, base_dir: str) -> None: self.logger.info(f"Reading tickets from {base_dir}...") for root, dirs, files in os.walk(base_dir): @@ -316,12 +316,12 @@ class Mai2Reader(BaseReader): ticket_type = int(troot.find("ticketKind").find("id").text) price = int(troot.find("creditNum").text) - self.data.static.put_game_ticket( + await self.data.static.put_game_ticket( self.version, id, ticket_type, price, name ) self.logger.info(f"Added ticket {id}...") - def read_old_events(self, events: Optional[List[Dict[str, str]]]) -> None: + async def read_old_events(self, events: Optional[List[Dict[str, str]]]) -> None: if events is None: return @@ -332,12 +332,12 @@ class Mai2Reader(BaseReader): is_aou = bool(int(event.get('AOU許可', '0'))) name = event.get('comment', f'evt_{evt_id}') - self.data.static.put_game_event(self.version, 0, evt_id, name) + await self.data.static.put_game_event(self.version, 0, evt_id, name) if not (is_exp or is_aou): - self.data.static.toggle_game_event(self.version, evt_id, False) + await self.data.static.toggle_game_event(self.version, evt_id, False) - def read_old_music(self, scores: Optional[List[Dict[str, str]]], text: Optional[List[Dict[str, str]]]) -> None: + async def read_old_music(self, scores: Optional[List[Dict[str, str]]], text: Optional[List[Dict[str, str]]]) -> None: if scores is None or text is None: return # TODO diff --git a/titles/mai2/schema/item.py b/titles/mai2/schema/item.py index a6ed876..9aaf592 100644 --- a/titles/mai2/schema/item.py +++ b/titles/mai2/schema/item.py @@ -186,7 +186,7 @@ print_detail = Table( class Mai2ItemData(BaseData): - def put_item( + async def put_item( self, user_id: int, item_kind: int, item_id: int, stock: int, is_valid: bool ) -> None: sql = insert(item).values( @@ -202,7 +202,7 @@ class Mai2ItemData(BaseData): isValid=is_valid, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_item: failed to insert item! user_id: {user_id}, item_kind: {item_kind}, item_id: {item_id}" @@ -210,7 +210,7 @@ class Mai2ItemData(BaseData): return None return result.lastrowid - def get_items(self, user_id: int, item_kind: int = None) -> Optional[List[Row]]: + async def get_items(self, user_id: int, item_kind: int = None) -> Optional[List[Row]]: if item_kind is None: sql = item.select(item.c.user == user_id) else: @@ -218,12 +218,12 @@ class Mai2ItemData(BaseData): and_(item.c.user == user_id, item.c.itemKind == item_kind) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_item(self, user_id: int, item_kind: int, item_id: int) -> Optional[Row]: + async def get_item(self, user_id: int, item_kind: int, item_id: int) -> Optional[Row]: sql = item.select( and_( item.c.user == user_id, @@ -232,12 +232,12 @@ class Mai2ItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_login_bonus( + async def put_login_bonus( self, user_id: int, bonus_id: int, @@ -259,7 +259,7 @@ class Mai2ItemData(BaseData): isComplete=is_complete, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_login_bonus: failed to insert item! user_id: {user_id}, bonus_id: {bonus_id}, point: {point}" @@ -267,25 +267,25 @@ class Mai2ItemData(BaseData): return None return result.lastrowid - def get_login_bonuses(self, user_id: int) -> Optional[List[Row]]: + async def get_login_bonuses(self, user_id: int) -> Optional[List[Row]]: sql = login_bonus.select(login_bonus.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_login_bonus(self, user_id: int, bonus_id: int) -> Optional[Row]: + async def get_login_bonus(self, user_id: int, bonus_id: int) -> Optional[Row]: sql = login_bonus.select( and_(login_bonus.c.user == user_id, login_bonus.c.bonus_id == bonus_id) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_map( + async def put_map( self, user_id: int, map_id: int, @@ -310,7 +310,7 @@ class Mai2ItemData(BaseData): isComplete=is_complete, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_map: failed to insert item! user_id: {user_id}, map_id: {map_id}, distance: {distance}" @@ -318,28 +318,28 @@ class Mai2ItemData(BaseData): return None return result.lastrowid - def get_maps(self, user_id: int) -> Optional[List[Row]]: + async def get_maps(self, user_id: int) -> Optional[List[Row]]: sql = map.select(map.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_map(self, user_id: int, map_id: int) -> Optional[Row]: + async def get_map(self, user_id: int, map_id: int) -> Optional[Row]: sql = map.select(and_(map.c.user == user_id, map.c.mapId == map_id)) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_character_(self, user_id: int, char_data: Dict) -> Optional[int]: + async def put_character_(self, user_id: int, char_data: Dict) -> Optional[int]: char_data["user"] = user_id sql = insert(character).values(**char_data) conflict = sql.on_duplicate_key_update(**char_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_character_: failed to insert item! user_id: {user_id}" @@ -347,7 +347,7 @@ class Mai2ItemData(BaseData): return None return result.lastrowid - def put_character( + async def put_character( self, user_id: int, character_id: int, @@ -369,7 +369,7 @@ class Mai2ItemData(BaseData): useCount=use_count, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_character: failed to insert item! user_id: {user_id}, character_id: {character_id}, level: {level}" @@ -377,33 +377,33 @@ class Mai2ItemData(BaseData): return None return result.lastrowid - def get_characters(self, user_id: int) -> Optional[List[Row]]: + async def get_characters(self, user_id: int) -> Optional[List[Row]]: sql = character.select(character.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_character(self, user_id: int, character_id: int) -> Optional[Row]: + async def get_character(self, user_id: int, character_id: int) -> Optional[Row]: sql = character.select( and_(character.c.user == user_id, character.c.character_id == character_id) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_friend_season_ranking(self, user_id: int) -> Optional[Row]: + async def get_friend_season_ranking(self, user_id: int) -> Optional[Row]: sql = friend_season_ranking.select(friend_season_ranking.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_friend_season_ranking( + async def put_friend_season_ranking( self, aime_id: int, friend_season_ranking_data: Dict ) -> Optional[int]: sql = insert(friend_season_ranking).values( @@ -411,7 +411,7 @@ class Mai2ItemData(BaseData): ) conflict = sql.on_duplicate_key_update(**friend_season_ranking_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( @@ -421,7 +421,7 @@ class Mai2ItemData(BaseData): return None return result.lastrowid - def put_favorite( + async def put_favorite( self, user_id: int, kind: int, item_id_list: List[int] ) -> Optional[int]: sql = insert(favorite).values( @@ -430,7 +430,7 @@ class Mai2ItemData(BaseData): conflict = sql.on_duplicate_key_update(item_id_list=item_id_list) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_favorite: failed to insert item! user_id: {user_id}, kind: {kind}" @@ -438,7 +438,7 @@ class Mai2ItemData(BaseData): return None return result.lastrowid - def get_favorites(self, user_id: int, kind: int = None) -> Optional[Row]: + async def get_favorites(self, user_id: int, kind: int = None) -> Optional[Row]: if kind is None: sql = favorite.select(favorite.c.user == user_id) else: @@ -446,12 +446,12 @@ class Mai2ItemData(BaseData): and_(favorite.c.user == user_id, favorite.c.itemKind == kind) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_card( + async def put_card( self, user_id: int, card_type_id: int, @@ -475,7 +475,7 @@ class Mai2ItemData(BaseData): charaId=chara_id, mapId=map_id, startDate=start_date, endDate=end_date ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_card: failed to insert card! user_id: {user_id}, kind: {card_kind}" @@ -483,7 +483,7 @@ class Mai2ItemData(BaseData): return None return result.lastrowid - def get_cards(self, user_id: int, kind: int = None) -> Optional[Row]: + async def get_cards(self, user_id: int, kind: int = None) -> Optional[Row]: if kind is None: sql = card.select(card.c.user == user_id) else: @@ -491,12 +491,12 @@ class Mai2ItemData(BaseData): sql = sql.order_by(card.c.startDate.desc()) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_charge( + async def put_charge( self, user_id: int, charge_id: int, @@ -516,7 +516,7 @@ class Mai2ItemData(BaseData): stock=stock, purchaseDate=purchase_date, validDate=valid_date ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_card: failed to insert charge! user_id: {user_id}, chargeId: {charge_id}" @@ -524,15 +524,15 @@ class Mai2ItemData(BaseData): return None return result.lastrowid - def get_charges(self, user_id: int) -> Optional[Row]: + async def get_charges(self, user_id: int) -> Optional[Row]: sql = charge.select(charge.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_user_print_detail( + async def put_user_print_detail( self, aime_id: int, serial_id: str, user_print_data: Dict ) -> Optional[int]: sql = insert(print_detail).values( @@ -540,7 +540,7 @@ class Mai2ItemData(BaseData): ) conflict = sql.on_duplicate_key_update(**user_print_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( diff --git a/titles/mai2/schema/profile.py b/titles/mai2/schema/profile.py index 211440d..b4cdbd8 100644 --- a/titles/mai2/schema/profile.py +++ b/titles/mai2/schema/profile.py @@ -491,7 +491,7 @@ consec_logins = Table( class Mai2ProfileData(BaseData): - def put_profile_detail( + async def put_profile_detail( self, user_id: int, version: int, detail_data: Dict, is_dx: bool = True ) -> Optional[Row]: detail_data["user"] = user_id @@ -504,7 +504,7 @@ class Mai2ProfileData(BaseData): conflict = sql.on_duplicate_key_update(**detail_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_profile: Failed to create profile! user_id {user_id} is_dx {is_dx}" @@ -512,7 +512,7 @@ class Mai2ProfileData(BaseData): return None return result.lastrowid - def get_profile_detail( + async def get_profile_detail( self, user_id: int, version: int, is_dx: bool = True ) -> Optional[Row]: if is_dx: @@ -531,12 +531,12 @@ class Mai2ProfileData(BaseData): .order_by(detail_old.c.version.desc()) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_profile_ghost( + async def put_profile_ghost( self, user_id: int, version: int, ghost_data: Dict ) -> Optional[int]: ghost_data["user"] = user_id @@ -545,25 +545,25 @@ class Mai2ProfileData(BaseData): sql = insert(ghost).values(**ghost_data) conflict = sql.on_duplicate_key_update(**ghost_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_profile_ghost: failed to update! {user_id}") return None return result.lastrowid - def get_profile_ghost(self, user_id: int, version: int) -> Optional[Row]: + async def get_profile_ghost(self, user_id: int, version: int) -> Optional[Row]: sql = ( select(ghost) .where(and_(ghost.c.user == user_id, ghost.c.version_int <= version)) .order_by(ghost.c.version.desc()) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_profile_extend( + async def put_profile_extend( self, user_id: int, version: int, extend_data: Dict ) -> Optional[int]: extend_data["user"] = user_id @@ -572,25 +572,25 @@ class Mai2ProfileData(BaseData): sql = insert(extend).values(**extend_data) conflict = sql.on_duplicate_key_update(**extend_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_profile_extend: failed to update! {user_id}") return None return result.lastrowid - def get_profile_extend(self, user_id: int, version: int) -> Optional[Row]: + async def get_profile_extend(self, user_id: int, version: int) -> Optional[Row]: sql = ( select(extend) .where(and_(extend.c.user == user_id, extend.c.version <= version)) .order_by(extend.c.version.desc()) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_profile_option( + async def put_profile_option( self, user_id: int, version: int, option_data: Dict, is_dx: bool = True ) -> Optional[int]: option_data["user"] = user_id @@ -602,7 +602,7 @@ class Mai2ProfileData(BaseData): sql = insert(option_old).values(**option_data) conflict = sql.on_duplicate_key_update(**option_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_profile_option: failed to update! {user_id} is_dx {is_dx}" @@ -610,7 +610,7 @@ class Mai2ProfileData(BaseData): return None return result.lastrowid - def get_profile_option( + async def get_profile_option( self, user_id: int, version: int, is_dx: bool = True ) -> Optional[Row]: if is_dx: @@ -628,12 +628,12 @@ class Mai2ProfileData(BaseData): .order_by(option_old.c.version.desc()) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_profile_rating( + async def put_profile_rating( self, user_id: int, version: int, rating_data: Dict ) -> Optional[int]: rating_data["user"] = user_id @@ -642,25 +642,25 @@ class Mai2ProfileData(BaseData): sql = insert(rating).values(**rating_data) conflict = sql.on_duplicate_key_update(**rating_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_profile_rating: failed to update! {user_id}") return None return result.lastrowid - def get_profile_rating(self, user_id: int, version: int) -> Optional[Row]: + async def get_profile_rating(self, user_id: int, version: int) -> Optional[Row]: sql = ( select(rating) .where(and_(rating.c.user == user_id, rating.c.version <= version)) .order_by(rating.c.version.desc()) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_profile_region(self, user_id: int, region_id: int) -> Optional[int]: + async def put_profile_region(self, user_id: int, region_id: int) -> Optional[int]: sql = insert(region).values( user=user_id, regionId=region_id, @@ -669,21 +669,21 @@ class Mai2ProfileData(BaseData): conflict = sql.on_duplicate_key_update(playCount=region.c.playCount + 1) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_region: failed to update! {user_id}") return None return result.lastrowid - def get_regions(self, user_id: int) -> Optional[List[Dict]]: + async def get_regions(self, user_id: int) -> Optional[List[Dict]]: sql = select(region).where(region.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_profile_activity(self, user_id: int, activity_data: Dict) -> Optional[int]: + async def put_profile_activity(self, user_id: int, activity_data: Dict) -> Optional[int]: if "id" in activity_data: activity_data["activityId"] = activity_data["id"] activity_data.pop("id") @@ -694,7 +694,7 @@ class Mai2ProfileData(BaseData): conflict = sql.on_duplicate_key_update(**activity_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_profile_activity: failed to update! user_id: {user_id}" @@ -702,7 +702,7 @@ class Mai2ProfileData(BaseData): return None return result.lastrowid - def get_profile_activity( + async def get_profile_activity( self, user_id: int, kind: int = None ) -> Optional[List[Row]]: sql = activity.select( @@ -712,12 +712,12 @@ class Mai2ProfileData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_web_option( + async def put_web_option( self, user_id: int, version: int, web_opts: Dict ) -> Optional[int]: web_opts["user"] = user_id @@ -726,29 +726,29 @@ class Mai2ProfileData(BaseData): conflict = sql.on_duplicate_key_update(**web_opts) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_web_option: failed to update! user_id: {user_id}") return None return result.lastrowid - def get_web_option(self, user_id: int, version: int) -> Optional[Row]: + async def get_web_option(self, user_id: int, version: int) -> Optional[Row]: sql = web_opt.select( and_(web_opt.c.user == user_id, web_opt.c.version == version) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_grade_status(self, user_id: int, grade_stat: Dict) -> Optional[int]: + async def put_grade_status(self, user_id: int, grade_stat: Dict) -> Optional[int]: grade_stat["user"] = user_id sql = insert(grade_status).values(**grade_stat) conflict = sql.on_duplicate_key_update(**grade_stat) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_grade_status: failed to update! user_id: {user_id}" @@ -756,40 +756,40 @@ class Mai2ProfileData(BaseData): return None return result.lastrowid - def get_grade_status(self, user_id: int) -> Optional[Row]: + async def get_grade_status(self, user_id: int) -> Optional[Row]: sql = grade_status.select(grade_status.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_boss_list(self, user_id: int, boss_stat: Dict) -> Optional[int]: + async def put_boss_list(self, user_id: int, boss_stat: Dict) -> Optional[int]: boss_stat["user"] = user_id sql = insert(boss).values(**boss_stat) conflict = sql.on_duplicate_key_update(**boss_stat) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_boss_list: failed to update! user_id: {user_id}") return None return result.lastrowid - def get_boss_list(self, user_id: int) -> Optional[Row]: + async def get_boss_list(self, user_id: int) -> Optional[Row]: sql = boss.select(boss.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_recent_rating(self, user_id: int, rr: Dict) -> Optional[int]: + async def put_recent_rating(self, user_id: int, rr: Dict) -> Optional[int]: sql = insert(recent_rating).values(user=user_id, userRecentRatingList=rr) conflict = sql.on_duplicate_key_update({"userRecentRatingList": rr}) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_recent_rating: failed to update! user_id: {user_id}" @@ -797,26 +797,26 @@ class Mai2ProfileData(BaseData): return None return result.lastrowid - def get_recent_rating(self, user_id: int) -> Optional[Row]: + async def get_recent_rating(self, user_id: int) -> Optional[Row]: sql = recent_rating.select(recent_rating.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def add_consec_login(self, user_id: int, version: int) -> None: + async def add_consec_login(self, user_id: int, version: int) -> None: sql = insert(consec_logins).values(user=user_id, version=version, logins=1) conflict = sql.on_duplicate_key_update(logins=consec_logins.c.logins + 1) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"Failed to update consecutive login count for user {user_id} version {version}" ) - def get_consec_login(self, user_id: int, version: int) -> Optional[Row]: + async def get_consec_login(self, user_id: int, version: int) -> Optional[Row]: sql = select(consec_logins).where( and_( consec_logins.c.user == user_id, @@ -824,12 +824,12 @@ class Mai2ProfileData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def reset_consec_login(self, user_id: int, version: int) -> Optional[Row]: + async def reset_consec_login(self, user_id: int, version: int) -> Optional[Row]: sql = consec_logins.update( and_( consec_logins.c.user == user_id, @@ -837,7 +837,7 @@ class Mai2ProfileData(BaseData): ) ).values(logins=1) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() diff --git a/titles/mai2/schema/score.py b/titles/mai2/schema/score.py index 181a895..51dcc18 100644 --- a/titles/mai2/schema/score.py +++ b/titles/mai2/schema/score.py @@ -273,7 +273,7 @@ best_score_old = Table( ) class Mai2ScoreData(BaseData): - def put_best_score(self, user_id: int, score_data: Dict, is_dx: bool = True) -> Optional[int]: + async def put_best_score(self, user_id: int, score_data: Dict, is_dx: bool = True) -> Optional[int]: score_data["user"] = user_id if is_dx: @@ -282,7 +282,7 @@ class Mai2ScoreData(BaseData): sql = insert(best_score_old).values(**score_data) conflict = sql.on_duplicate_key_update(**score_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"put_best_score: Failed to insert best score! user_id {user_id} is_dx {is_dx}" @@ -291,7 +291,7 @@ class Mai2ScoreData(BaseData): return result.lastrowid @cached(2) - def get_best_scores(self, user_id: int, song_id: int = None, is_dx: bool = True) -> Optional[List[Row]]: + async def get_best_scores(self, user_id: int, song_id: int = None, is_dx: bool = True) -> Optional[List[Row]]: if is_dx: sql = best_score.select( and_( @@ -307,12 +307,12 @@ class Mai2ScoreData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_best_score( + async def get_best_score( self, user_id: int, song_id: int, chart_id: int ) -> Optional[Row]: sql = best_score.select( @@ -323,12 +323,12 @@ class Mai2ScoreData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_playlog(self, user_id: int, playlog_data: Dict, is_dx: bool = True) -> Optional[int]: + async def put_playlog(self, user_id: int, playlog_data: Dict, is_dx: bool = True) -> Optional[int]: playlog_data["user"] = user_id if is_dx: @@ -338,28 +338,28 @@ class Mai2ScoreData(BaseData): conflict = sql.on_duplicate_key_update(**playlog_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error(f"put_playlog: Failed to insert! user_id {user_id} is_dx {is_dx}") return None return result.lastrowid - def put_course(self, user_id: int, course_data: Dict) -> Optional[int]: + async def put_course(self, user_id: int, course_data: Dict) -> Optional[int]: course_data["user"] = user_id sql = insert(course).values(**course_data) conflict = sql.on_duplicate_key_update(**course_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error(f"put_course: Failed to insert! user_id {user_id}") return None return result.lastrowid - def get_courses(self, user_id: int) -> Optional[List[Row]]: + async def get_courses(self, user_id: int) -> Optional[List[Row]]: sql = course.select(course.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() diff --git a/titles/mai2/schema/static.py b/titles/mai2/schema/static.py index 0f5bfad..e33e4ec 100644 --- a/titles/mai2/schema/static.py +++ b/titles/mai2/schema/static.py @@ -72,7 +72,7 @@ cards = Table( class Mai2StaticData(BaseData): - def put_game_event( + async def put_game_event( self, version: int, type: int, event_id: int, name: str ) -> Optional[int]: sql = insert(event).values( @@ -84,46 +84,46 @@ class Mai2StaticData(BaseData): conflict = sql.on_duplicate_key_update(eventId=event_id) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_game_event: Failed to insert event! event_id {event_id} type {type} name {name}" ) return result.lastrowid - def get_game_events(self, version: int) -> Optional[List[Row]]: + async def get_game_events(self, version: int) -> Optional[List[Row]]: sql = event.select(event.c.version == version) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_enabled_events(self, version: int) -> Optional[List[Row]]: + async def get_enabled_events(self, version: int) -> Optional[List[Row]]: sql = select(event).where( and_(event.c.version == version, event.c.enabled == True) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def toggle_game_event( + async def toggle_game_event( self, version: int, event_id: int, toggle: bool ) -> Optional[List]: sql = event.update( and_(event.c.version == version, event.c.eventId == event_id) ).values(enabled=int(toggle)) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.warning( f"toggle_game_event: Failed to update event! event_id {event_id} toggle {toggle}" ) return result.last_updated_params() - def put_game_music( + async def put_game_music( self, version: int, song_id: int, @@ -159,13 +159,13 @@ class Mai2StaticData(BaseData): noteDesigner=note_designer, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"Failed to insert song {song_id} chart {chart_id}") return None return result.lastrowid - def put_game_ticket( + async def put_game_ticket( self, version: int, ticket_id: int, @@ -185,13 +185,13 @@ class Mai2StaticData(BaseData): conflict = sql.on_duplicate_key_update(price=ticket_price) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"Failed to insert charge {ticket_id} type {ticket_type}") return None return result.lastrowid - def get_enabled_tickets( + async def get_enabled_tickets( self, version: int, kind: int = None ) -> Optional[List[Row]]: if kind is not None: @@ -207,12 +207,12 @@ class Mai2StaticData(BaseData): and_(ticket.c.version == version, ticket.c.enabled == True) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_music_chart( + async def get_music_chart( self, version: int, song_id: int, chart_id: int ) -> Optional[List[Row]]: sql = select(music).where( @@ -223,28 +223,28 @@ class Mai2StaticData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_card(self, version: int, card_id: int, card_name: str, **card_data) -> int: + async def put_card(self, version: int, card_id: int, card_name: str, **card_data) -> int: sql = insert(cards).values( version=version, cardId=card_id, cardName=card_name, **card_data ) conflict = sql.on_duplicate_key_update(**card_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"Failed to insert card {card_id}") return None return result.lastrowid - def get_enabled_cards(self, version: int) -> Optional[List[Row]]: + async def get_enabled_cards(self, version: int) -> Optional[List[Row]]: sql = cards.select(and_(cards.c.version == version, cards.c.enabled == True)) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() diff --git a/titles/mai2/universe.py b/titles/mai2/universe.py index a8d5b46..774ea60 100644 --- a/titles/mai2/universe.py +++ b/titles/mai2/universe.py @@ -16,7 +16,7 @@ class Mai2Universe(Mai2SplashPlus): self.version = Mai2Constants.VER_MAIMAI_DX_UNIVERSE async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: - p = self.data.profile.get_profile_detail(data["userId"], self.version) + p = await self.data.profile.get_profile_detail(data["userId"], self.version) if p is None: return {} @@ -32,9 +32,9 @@ class Mai2Universe(Mai2SplashPlus): async def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict: # user already exists, because the preview checks that already - p = self.data.profile.get_profile_detail(data["userId"], self.version) + p = await self.data.profile.get_profile_detail(data["userId"], self.version) - cards = self.data.card.get_user_cards(data["userId"]) + cards = await self.data.card.get_user_cards(data["userId"]) if cards is None or len(cards) == 0: # This should never happen self.logger.error( @@ -59,7 +59,7 @@ class Mai2Universe(Mai2SplashPlus): return {"returnCode": 1} async def handle_cm_get_selling_card_api_request(self, data: Dict) -> Dict: - selling_cards = self.data.static.get_enabled_cards(self.version) + selling_cards = await self.data.static.get_enabled_cards(self.version) if selling_cards is None: return {"length": 0, "sellingCardList": []} @@ -89,7 +89,7 @@ class Mai2Universe(Mai2SplashPlus): return {"length": len(selling_card_list), "sellingCardList": selling_card_list} async def handle_cm_get_user_card_api_request(self, data: Dict) -> Dict: - user_cards = self.data.item.get_cards(data["userId"]) + user_cards = await self.data.item.get_cards(data["userId"]) if user_cards is None: return {"returnCode": 1, "length": 0, "nextIndex": 0, "userCardList": []} @@ -128,7 +128,7 @@ class Mai2Universe(Mai2SplashPlus): super().handle_get_user_item_api_request(data) async def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict: - characters = self.data.item.get_characters(data["userId"]) + characters = await self.data.item.get_characters(data["userId"]) chara_list = [] for chara in characters: @@ -168,7 +168,7 @@ class Mai2Universe(Mai2SplashPlus): end_date = datetime.utcnow() + timedelta(days=15) user_card = upsert["userCard"] - self.data.item.put_card( + await self.data.item.put_card( user_id, user_card["cardId"], user_card["cardTypeId"], @@ -180,7 +180,7 @@ class Mai2Universe(Mai2SplashPlus): ) # get the profile extend to save the new bought card - extend = self.data.profile.get_profile_extend(user_id, self.version) + extend = await self.data.profile.get_profile_extend(user_id, self.version) if extend: extend = extend._asdict() # parse the selectedCardList @@ -192,14 +192,14 @@ class Mai2Universe(Mai2SplashPlus): selected_cards.insert(0, user_card["cardTypeId"]) extend["selectedCardList"] = selected_cards - self.data.profile.put_profile_extend(user_id, self.version, extend) + await self.data.profile.put_profile_extend(user_id, self.version, extend) # properly format userPrintDetail for the database upsert.pop("userCard") upsert.pop("serialId") upsert["printDate"] = datetime.strptime(upsert["printDate"], "%Y-%m-%d") - self.data.item.put_user_print_detail(user_id, serial_id, upsert) + await self.data.item.put_user_print_detail(user_id, serial_id, upsert) return { "returnCode": 1, diff --git a/titles/ongeki/base.py b/titles/ongeki/base.py index 2d50184..0ade9da 100644 --- a/titles/ongeki/base.py +++ b/titles/ongeki/base.py @@ -157,7 +157,7 @@ class OngekiBase: return {"type": data["type"], "length": 0, "gameIdlistList": []} async def handle_get_game_ranking_api_request(self, data: Dict) -> Dict: - game_ranking_list = self.data.static.get_ranking_list(self.version) + game_ranking_list = await self.data.static.get_ranking_list(self.version) ranking_list = [] for music in game_ranking_list: @@ -172,13 +172,13 @@ class OngekiBase: } async def handle_get_game_point_api_request(self, data: Dict) -> Dict: - get_game_point = self.data.static.get_static_game_point() + get_game_point = await self.data.static.get_static_game_point() game_point = [] if not get_game_point: self.logger.info(f"GP table is empty, inserting defaults") - self.data.static.put_static_game_point_defaults() - get_game_point = self.data.static.get_static_game_point() + await self.data.static.put_static_game_point_defaults() + get_game_point = await self.data.static.get_static_game_point() for gp in get_game_point: tmp = gp._asdict() game_point.append(tmp) @@ -204,7 +204,7 @@ class OngekiBase: return {"returnCode": 1, "apiName": "ExtendLockTimeApi"} async def handle_get_game_reward_api_request(self, data: Dict) -> Dict: - get_game_rewards = self.data.static.get_reward_list(self.version) + get_game_rewards = await self.data.static.get_reward_list(self.version) reward_list = [] for reward in get_game_rewards: @@ -222,7 +222,7 @@ class OngekiBase: } async def handle_get_game_present_api_request(self, data: Dict) -> Dict: - get_present = self.data.static.get_present_list(self.version) + get_present = await self.data.static.get_present_list(self.version) present_list = [] for present in get_present: @@ -245,7 +245,7 @@ class OngekiBase: return {"length": 0, "gameSaleList": []} async def handle_get_game_tech_music_api_request(self, data: Dict) -> Dict: - music_list = self.data.static.get_tech_music(self.version) + music_list = await self.data.static.get_tech_music(self.version) prep_music_list = [] for music in music_list: @@ -268,9 +268,9 @@ class OngekiBase: client_id = data["clientId"] client_setting_data = data["clientSetting"] - cab = self.data.arcade.get_machine(client_id) + cab = await self.data.arcade.get_machine(client_id) if cab is not None: - self.data.static.put_client_setting_data(cab['id'], client_setting_data) + await self.data.static.put_client_setting_data(cab['id'], client_setting_data) return {"returnCode": 1, "apiName": "UpsertClientSettingApi"} async def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict: @@ -279,7 +279,7 @@ class OngekiBase: region_id = data["regionId"] client_testmode_data = data["clientTestmode"] - self.data.static.put_client_testmode_data(region_id, client_testmode_data) + await self.data.static.put_client_testmode_data(region_id, client_testmode_data) return {"returnCode": 1, "apiName": "UpsertClientTestmodeApi"} async def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict: @@ -296,7 +296,7 @@ class OngekiBase: if user >= 200000000000000: # Account for guest play user = None - self.data.log.put_gp_log( + await self.data.log.put_gp_log( user, data["usedCredit"], data["placeName"], @@ -313,7 +313,7 @@ class OngekiBase: return {"returnCode": 1, "apiName": "ExtendLockTimeApi"} async def handle_get_game_event_api_request(self, data: Dict) -> Dict: - evts = self.data.static.get_enabled_events(self.version) + evts = await self.data.static.get_enabled_events(self.version) if evts is None: return { @@ -366,7 +366,7 @@ class OngekiBase: return {"userId": data["userId"], "length": 0, "userRegionList": []} async def handle_get_user_preview_api_request(self, data: Dict) -> Dict: - profile = self.data.profile.get_profile_preview(data["userId"], self.version) + profile = await self.data.profile.get_profile_preview(data["userId"], self.version) if profile is None: return { @@ -422,7 +422,7 @@ class OngekiBase: Gets the number of AB and ABPs a player has per-difficulty (7, 7+, 8, etc) The game sends this in upsert so we don't have to calculate it all out thankfully """ - utcl = self.data.score.get_tech_count(data["userId"]) + utcl = await self.data.score.get_tech_count(data["userId"]) userTechCountList = [] for tc in utcl: @@ -437,7 +437,7 @@ class OngekiBase: } async def handle_get_user_tech_event_api_request(self, data: Dict) -> Dict: - user_tech_event_list = self.data.item.get_tech_event(self.version, data["userId"]) + user_tech_event_list = await self.data.item.get_tech_event(self.version, data["userId"]) if user_tech_event_list is None: return {} @@ -456,7 +456,7 @@ class OngekiBase: } async def handle_get_user_tech_event_ranking_api_request(self, data: Dict) -> Dict: - user_tech_event_ranks = self.data.item.get_tech_event_ranking(self.version, data["userId"]) + user_tech_event_ranks = await self.data.item.get_tech_event_ranking(self.version, data["userId"]) if user_tech_event_ranks is None: return { "userId": data["userId"], @@ -482,7 +482,7 @@ class OngekiBase: } async def handle_get_user_kop_api_request(self, data: Dict) -> Dict: - kop_list = self.data.profile.get_kop(data["userId"]) + kop_list = await self.data.profile.get_kop(data["userId"]) if kop_list is None: return {} @@ -518,7 +518,7 @@ class OngekiBase: async def handle_get_user_item_api_request(self, data: Dict) -> Dict: kind = data["nextIndex"] / 10000000000 - p = self.data.item.get_items(data["userId"], kind) + p = await self.data.item.get_items(data["userId"], kind) if p is None: return { @@ -553,7 +553,7 @@ class OngekiBase: } async def handle_get_user_option_api_request(self, data: Dict) -> Dict: - o = self.data.profile.get_profile_options(data["userId"]) + o = await self.data.profile.get_profile_options(data["userId"]) if o is None: return {} @@ -567,11 +567,11 @@ class OngekiBase: return {"userId": data["userId"], "userOption": user_opts} async def handle_get_user_data_api_request(self, data: Dict) -> Dict: - p = self.data.profile.get_profile_data(data["userId"], self.version) + p = await self.data.profile.get_profile_data(data["userId"], self.version) if p is None: return {} - cards = self.data.card.get_user_cards(data["userId"]) + cards = await self.data.card.get_user_cards(data["userId"]) if cards is None or len(cards) == 0: # This should never happen self.logger.error( @@ -595,7 +595,7 @@ class OngekiBase: return {"userId": data["userId"], "userData": user_data} async def handle_get_user_event_ranking_api_request(self, data: Dict) -> Dict: - user_event_ranking_list = self.data.item.get_ranking_event_ranks(self.version, data["userId"]) + user_event_ranking_list = await self.data.item.get_ranking_event_ranks(self.version, data["userId"]) if user_event_ranking_list is None: return {} @@ -618,7 +618,7 @@ class OngekiBase: } async def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict: - user_login_bonus_list = self.data.item.get_login_bonuses(data["userId"]) + user_login_bonus_list = await self.data.item.get_login_bonuses(data["userId"]) if user_login_bonus_list is None: return {} @@ -636,7 +636,7 @@ class OngekiBase: } async def handle_get_user_bp_base_request(self, data: Dict) -> Dict: - p = self.data.profile.get_profile( + p = await self.data.profile.get_profile( self.game, self.version, user_id=data["userId"] ) if p is None: @@ -649,7 +649,7 @@ class OngekiBase: } async def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict: - recent_rating = self.data.profile.get_profile_recent_rating(data["userId"]) + recent_rating = await self.data.profile.get_profile_recent_rating(data["userId"]) if recent_rating is None: return { "userId": data["userId"], @@ -666,7 +666,7 @@ class OngekiBase: } async def handle_get_user_activity_api_request(self, data: Dict) -> Dict: - activity = self.data.profile.get_profile_activity(data["userId"], data["kind"]) + activity = await self.data.profile.get_profile_activity(data["userId"], data["kind"]) if activity is None: return {} @@ -693,7 +693,7 @@ class OngekiBase: } async def handle_get_user_story_api_request(self, data: Dict) -> Dict: - user_stories = self.data.item.get_stories(data["userId"]) + user_stories = await self.data.item.get_stories(data["userId"]) if user_stories is None: return {} @@ -711,7 +711,7 @@ class OngekiBase: } async def handle_get_user_chapter_api_request(self, data: Dict) -> Dict: - user_chapters = self.data.item.get_chapters(data["userId"]) + user_chapters = await self.data.item.get_chapters(data["userId"]) if user_chapters is None: return {} @@ -736,7 +736,7 @@ class OngekiBase: } async def handle_get_user_character_api_request(self, data: Dict) -> Dict: - user_characters = self.data.item.get_characters(data["userId"]) + user_characters = await self.data.item.get_characters(data["userId"]) if user_characters is None: return {} @@ -754,7 +754,7 @@ class OngekiBase: } async def handle_get_user_card_api_request(self, data: Dict) -> Dict: - user_cards = self.data.item.get_cards(data["userId"]) + user_cards = await self.data.item.get_cards(data["userId"]) if user_cards is None: return {} @@ -773,7 +773,7 @@ class OngekiBase: async def handle_get_user_deck_by_key_api_request(self, data: Dict) -> Dict: # Auth key doesn't matter, it just wants all the decks - decks = self.data.item.get_decks(data["userId"]) + decks = await self.data.item.get_decks(data["userId"]) if decks is None: return {} @@ -791,7 +791,7 @@ class OngekiBase: } async def handle_get_user_trade_item_api_request(self, data: Dict) -> Dict: - user_trade_items = self.data.item.get_trade_items(data["userId"]) + user_trade_items = await self.data.item.get_trade_items(data["userId"]) if user_trade_items is None: return {} @@ -809,7 +809,7 @@ class OngekiBase: } async def handle_get_user_scenario_api_request(self, data: Dict) -> Dict: - user_scenerio = self.data.item.get_scenerios(data["userId"]) + user_scenerio = await self.data.item.get_scenerios(data["userId"]) if user_scenerio is None: return {} @@ -827,7 +827,7 @@ class OngekiBase: } async def handle_get_user_ratinglog_api_request(self, data: Dict) -> Dict: - rating_log = self.data.profile.get_profile_rating_log(data["userId"]) + rating_log = await self.data.profile.get_profile_rating_log(data["userId"]) if rating_log is None: return {} @@ -845,7 +845,7 @@ class OngekiBase: } async def handle_get_user_mission_point_api_request(self, data: Dict) -> Dict: - user_mission_point_list = self.data.item.get_mission_points(self.version, data["userId"]) + user_mission_point_list = await self.data.item.get_mission_points(self.version, data["userId"]) if user_mission_point_list is None: return {} @@ -865,7 +865,7 @@ class OngekiBase: } async def handle_get_user_event_point_api_request(self, data: Dict) -> Dict: - user_event_point_list = self.data.item.get_event_points(data["userId"]) + user_event_point_list = await self.data.item.get_event_points(data["userId"]) if user_event_point_list is None: return {} @@ -887,7 +887,7 @@ class OngekiBase: } async def handle_get_user_music_item_api_request(self, data: Dict) -> Dict: - user_music_item_list = self.data.item.get_music_items(data["userId"]) + user_music_item_list = await self.data.item.get_music_items(data["userId"]) if user_music_item_list is None: return {} @@ -905,7 +905,7 @@ class OngekiBase: } async def handle_get_user_event_music_api_request(self, data: Dict) -> Dict: - user_evt_music_list = self.data.item.get_event_music(data["userId"]) + user_evt_music_list = await self.data.item.get_event_music(data["userId"]) if user_evt_music_list is None: return {} @@ -923,7 +923,7 @@ class OngekiBase: } async def handle_get_user_boss_api_request(self, data: Dict) -> Dict: - p = self.data.item.get_bosses(data["userId"]) + p = await self.data.item.get_bosses(data["userId"]) if p is None: return {} @@ -947,20 +947,20 @@ class OngekiBase: # The isNew fields are new as of Red and up. We just won't use them for now. if "userData" in upsert and len(upsert["userData"]) > 0: - self.data.profile.put_profile_data( + await self.data.profile.put_profile_data( user_id, self.version, upsert["userData"][0] ) if "userOption" in upsert and len(upsert["userOption"]) > 0: - self.data.profile.put_profile_options(user_id, upsert["userOption"][0]) + await self.data.profile.put_profile_options(user_id, upsert["userOption"][0]) if "userPlaylogList" in upsert: for playlog in upsert["userPlaylogList"]: - self.data.score.put_playlog(user_id, playlog) + await self.data.score.put_playlog(user_id, playlog) if "userActivityList" in upsert: for act in upsert["userActivityList"]: - self.data.profile.put_profile_activity( + await self.data.profile.put_profile_activity( user_id, act["kind"], act["id"], @@ -972,101 +972,101 @@ class OngekiBase: ) if "userRecentRatingList" in upsert: - self.data.profile.put_profile_recent_rating( + await self.data.profile.put_profile_recent_rating( user_id, upsert["userRecentRatingList"] ) if "userBpBaseList" in upsert: - self.data.profile.put_profile_bp_list(user_id, upsert["userBpBaseList"]) + await self.data.profile.put_profile_bp_list(user_id, upsert["userBpBaseList"]) if "userMusicDetailList" in upsert: for x in upsert["userMusicDetailList"]: - self.data.score.put_best_score(user_id, x) + await self.data.score.put_best_score(user_id, x) if "userCharacterList" in upsert: for x in upsert["userCharacterList"]: - self.data.item.put_character(user_id, x) + await self.data.item.put_character(user_id, x) if "userCardList" in upsert: for x in upsert["userCardList"]: - self.data.item.put_card(user_id, x) + await self.data.item.put_card(user_id, x) if "userDeckList" in upsert: for x in upsert["userDeckList"]: - self.data.item.put_deck(user_id, x) + await self.data.item.put_deck(user_id, x) if "userTrainingRoomList" in upsert: for x in upsert["userTrainingRoomList"]: - self.data.profile.put_training_room(user_id, x) + await self.data.profile.put_training_room(user_id, x) if "userStoryList" in upsert: for x in upsert["userStoryList"]: - self.data.item.put_story(user_id, x) + await self.data.item.put_story(user_id, x) if "userChapterList" in upsert: for x in upsert["userChapterList"]: - self.data.item.put_chapter(user_id, x) + await self.data.item.put_chapter(user_id, x) if "userMemoryChapterList" in upsert: for x in upsert["userMemoryChapterList"]: - self.data.item.put_memorychapter(user_id, x) + await self.data.item.put_memorychapter(user_id, x) if "userItemList" in upsert: for x in upsert["userItemList"]: - self.data.item.put_item(user_id, x) + await self.data.item.put_item(user_id, x) if "userMusicItemList" in upsert: for x in upsert["userMusicItemList"]: - self.data.item.put_music_item(user_id, x) + await self.data.item.put_music_item(user_id, x) if "userLoginBonusList" in upsert: for x in upsert["userLoginBonusList"]: - self.data.item.put_login_bonus(user_id, x) + await self.data.item.put_login_bonus(user_id, x) if "userEventPointList" in upsert: for x in upsert["userEventPointList"]: - self.data.item.put_event_point(user_id, self.version, x) + await self.data.item.put_event_point(user_id, self.version, x) if "userMissionPointList" in upsert: for x in upsert["userMissionPointList"]: - self.data.item.put_mission_point(user_id, self.version, x) + await self.data.item.put_mission_point(user_id, self.version, x) if "userRatinglogList" in upsert: for x in upsert["userRatinglogList"]: - self.data.profile.put_profile_rating_log( + await self.data.profile.put_profile_rating_log( user_id, x["dataVersion"], x["highestRating"] ) if "userBossList" in upsert: for x in upsert["userBossList"]: - self.data.item.put_boss(user_id, x) + await self.data.item.put_boss(user_id, x) if "userTechCountList" in upsert: for x in upsert["userTechCountList"]: - self.data.score.put_tech_count(user_id, x) + await self.data.score.put_tech_count(user_id, x) if "userScenerioList" in upsert: for x in upsert["userScenerioList"]: - self.data.item.put_scenerio(user_id, x) + await self.data.item.put_scenerio(user_id, x) if "userTradeItemList" in upsert: for x in upsert["userTradeItemList"]: - self.data.item.put_trade_item(user_id, x) + await self.data.item.put_trade_item(user_id, x) if "userEventMusicList" in upsert: for x in upsert["userEventMusicList"]: - self.data.item.put_event_music(user_id, x) + await self.data.item.put_event_music(user_id, x) if "userTechEventList" in upsert: for x in upsert["userTechEventList"]: - self.data.item.put_tech_event(user_id, self.version, x) + await self.data.item.put_tech_event(user_id, self.version, x) # This should be updated once a day in maintenance window, but for time being we will push the update on each upsert - self.data.item.put_tech_event_ranking(user_id, self.version, x) + await self.data.item.put_tech_event_ranking(user_id, self.version, x) if "userKopList" in upsert: for x in upsert["userKopList"]: - self.data.profile.put_kop(user_id, x) + await self.data.profile.put_kop(user_id, x) return {"returnCode": 1, "apiName": "upsertUserAll"} @@ -1076,7 +1076,7 @@ class OngekiBase: """ rival_list = [] - user_rivals = self.data.profile.get_rivals(data["userId"]) + user_rivals = await self.data.profile.get_rivals(data["userId"]) for rival in user_rivals: tmp = {} tmp["rivalUserId"] = rival[0] @@ -1100,7 +1100,7 @@ class OngekiBase: """ rivals = [] for rival in data["userRivalList"]: - name = self.data.profile.get_profile_name( + name = await self.data.profile.get_profile_name( rival["rivalUserId"], self.version ) if name is None: @@ -1135,8 +1135,8 @@ class OngekiBase: } @cached(2) - def util_generate_music_list(self, user_id: int) -> List: - music_detail = self.data.score.get_best_scores(user_id) + async def util_generate_music_list(self, user_id: int) -> List: + music_detail = await self.data.score.get_best_scores(user_id) song_list = [] for md in music_detail: diff --git a/titles/ongeki/bright.py b/titles/ongeki/bright.py index 0af5127..aec9182 100644 --- a/titles/ongeki/bright.py +++ b/titles/ongeki/bright.py @@ -23,11 +23,11 @@ class OngekiBright(OngekiBase): async def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict: # check for a bright profile - p = self.data.profile.get_profile_data(data["userId"], self.version) + p = await self.data.profile.get_profile_data(data["userId"], self.version) if p is None: return {} - cards = self.data.card.get_user_cards(data["userId"]) + cards = await self.data.card.get_user_cards(data["userId"]) if cards is None or len(cards) == 0: # This should never happen self.logger.error( @@ -62,7 +62,7 @@ class OngekiBright(OngekiBase): return {"returnCode": 1} async def handle_cm_get_user_card_api_request(self, data: Dict) -> Dict: - user_cards = self.data.item.get_cards(data["userId"]) + user_cards = await self.data.item.get_cards(data["userId"]) if user_cards is None: return {} @@ -91,7 +91,7 @@ class OngekiBright(OngekiBase): } async def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict: - user_characters = self.data.item.get_characters(data["userId"]) + user_characters = await self.data.item.get_characters(data["userId"]) if user_characters is None: return { "userId": data["userId"], @@ -125,7 +125,7 @@ class OngekiBright(OngekiBase): } async def handle_get_user_gacha_api_request(self, data: Dict) -> Dict: - user_gachas = self.data.item.get_user_gachas(data["userId"]) + user_gachas = await self.data.item.get_user_gachas(data["userId"]) if user_gachas is None: return {"userId": data["userId"], "length": 0, "userGachaList": []} @@ -148,7 +148,7 @@ class OngekiBright(OngekiBase): async def handle_cm_get_user_gacha_supply_api_request(self, data: Dict) -> Dict: # not used for now? not sure what it even does - user_gacha_supplies = self.data.item.get_user_gacha_supplies(data["userId"]) + user_gacha_supplies = await self.data.item.get_user_gacha_supplies(data["userId"]) if user_gacha_supplies is None: return {"supplyId": 1, "length": 0, "supplyCardList": []} @@ -168,7 +168,7 @@ class OngekiBright(OngekiBase): game_gachas = [] # for every gacha_id in the OngekiConfig, grab the banner from the db for gacha_id in self.game_cfg.gachas.enabled_gachas: - game_gacha = self.data.static.get_gacha(self.version, gacha_id) + game_gacha = await self.data.static.get_gacha(self.version, gacha_id) if game_gacha: game_gachas.append(game_gacha) @@ -265,26 +265,26 @@ class OngekiBright(OngekiBase): return self.handle_roll_gacha_api_request(data) # get a list of cards for each rarity - cards_r = self.data.static.get_cards_by_rarity(self.version, 1) + cards_r = await self.data.static.get_cards_by_rarity(self.version, 1) cards_sr, cards_ssr = [], [] # free gachas are only allowed to get their specific cards! (R irrelevant) if gacha_id in {1011, 1012}: - gacha_cards = self.data.static.get_gacha_cards(gacha_id) + gacha_cards = await self.data.static.get_gacha_cards(gacha_id) for card in gacha_cards: if card["rarity"] == 3: cards_sr.append({"cardId": card["cardId"], "rarity": 2}) elif card["rarity"] == 4: cards_ssr.append({"cardId": card["cardId"], "rarity": 3}) else: - cards_sr = self.data.static.get_cards_by_rarity(self.version, 2) - cards_ssr = self.data.static.get_cards_by_rarity(self.version, 3) + cards_sr = await self.data.static.get_cards_by_rarity(self.version, 2) + cards_ssr = await self.data.static.get_cards_by_rarity(self.version, 3) # get the promoted cards for that gacha and add them multiple # times to increase chances by factor chances chances = 10 - gacha_cards = self.data.static.get_gacha_cards(gacha_id) + gacha_cards = await self.data.static.get_gacha_cards(gacha_id) for card in gacha_cards: # make sure to add the cards to the corresponding rarity if card["rarity"] == 2: @@ -339,7 +339,7 @@ class OngekiBright(OngekiBase): daily_gacha_date = datetime.strptime("2000-01-01", "%Y-%m-%d") # check if the user previously rolled the exact same gacha - user_gacha = self.data.item.get_user_gacha(user_id, gacha_id) + user_gacha = await self.data.item.get_user_gacha(user_id, gacha_id) if user_gacha: total_gacha_count = user_gacha["totalGachaCnt"] ceiling_gacha_count = user_gacha["ceilingGachaCnt"] @@ -358,7 +358,7 @@ class OngekiBright(OngekiBase): daily_gacha_date = play_date daily_gacha_cnt = 0 - self.data.item.put_user_gacha( + await self.data.item.put_user_gacha( user_id, gacha_id, totalGachaCnt=total_gacha_count + gacha_count, @@ -375,29 +375,29 @@ class OngekiBright(OngekiBase): if "userData" in upsert and len(upsert["userData"]) > 0: # check if the profile is a bright memory profile - p = self.data.profile.get_profile_data(data["userId"], self.version) + p = await self.data.profile.get_profile_data(data["userId"], self.version) if p is not None: # save the bright memory profile - self.data.profile.put_profile_data( + await self.data.profile.put_profile_data( user_id, self.version, upsert["userData"][0] ) else: # save the bright profile - self.data.profile.put_profile_data( + await self.data.profile.put_profile_data( user_id, self.version, upsert["userData"][0] ) if "userCharacterList" in upsert: for x in upsert["userCharacterList"]: - self.data.item.put_character(user_id, x) + await self.data.item.put_character(user_id, x) if "userItemList" in upsert: for x in upsert["userItemList"]: - self.data.item.put_item(user_id, x) + await self.data.item.put_item(user_id, x) if "userCardList" in upsert: for x in upsert["userCardList"]: - self.data.item.put_card(user_id, x) + await self.data.item.put_card(user_id, x) # TODO? # if "gameGachaCardList" in upsert: @@ -411,29 +411,29 @@ class OngekiBright(OngekiBase): if "userData" in upsert and len(upsert["userData"]) > 0: # check if the profile is a bright memory profile - p = self.data.profile.get_profile_data(data["userId"], self.version) + p = await self.data.profile.get_profile_data(data["userId"], self.version) if p is not None: # save the bright memory profile - self.data.profile.put_profile_data( + await self.data.profile.put_profile_data( user_id, self.version, upsert["userData"][0] ) else: # save the bright profile - self.data.profile.put_profile_data( + await self.data.profile.put_profile_data( user_id, self.version, upsert["userData"][0] ) if "userCharacterList" in upsert: for x in upsert["userCharacterList"]: - self.data.item.put_character(user_id, x) + await self.data.item.put_character(user_id, x) if "userCardList" in upsert: for x in upsert["userCardList"]: - self.data.item.put_card(user_id, x) + await self.data.item.put_card(user_id, x) if "selectGachaLogList" in data: for x in data["selectGachaLogList"]: - self.data.item.put_user_gacha( + await self.data.item.put_user_gacha( user_id, x["gachaId"], selectPoint=0, @@ -443,7 +443,7 @@ class OngekiBright(OngekiBase): return {"returnCode": 1, "apiName": "cmUpsertUserSelectGacha"} async def handle_get_game_gacha_card_by_id_api_request(self, data: Dict) -> Dict: - game_gacha_cards = self.data.static.get_gacha_cards(data["gachaId"]) + game_gacha_cards = await self.data.static.get_gacha_cards(data["gachaId"]) if game_gacha_cards == []: # fallback to be at least able to select that gacha return { @@ -579,7 +579,7 @@ class OngekiBright(OngekiBase): ) # add the entry to the user print table with the random serialId - self.data.item.put_user_print_detail( + await self.data.item.put_user_print_detail( data["userId"], serial_id, user_print_detail ) @@ -595,21 +595,21 @@ class OngekiBright(OngekiBase): if "userData" in upsert and len(upsert["userData"]) > 0: # check if the profile is a bright memory profile - p = self.data.profile.get_profile_data(data["userId"], self.version) + p = await self.data.profile.get_profile_data(data["userId"], self.version) if p is not None: # save the bright memory profile - self.data.profile.put_profile_data( + await self.data.profile.put_profile_data( user_id, self.version, upsert["userData"][0] ) else: # save the bright profile - self.data.profile.put_profile_data( + await self.data.profile.put_profile_data( user_id, self.version, upsert["userData"][0] ) if "userActivityList" in upsert: for act in upsert["userActivityList"]: - self.data.profile.put_profile_activity( + await self.data.profile.put_profile_activity( user_id, act["kind"], act["id"], @@ -622,10 +622,10 @@ class OngekiBright(OngekiBase): if "userItemList" in upsert: for x in upsert["userItemList"]: - self.data.item.put_item(user_id, x) + await self.data.item.put_item(user_id, x) if "userCardList" in upsert: for x in upsert["userCardList"]: - self.data.item.put_card(user_id, x) + await self.data.item.put_card(user_id, x) return {"returnCode": 1, "apiName": "cmUpsertUserAll"} diff --git a/titles/ongeki/brightmemory.py b/titles/ongeki/brightmemory.py index 0bff264..6fdd60a 100644 --- a/titles/ongeki/brightmemory.py +++ b/titles/ongeki/brightmemory.py @@ -28,7 +28,7 @@ class OngekiBrightMemory(OngekiBright): return ret async def handle_get_user_memory_chapter_api_request(self, data: Dict) -> Dict: - memories = self.data.item.get_memorychapters(data["userId"]) + memories = await self.data.item.get_memorychapters(data["userId"]) if not memories: return { "userId": data["userId"], diff --git a/titles/ongeki/frontend.py b/titles/ongeki/frontend.py index 4c17165..c4d875b 100644 --- a/titles/ongeki/frontend.py +++ b/titles/ongeki/frontend.py @@ -28,7 +28,7 @@ class OngekiFrontend(FE_Base): self.nav_name = "O.N.G.E.K.I." self.version_list = OngekiConstants.VERSION_NAMES - def render_GET(self, request: Request) -> bytes: + async def render_GET(self, request: Request) -> bytes: template = self.environment.get_template( "titles/ongeki/frontend/ongeki_index.jinja" ) @@ -37,7 +37,7 @@ class OngekiFrontend(FE_Base): self.version = usr_sesh.ongeki_version if getattr(usr_sesh, "userId", 0) != 0: profile_data =self.data.profile.get_profile_data(usr_sesh.userId, self.version) - rival_list = self.data.profile.get_rivals(usr_sesh.userId) + rival_list = await self.data.profile.get_rivals(usr_sesh.userId) rival_data = { "userRivalList": rival_list, "userId": usr_sesh.userId @@ -58,20 +58,20 @@ class OngekiFrontend(FE_Base): else: return redirectTo(b"/gate/", request) - def render_POST(self, request: Request): + async def render_POST(self, request: Request): uri = request.uri.decode() sesh: Session = request.getSession() usr_sesh = IUserSession(sesh) if hasattr(usr_sesh, "userId"): if uri == "/game/ongeki/rival.add": rival_id = request.args[b"rivalUserId"][0].decode() - self.data.profile.put_rival(usr_sesh.userId, rival_id) + await self.data.profile.put_rival(usr_sesh.userId, rival_id) # self.logger.info(f"{usr_sesh.userId} added a rival") return redirectTo(b"/game/ongeki/", request) elif uri == "/game/ongeki/rival.delete": rival_id = request.args[b"rivalUserId"][0].decode() - self.data.profile.delete_rival(usr_sesh.userId, rival_id) + await self.data.profile.delete_rival(usr_sesh.userId, rival_id) # self.logger.info(f"{response}") return redirectTo(b"/game/ongeki/", request) diff --git a/titles/ongeki/read.py b/titles/ongeki/read.py index 6e94094..a804956 100644 --- a/titles/ongeki/read.py +++ b/titles/ongeki/read.py @@ -1,16 +1,11 @@ -from decimal import Decimal -import logging import os -import re import xml.etree.ElementTree as ET -from typing import Any, Dict, List, Optional +from typing import Optional from read import BaseReader from core.config import CoreConfig from titles.ongeki.database import OngekiData from titles.ongeki.const import OngekiConstants -from titles.ongeki.config import OngekiConfig - class OngekiReader(BaseReader): def __init__( @@ -32,7 +27,7 @@ class OngekiReader(BaseReader): self.logger.error(f"Invalid ongeki version {version}") exit(1) - def read(self) -> None: + async def read(self) -> None: data_dirs = [] if self.bin_dir is not None: data_dirs += self.get_data_directories(self.bin_dir) @@ -41,12 +36,12 @@ class OngekiReader(BaseReader): data_dirs += self.get_data_directories(self.opt_dir) for dir in data_dirs: - self.read_events(f"{dir}/event") - self.read_music(f"{dir}/music") - self.read_card(f"{dir}/card") - self.read_reward(f"{dir}/reward") + await self.read_events(f"{dir}/event") + await self.read_music(f"{dir}/music") + await self.read_card(f"{dir}/card") + await self.read_reward(f"{dir}/reward") - def read_card(self, base_dir: str) -> None: + async def read_card(self, base_dir: str) -> None: self.logger.info(f"Reading cards from {base_dir}...") version_ids = { @@ -70,7 +65,7 @@ class OngekiReader(BaseReader): # skip already existing cards if ( - self.data.static.get_card( + await self.data.static.get_card( OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY, card_id ) is not None @@ -100,7 +95,7 @@ class OngekiReader(BaseReader): version = version_ids[troot.find("VersionID").find("id").text] card_number = troot.find("CardNumberString").text - self.data.static.put_card( + await self.data.static.put_card( version, card_id, name=name, @@ -117,7 +112,7 @@ class OngekiReader(BaseReader): ) self.logger.info(f"Added card {card_id}") - def read_events(self, base_dir: str) -> None: + async def read_events(self, base_dir: str) -> None: self.logger.info(f"Reading events from {base_dir}...") for root, dirs, files in os.walk(base_dir): @@ -132,10 +127,10 @@ class OngekiReader(BaseReader): troot.find("EventType").text ].value - self.data.static.put_event(self.version, id, event_type, name) + await self.data.static.put_event(self.version, id, event_type, name) self.logger.info(f"Added event {id}") - def read_music(self, base_dir: str) -> None: + async def read_music(self, base_dir: str) -> None: self.logger.info(f"Reading music from {base_dir}...") for root, dirs, files in os.walk(base_dir): @@ -168,12 +163,12 @@ class OngekiReader(BaseReader): f"{fumens_data.find('FumenConstIntegerPart').text}.{fumens_data.find('FumenConstFractionalPart').text}" ) - self.data.static.put_chart( + await self.data.static.put_chart( self.version, song_id, chart_id, title, artist, genre, level ) self.logger.info(f"Added song {song_id} chart {chart_id}") - def read_reward(self, base_dir: str) -> None: + async def read_reward(self, base_dir: str) -> None: self.logger.info(f"Reading rewards from {base_dir}...") for root, dirs, files in os.walk(base_dir): @@ -195,5 +190,5 @@ class OngekiReader(BaseReader): itemKind = OngekiConstants.REWARD_TYPES[troot.find("ItemType").text].value itemId = troot.find("RewardItem").find("ItemName").find("id").text - self.data.static.put_reward(self.version, rewardId, rewardname, itemKind, itemId) + await self.data.static.put_reward(self.version, rewardId, rewardname, itemKind, itemId) self.logger.info(f"Added reward {rewardId}") diff --git a/titles/ongeki/schema/item.py b/titles/ongeki/schema/item.py index 55b4c68..ca2de1f 100644 --- a/titles/ongeki/schema/item.py +++ b/titles/ongeki/schema/item.py @@ -339,147 +339,147 @@ print_detail = Table( ) class OngekiItemData(BaseData): - def put_card(self, aime_id: int, card_data: Dict) -> Optional[int]: + async def put_card(self, aime_id: int, card_data: Dict) -> Optional[int]: card_data["user"] = aime_id sql = insert(card).values(**card_data) conflict = sql.on_duplicate_key_update(**card_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_card: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def get_cards(self, aime_id: int) -> Optional[List[Dict]]: + async def get_cards(self, aime_id: int) -> Optional[List[Dict]]: sql = select(card).where(card.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_character(self, aime_id: int, character_data: Dict) -> Optional[int]: + async def put_character(self, aime_id: int, character_data: Dict) -> Optional[int]: character_data["user"] = aime_id sql = insert(character).values(**character_data) conflict = sql.on_duplicate_key_update(**character_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_character: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def get_characters(self, aime_id: int) -> Optional[List[Dict]]: + async def get_characters(self, aime_id: int) -> Optional[List[Dict]]: sql = select(character).where(character.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_deck(self, aime_id: int, deck_data: Dict) -> Optional[int]: + async def put_deck(self, aime_id: int, deck_data: Dict) -> Optional[int]: deck_data["user"] = aime_id sql = insert(deck).values(**deck_data) conflict = sql.on_duplicate_key_update(**deck_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_deck: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def get_deck(self, aime_id: int, deck_id: int) -> Optional[Dict]: + async def get_deck(self, aime_id: int, deck_id: int) -> Optional[Dict]: sql = select(deck).where(and_(deck.c.user == aime_id, deck.c.deckId == deck_id)) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_decks(self, aime_id: int) -> Optional[List[Dict]]: + async def get_decks(self, aime_id: int) -> Optional[List[Dict]]: sql = select(deck).where(deck.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_boss(self, aime_id: int, boss_data: Dict) -> Optional[int]: + async def put_boss(self, aime_id: int, boss_data: Dict) -> Optional[int]: boss_data["user"] = aime_id sql = insert(boss).values(**boss_data) conflict = sql.on_duplicate_key_update(**boss_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_boss: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def put_story(self, aime_id: int, story_data: Dict) -> Optional[int]: + async def put_story(self, aime_id: int, story_data: Dict) -> Optional[int]: story_data["user"] = aime_id sql = insert(story).values(**story_data) conflict = sql.on_duplicate_key_update(**story_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_story: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def get_stories(self, aime_id: int) -> Optional[List[Dict]]: + async def get_stories(self, aime_id: int) -> Optional[List[Dict]]: sql = select(story).where(story.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_chapter(self, aime_id: int, chapter_data: Dict) -> Optional[int]: + async def put_chapter(self, aime_id: int, chapter_data: Dict) -> Optional[int]: chapter_data["user"] = aime_id sql = insert(chapter).values(**chapter_data) conflict = sql.on_duplicate_key_update(**chapter_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_chapter: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def get_chapters(self, aime_id: int) -> Optional[List[Dict]]: + async def get_chapters(self, aime_id: int) -> Optional[List[Dict]]: sql = select(chapter).where(chapter.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_item(self, aime_id: int, item_data: Dict) -> Optional[int]: + async def put_item(self, aime_id: int, item_data: Dict) -> Optional[int]: item_data["user"] = aime_id sql = insert(item).values(**item_data) conflict = sql.on_duplicate_key_update(**item_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_item: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def get_item(self, aime_id: int, item_id: int, item_kind: int) -> Optional[Dict]: + async def get_item(self, aime_id: int, item_id: int, item_kind: int) -> Optional[Dict]: sql = select(item).where(and_(item.c.user == aime_id, item.c.itemId == item_id)) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_items(self, aime_id: int, item_kind: int = None) -> Optional[List[Dict]]: + async def get_items(self, aime_id: int, item_kind: int = None) -> Optional[List[Dict]]: if item_kind is None: sql = select(item).where(item.c.user == aime_id) else: @@ -487,73 +487,73 @@ class OngekiItemData(BaseData): and_(item.c.user == aime_id, item.c.itemKind == item_kind) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_music_item(self, aime_id: int, music_item_data: Dict) -> Optional[int]: + async def put_music_item(self, aime_id: int, music_item_data: Dict) -> Optional[int]: music_item_data["user"] = aime_id sql = insert(music_item).values(**music_item_data) conflict = sql.on_duplicate_key_update(**music_item_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_music_item: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def get_music_items(self, aime_id: int) -> Optional[List[Dict]]: + async def get_music_items(self, aime_id: int) -> Optional[List[Dict]]: sql = select(music_item).where(music_item.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_login_bonus(self, aime_id: int, login_bonus_data: Dict) -> Optional[int]: + async def put_login_bonus(self, aime_id: int, login_bonus_data: Dict) -> Optional[int]: login_bonus_data["user"] = aime_id sql = insert(login_bonus).values(**login_bonus_data) conflict = sql.on_duplicate_key_update(**login_bonus_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_login_bonus: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def get_login_bonuses(self, aime_id: int) -> Optional[List[Dict]]: + async def get_login_bonuses(self, aime_id: int) -> Optional[List[Dict]]: sql = select(login_bonus).where(login_bonus.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_mission_point(self, aime_id: int, version: int, mission_point_data: Dict) -> Optional[int]: + async def put_mission_point(self, aime_id: int, version: int, mission_point_data: Dict) -> Optional[int]: mission_point_data["version"] = version mission_point_data["user"] = aime_id sql = insert(mission_point).values(**mission_point_data) conflict = sql.on_duplicate_key_update(**mission_point_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_mission_point: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def get_mission_points(self, version: int, aime_id: int) -> Optional[List[Dict]]: + async def get_mission_points(self, version: int, aime_id: int) -> Optional[List[Dict]]: sql = select(mission_point).where(and_(mission_point.c.user == aime_id, mission_point.c.version == version)) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_event_point(self, aime_id: int, version: int, event_point_data: Dict) -> Optional[int]: + async def put_event_point(self, aime_id: int, version: int, event_point_data: Dict) -> Optional[int]: # We update only the newest (type: 1) entry, in official spec game watches for both latest(type:1) and previous (type:2) entries to give an additional info how many ranks has player moved up or down # This fully featured is on TODO list, at the moment we just update the tables as data comes and give out rank as request comes event_point_data["user"] = aime_id @@ -564,95 +564,95 @@ class OngekiItemData(BaseData): sql = insert(event_point).values(**event_point_data) conflict = sql.on_duplicate_key_update(**event_point_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_event_point: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def get_event_points(self, aime_id: int) -> Optional[List[Dict]]: + async def get_event_points(self, aime_id: int) -> Optional[List[Dict]]: sql = select(event_point).where(event_point.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_scenerio(self, aime_id: int, scenerio_data: Dict) -> Optional[int]: + async def put_scenerio(self, aime_id: int, scenerio_data: Dict) -> Optional[int]: scenerio_data["user"] = aime_id sql = insert(scenerio).values(**scenerio_data) conflict = sql.on_duplicate_key_update(**scenerio_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_scenerio: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def get_scenerios(self, aime_id: int) -> Optional[List[Dict]]: + async def get_scenerios(self, aime_id: int) -> Optional[List[Dict]]: sql = select(scenerio).where(scenerio.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_trade_item(self, aime_id: int, trade_item_data: Dict) -> Optional[int]: + async def put_trade_item(self, aime_id: int, trade_item_data: Dict) -> Optional[int]: trade_item_data["user"] = aime_id sql = insert(trade_item).values(**trade_item_data) conflict = sql.on_duplicate_key_update(**trade_item_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_trade_item: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def get_trade_items(self, aime_id: int) -> Optional[List[Dict]]: + async def get_trade_items(self, aime_id: int) -> Optional[List[Dict]]: sql = select(trade_item).where(trade_item.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_event_music(self, aime_id: int, event_music_data: Dict) -> Optional[int]: + async def put_event_music(self, aime_id: int, event_music_data: Dict) -> Optional[int]: event_music_data["user"] = aime_id sql = insert(event_music).values(**event_music_data) conflict = sql.on_duplicate_key_update(**event_music_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_event_music: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def get_event_music(self, aime_id: int) -> Optional[List[Dict]]: + async def get_event_music(self, aime_id: int) -> Optional[List[Dict]]: sql = select(event_music).where(event_music.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_tech_event(self, aime_id: int, version: int, tech_event_data: Dict) -> Optional[int]: + async def put_tech_event(self, aime_id: int, version: int, tech_event_data: Dict) -> Optional[int]: tech_event_data["user"] = aime_id tech_event_data["version"] = version sql = insert(tech_event).values(**tech_event_data) conflict = sql.on_duplicate_key_update(**tech_event_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_tech_event: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def put_tech_event_ranking(self, aime_id: int, version: int, tech_event_data: Dict) -> Optional[int]: + async def put_tech_event_ranking(self, aime_id: int, version: int, tech_event_data: Dict) -> Optional[int]: tech_event_data["user"] = aime_id tech_event_data["version"] = version tech_event_data.pop("isRankingRewarded") @@ -662,87 +662,87 @@ class OngekiItemData(BaseData): sql = insert(tech_ranking).values(**tech_event_data) conflict = sql.on_duplicate_key_update(**tech_event_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_tech_event_ranking: Failed to update ranking! aime_id {aime_id}") return None return result.lastrowid - def get_tech_event(self, version: int, aime_id: int) -> Optional[List[Dict]]: + async def get_tech_event(self, version: int, aime_id: int) -> Optional[List[Dict]]: sql = select(tech_event).where(and_(tech_event.c.user == aime_id, tech_event.c.version == version)) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_bosses(self, aime_id: int) -> Optional[List[Dict]]: + async def get_bosses(self, aime_id: int) -> Optional[List[Dict]]: sql = select(boss).where(boss.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_memorychapter( + async def put_memorychapter( self, aime_id: int, memorychapter_data: Dict ) -> Optional[int]: memorychapter_data["user"] = aime_id sql = insert(memorychapter).values(**memorychapter_data) conflict = sql.on_duplicate_key_update(**memorychapter_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_memorychapter: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def get_memorychapters(self, aime_id: int) -> Optional[List[Dict]]: + async def get_memorychapters(self, aime_id: int) -> Optional[List[Dict]]: sql = select(memorychapter).where(memorychapter.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_user_gacha(self, aime_id: int, gacha_id: int) -> Optional[Row]: + async def get_user_gacha(self, aime_id: int, gacha_id: int) -> Optional[Row]: sql = gacha.select(and_(gacha.c.user == aime_id, gacha.c.gachaId == gacha_id)) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_user_gachas(self, aime_id: int) -> Optional[List[Row]]: + async def get_user_gachas(self, aime_id: int) -> Optional[List[Row]]: sql = gacha.select(gacha.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_user_gacha_supplies(self, aime_id: int) -> Optional[List[Row]]: + async def get_user_gacha_supplies(self, aime_id: int) -> Optional[List[Row]]: sql = gacha_supply.select(gacha_supply.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_user_gacha(self, aime_id: int, gacha_id: int, **data) -> Optional[int]: + async def put_user_gacha(self, aime_id: int, gacha_id: int, **data) -> Optional[int]: sql = insert(gacha).values(user=aime_id, gachaId=gacha_id, **data) conflict = sql.on_duplicate_key_update(user=aime_id, gachaId=gacha_id, **data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_user_gacha: Failed to insert! aime_id: {aime_id}") return None return result.lastrowid - def put_user_print_detail( + async def put_user_print_detail( self, aime_id: int, serial_id: str, user_print_data: Dict ) -> Optional[int]: sql = insert(print_detail).values( @@ -750,7 +750,7 @@ class OngekiItemData(BaseData): ) conflict = sql.on_duplicate_key_update(user=aime_id, **user_print_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( @@ -760,18 +760,18 @@ class OngekiItemData(BaseData): return result.lastrowid - def get_ranking_event_ranks(self, version: int, aime_id: int) -> Optional[List[Dict]]: + async def get_ranking_event_ranks(self, version: int, aime_id: int) -> Optional[List[Dict]]: # Calculates player rank on GameRequest from server, and sends it back, official spec would rank players in maintenance period, on TODO list sql = select(event_point.c.id, event_point.c.user, event_point.c.eventId, event_point.c.type, func.row_number().over(partition_by=event_point.c.eventId, order_by=event_point.c.point.desc()).label('rank'), event_point.c.date, event_point.c.point).where(event_point.c.version == version) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error(f"failed to rank aime_id: {aime_id} ranking event positions") return None return result.fetchall() - def get_tech_event_ranking(self, version: int, aime_id: int) -> Optional[List[Dict]]: + async def get_tech_event_ranking(self, version: int, aime_id: int) -> Optional[List[Dict]]: sql = select(tech_ranking.c.id, tech_ranking.c.user, tech_ranking.c.date, tech_ranking.c.eventId, func.row_number().over(partition_by=tech_ranking.c.eventId, order_by=[tech_ranking.c.totalTechScore.desc(),tech_ranking.c.totalPlatinumScore.desc()]).label('rank'), tech_ranking.c.totalTechScore, tech_ranking.c.totalPlatinumScore).where(tech_ranking.c.version == version) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.warning(f"aime_id: {aime_id} has no tech ranking ranks") return None diff --git a/titles/ongeki/schema/log.py b/titles/ongeki/schema/log.py index bd5b071..fccd29f 100644 --- a/titles/ongeki/schema/log.py +++ b/titles/ongeki/schema/log.py @@ -39,7 +39,7 @@ session_log = Table( class OngekiLogData(BaseData): - def put_gp_log( + async def put_gp_log( self, aime_id: Optional[int], used_credit: int, @@ -61,7 +61,7 @@ class OngekiLogData(BaseData): currentGP=current_gp, ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.warning( f"put_gp_log: Failed to insert GP log! aime_id: {aime_id} kind {kind} pattern {pattern} current_gp {current_gp}" diff --git a/titles/ongeki/schema/profile.py b/titles/ongeki/schema/profile.py index 6071bad..1f6bcab 100644 --- a/titles/ongeki/schema/profile.py +++ b/titles/ongeki/schema/profile.py @@ -255,12 +255,12 @@ class OngekiProfileData(BaseData): ) self.date_time_format_short = "%Y-%m-%d" - def get_profile_name(self, aime_id: int, version: int) -> Optional[str]: + async def get_profile_name(self, aime_id: int, version: int) -> Optional[str]: sql = select(profile.c.userName).where( and_(profile.c.user == aime_id, profile.c.version == version) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None @@ -270,19 +270,19 @@ class OngekiProfileData(BaseData): return row["userName"] - def get_profile_preview(self, aime_id: int, version: int) -> Optional[Row]: + async def get_profile_preview(self, aime_id: int, version: int) -> Optional[Row]: sql = ( select([profile, option]) .join(option, profile.c.user == option.c.user) .filter(and_(profile.c.user == aime_id, profile.c.version == version)) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_profile_data(self, aime_id: int, version: int) -> Optional[Row]: + async def get_profile_data(self, aime_id: int, version: int) -> Optional[Row]: sql = select(profile).where( and_( profile.c.user == aime_id, @@ -290,40 +290,40 @@ class OngekiProfileData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_profile_options(self, aime_id: int) -> Optional[Row]: + async def get_profile_options(self, aime_id: int) -> Optional[Row]: sql = select(option).where( and_( option.c.user == aime_id, ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_profile_recent_rating(self, aime_id: int) -> Optional[List[Row]]: + async def get_profile_recent_rating(self, aime_id: int) -> Optional[List[Row]]: sql = select(recent_rating).where(recent_rating.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_profile_rating_log(self, aime_id: int) -> Optional[List[Row]]: + async def get_profile_rating_log(self, aime_id: int) -> Optional[List[Row]]: sql = select(rating_log).where(rating_log.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_profile_activity( + async def get_profile_activity( self, aime_id: int, kind: int = None ) -> Optional[List[Row]]: sql = select(activity).where( @@ -333,47 +333,47 @@ class OngekiProfileData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_kop(self, aime_id: int) -> Optional[List[Row]]: + async def get_kop(self, aime_id: int) -> Optional[List[Row]]: sql = select(kop).where(kop.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_rivals(self, aime_id: int) -> Optional[List[Row]]: + async def get_rivals(self, aime_id: int) -> Optional[List[Row]]: sql = select(rival.c.rivalUserId).where(rival.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_profile_data(self, aime_id: int, version: int, data: Dict) -> Optional[int]: + async def put_profile_data(self, aime_id: int, version: int, data: Dict) -> Optional[int]: data["user"] = aime_id data["version"] = version data.pop("accessCode") sql = insert(profile).values(**data) conflict = sql.on_duplicate_key_update(**data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_profile_data: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def put_profile_options(self, aime_id: int, options_data: Dict) -> Optional[int]: + async def put_profile_options(self, aime_id: int, options_data: Dict) -> Optional[int]: options_data["user"] = aime_id sql = insert(option).values(**options_data) conflict = sql.on_duplicate_key_update(**options_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( @@ -382,7 +382,7 @@ class OngekiProfileData(BaseData): return None return result.lastrowid - def put_profile_recent_rating( + async def put_profile_recent_rating( self, aime_id: int, recent_rating_data: List[Dict] ) -> Optional[int]: sql = insert(recent_rating).values( @@ -391,7 +391,7 @@ class OngekiProfileData(BaseData): conflict = sql.on_duplicate_key_update(recentRating=recent_rating_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_profile_recent_rating: failed to update recent rating! aime_id {aime_id}" @@ -399,12 +399,12 @@ class OngekiProfileData(BaseData): return None return result.lastrowid - def put_profile_bp_list( + async def put_profile_bp_list( self, aime_id: int, bp_base_list: List[Dict] ) -> Optional[int]: pass - def put_profile_rating_log( + async def put_profile_rating_log( self, aime_id: int, data_version: str, highest_rating: int ) -> Optional[int]: sql = insert(rating_log).values( @@ -413,7 +413,7 @@ class OngekiProfileData(BaseData): conflict = sql.on_duplicate_key_update(highestRating=highest_rating) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_profile_rating_log: failed to update rating log! aime_id {aime_id} data_version {data_version} highest_rating {highest_rating}" @@ -421,7 +421,7 @@ class OngekiProfileData(BaseData): return None return result.lastrowid - def put_profile_activity( + async def put_profile_activity( self, aime_id: int, kind: int, @@ -447,7 +447,7 @@ class OngekiProfileData(BaseData): sortNumber=sort_num, param1=p1, param2=p2, param3=p3, param4=p4 ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_profile_activity: failed to put activity! aime_id {aime_id} kind {kind} activity_id {activity_id}" @@ -455,7 +455,7 @@ class OngekiProfileData(BaseData): return None return result.lastrowid - def put_profile_region(self, aime_id: int, region: int, date: str) -> Optional[int]: + async def put_profile_region(self, aime_id: int, region: int, date: str) -> Optional[int]: sql = insert(activity).values( user=aime_id, region=region, playCount=1, created=date ) @@ -464,7 +464,7 @@ class OngekiProfileData(BaseData): playCount=activity.c.playCount + 1, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_profile_region: failed to update! aime_id {aime_id} region {region}" @@ -472,45 +472,45 @@ class OngekiProfileData(BaseData): return None return result.lastrowid - def put_training_room(self, aime_id: int, room_detail: Dict) -> Optional[int]: + async def put_training_room(self, aime_id: int, room_detail: Dict) -> Optional[int]: room_detail["user"] = aime_id sql = insert(training_room).values(**room_detail) conflict = sql.on_duplicate_key_update(**room_detail) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_best_score: Failed to add score! aime_id: {aime_id}") return None return result.lastrowid - def put_kop(self, aime_id: int, kop_data: Dict) -> Optional[int]: + async def put_kop(self, aime_id: int, kop_data: Dict) -> Optional[int]: kop_data["user"] = aime_id sql = insert(kop).values(**kop_data) conflict = sql.on_duplicate_key_update(**kop_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_kop: Failed to add score! aime_id: {aime_id}") return None return result.lastrowid - def put_rival(self, aime_id: int, rival_id: int) -> Optional[int]: + async def put_rival(self, aime_id: int, rival_id: int) -> Optional[int]: sql = insert(rival).values(user=aime_id, rivalUserId=rival_id) conflict = sql.on_duplicate_key_update(rivalUserId=rival_id) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_rival: failed to update! aime_id: {aime_id}, rival_id: {rival_id}" ) return None return result.lastrowid - def delete_rival(self, aime_id: int, rival_id: int) -> Optional[int]: + async def delete_rival(self, aime_id: int, rival_id: int) -> Optional[int]: sql = delete(rival).where(rival.c.user==aime_id, rival.c.rivalUserId==rival_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error(f"delete_rival: failed to delete! aime_id: {aime_id}, rival_id: {rival_id}") else: diff --git a/titles/ongeki/schema/score.py b/titles/ongeki/schema/score.py index 7c8ce15..4770725 100644 --- a/titles/ongeki/schema/score.py +++ b/titles/ongeki/schema/score.py @@ -128,52 +128,52 @@ tech_count = Table( class OngekiScoreData(BaseData): - def get_tech_count(self, aime_id: int) -> Optional[List[Dict]]: + async def get_tech_count(self, aime_id: int) -> Optional[List[Dict]]: return [] - def put_tech_count(self, aime_id: int, tech_count_data: Dict) -> Optional[int]: + async def put_tech_count(self, aime_id: int, tech_count_data: Dict) -> Optional[int]: tech_count_data["user"] = aime_id sql = insert(tech_count).values(**tech_count_data) conflict = sql.on_duplicate_key_update(**tech_count_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_tech_count: Failed to update! aime_id: {aime_id}") return None return result.lastrowid - def get_best_scores(self, aime_id: int) -> Optional[List[Dict]]: + async def get_best_scores(self, aime_id: int) -> Optional[List[Dict]]: sql = select(score_best).where(score_best.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_best_score( + async def get_best_score( self, aime_id: int, song_id: int, chart_id: int = None ) -> Optional[List[Dict]]: return [] - def put_best_score(self, aime_id: int, music_detail: Dict) -> Optional[int]: + async def put_best_score(self, aime_id: int, music_detail: Dict) -> Optional[int]: music_detail["user"] = aime_id sql = insert(score_best).values(**music_detail) conflict = sql.on_duplicate_key_update(**music_detail) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"put_best_score: Failed to add score! aime_id: {aime_id}") return None return result.lastrowid - def put_playlog(self, aime_id: int, playlog_data: Dict) -> Optional[int]: + async def put_playlog(self, aime_id: int, playlog_data: Dict) -> Optional[int]: playlog_data["user"] = aime_id sql = insert(playlog).values(**playlog_data) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.warning(f"put_playlog: Failed to add playlog! aime_id: {aime_id}") return None diff --git a/titles/ongeki/schema/static.py b/titles/ongeki/schema/static.py index 695d39a..85a9df4 100644 --- a/titles/ongeki/schema/static.py +++ b/titles/ongeki/schema/static.py @@ -188,26 +188,26 @@ game_point = Table( ) class OngekiStaticData(BaseData): - def put_card(self, version: int, card_id: int, **card_data) -> Optional[int]: + async def put_card(self, version: int, card_id: int, **card_data) -> Optional[int]: sql = insert(cards).values(version=version, cardId=card_id, **card_data) conflict = sql.on_duplicate_key_update(**card_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"Failed to insert card! card_id {card_id}") return None return result.lastrowid - def get_card(self, version: int, card_id: int) -> Optional[Dict]: + async def get_card(self, version: int, card_id: int) -> Optional[Dict]: sql = cards.select(and_(cards.c.version <= version, cards.c.cardId == card_id)) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_card_by_card_number(self, version: int, card_number: str) -> Optional[Dict]: + async def get_card_by_card_number(self, version: int, card_number: str) -> Optional[Dict]: if not card_number.startswith("[O.N.G.E.K.I.]"): card_number = f"[O.N.G.E.K.I.]{card_number}" @@ -215,36 +215,36 @@ class OngekiStaticData(BaseData): and_(cards.c.version <= version, cards.c.cardNumber == card_number) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_card_by_name(self, version: int, name: str) -> Optional[Dict]: + async def get_card_by_name(self, version: int, name: str) -> Optional[Dict]: sql = cards.select(and_(cards.c.version <= version, cards.c.name == name)) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_cards(self, version: int) -> Optional[List[Dict]]: + async def get_cards(self, version: int) -> Optional[List[Dict]]: sql = cards.select(cards.c.version <= version) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_cards_by_rarity(self, version: int, rarity: int) -> Optional[List[Dict]]: + async def get_cards_by_rarity(self, version: int, rarity: int) -> Optional[List[Dict]]: sql = cards.select(and_(cards.c.version <= version, cards.c.rarity == rarity)) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_gacha( + async def put_gacha( self, version: int, gacha_id: int, @@ -268,33 +268,33 @@ class OngekiStaticData(BaseData): **gacha_data, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"Failed to insert gacha! gacha_id {gacha_id}") return None return result.lastrowid - def get_gacha(self, version: int, gacha_id: int) -> Optional[Dict]: + async def get_gacha(self, version: int, gacha_id: int) -> Optional[Dict]: sql = gachas.select( and_(gachas.c.version <= version, gachas.c.gachaId == gacha_id) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_gachas(self, version: int) -> Optional[List[Dict]]: + async def get_gachas(self, version: int) -> Optional[List[Dict]]: sql = gachas.select(gachas.c.version == version).order_by( gachas.c.gachaId.asc() ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_gacha_card( + async def put_gacha_card( self, gacha_id: int, card_id: int, **gacha_card ) -> Optional[int]: sql = insert(gacha_cards).values(gachaId=gacha_id, cardId=card_id, **gacha_card) @@ -303,21 +303,21 @@ class OngekiStaticData(BaseData): gachaId=gacha_id, cardId=card_id, **gacha_card ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"Failed to insert gacha card! gacha_id {gacha_id}") return None return result.lastrowid - def get_gacha_cards(self, gacha_id: int) -> Optional[List[Dict]]: + async def get_gacha_cards(self, gacha_id: int) -> Optional[List[Dict]]: sql = gacha_cards.select(gacha_cards.c.gachaId == gacha_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_event( + async def put_event( self, version: int, event_id: int, event_type: int, event_name: str ) -> Optional[int]: sql = insert(events).values( @@ -332,41 +332,41 @@ class OngekiStaticData(BaseData): name=event_name, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"Failed to insert event! event_id {event_id}") return None return result.lastrowid - def get_event(self, version: int, event_id: int) -> Optional[List[Dict]]: + async def get_event(self, version: int, event_id: int) -> Optional[List[Dict]]: sql = select(events).where( and_(events.c.version == version, events.c.eventId == event_id) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_events(self, version: int) -> Optional[List[Dict]]: + async def get_events(self, version: int) -> Optional[List[Dict]]: sql = select(events).where(events.c.version == version) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_enabled_events(self, version: int) -> Optional[List[Dict]]: + async def get_enabled_events(self, version: int) -> Optional[List[Dict]]: sql = select(events).where( and_(events.c.version == version, events.c.enabled == True) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_chart( + async def put_chart( self, version: int, song_id: int, @@ -393,7 +393,7 @@ class OngekiStaticData(BaseData): level=level, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"Failed to insert chart! song_id: {song_id}, chart_id: {chart_id}" @@ -401,15 +401,15 @@ class OngekiStaticData(BaseData): return None return result.lastrowid - def get_chart( + async def get_chart( self, version: int, song_id: int, chart_id: int = None ) -> Optional[List[Dict]]: pass - def get_music(self, version: int) -> Optional[List[Dict]]: + async def get_music(self, version: int) -> Optional[List[Dict]]: pass - def get_music_chart( + async def get_music_chart( self, version: int, song_id: int, chart_id: int ) -> Optional[List[Row]]: sql = select(music).where( @@ -420,19 +420,19 @@ class OngekiStaticData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_ranking_list(self, version: int) -> Optional[List[Dict]]: + async def get_ranking_list(self, version: int) -> Optional[List[Dict]]: sql = select(music_ranking.c.musicId.label('id'), music_ranking.c.point, music_ranking.c.userName).where(music_ranking.c.version == version) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_reward(self, version: int, rewardId: int, rewardname: str, itemKind: int, itemId: int) -> Optional[int]: + async def put_reward(self, version: int, rewardId: int, rewardname: str, itemKind: int, itemId: int) -> Optional[int]: sql = insert(rewards).values( version=version, rewardId=rewardId, @@ -443,70 +443,70 @@ class OngekiStaticData(BaseData): conflict = sql.on_duplicate_key_update( rewardname=rewardname, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"Failed to insert reward! reward_id: {rewardId}") return None return result.lastrowid - def get_reward_list(self, version: int) -> Optional[List[Dict]]: + async def get_reward_list(self, version: int) -> Optional[List[Dict]]: sql = select(rewards).where(rewards.c.version == version) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.warning(f"Failed to load reward list") return None return result.fetchall() - def get_present_list(self, version: int) -> Optional[List[Dict]]: + async def get_present_list(self, version: int) -> Optional[List[Dict]]: sql = select(present).where(present.c.version == version) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.warning(f"Failed to load present list") return None return result.fetchall() - def get_tech_music(self, version: int) -> Optional[List[Dict]]: + async def get_tech_music(self, version: int) -> Optional[List[Dict]]: sql = select(tech_music).where(tech_music.c.version == version) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_client_testmode_data(self, region_id: int, client_testmode_data: Dict) -> Optional[List[Dict]]: + async def put_client_testmode_data(self, region_id: int, client_testmode_data: Dict) -> Optional[List[Dict]]: sql = insert(client_testmode).values(regionId=region_id, **client_testmode_data) conflict = sql.on_duplicate_key_update(regionId=region_id, **client_testmode_data) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"region_id: {region_id} Failed to update ClientTestMode data"), return None return result.lastrowid - def put_client_setting_data(self, machine_id: int, client_setting_data: Dict) -> Optional[List[Dict]]: + async def put_client_setting_data(self, machine_id: int, client_setting_data: Dict) -> Optional[List[Dict]]: sql = machine.update(machine.c.id == machine_id).values(data=client_setting_data) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.warning(f"machine_id: {machine_id} Failed to update ClientSetting data"), return None return result.lastrowid - def put_static_game_point_defaults(self) -> Optional[List[Dict]]: + async def put_static_game_point_defaults(self) -> Optional[List[Dict]]: game_point_defaults = [{"type": 0, "cost": 100},{"type": 1, "cost": 230},{"type": 2, "cost": 370},{"type": 3, "cost": 120},{"type": 4, "cost": 240},{"type": 5, "cost": 360}] sql = insert(game_point).values(game_point_defaults) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.warning(f"Failed to insert default GP table!") return None return result.lastrowid - def get_static_game_point(self) -> Optional[List[Dict]]: + async def get_static_game_point(self) -> Optional[List[Dict]]: sql = select(game_point.c.type, game_point.c.cost, game_point.c.startDate, game_point.c.endDate) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() diff --git a/titles/pokken/base.py b/titles/pokken/base.py index 9eab298..562f3b9 100644 --- a/titles/pokken/base.py +++ b/titles/pokken/base.py @@ -132,11 +132,11 @@ class PokkenBase: res.type = jackal_pb2.MessageType.LOAD_USER access_code = request.load_user.access_code load_usr = jackal_pb2.LoadUserResponseData() - user_id = self.data.card.get_user_id_from_card(access_code) + user_id = await self.data.card.get_user_id_from_card(access_code) if user_id is None and self.game_cfg.server.auto_register: - user_id = self.data.user.create_user() - card_id = self.data.card.create_card(user_id, access_code) + user_id = await self.data.user.create_user() + card_id = await self.data.card.create_card(user_id, access_code) self.logger.info( f"Register new card {access_code} (UserId {user_id}, CardId {card_id})" @@ -160,7 +160,7 @@ class PokkenBase: event_achievement_flag event_achievement_param """ - profile = self.data.profile.get_profile(user_id) + profile = await self.data.profile.get_profile(user_id) load_usr.commidserv_result = 1 load_usr.load_hash = 1 load_usr.cardlock_status = False @@ -169,7 +169,7 @@ class PokkenBase: load_usr.precedent_release_flag = 0xFFFFFFFF if profile is None: - profile_id = self.data.profile.create_profile(user_id) + profile_id = await self.data.profile.create_profile(user_id) profile_dict = {"id": profile_id, "user": user_id} pokemon_data = [] tutorial_progress = [] @@ -184,7 +184,7 @@ class PokkenBase: self.logger.info( f"Card-in user {user_id} (Trainer name {profile_dict.get('trainer_name', '')})" ) - pokemon_data = self.data.profile.get_all_pokemon_data(user_id) + pokemon_data = await self.data.profile.get_all_pokemon_data(user_id) tutorial_progress = [] rankmatch_progress = [] achievement_flag = [] @@ -324,22 +324,22 @@ class PokkenBase: battle = req.battle_data mon = req.pokemon_data - p = self.data.profile.touch_profile(user_id) + p = await self.data.profile.touch_profile(user_id) if p is None or not p: - self.data.profile.create_profile(user_id) + await self.data.profile.create_profile(user_id) if req.trainer_name_pending is not None and req.trainer_name_pending: # we're saving for the first time - self.data.profile.set_profile_name(user_id, req.trainer_name_pending, req.avatar_gender if req.avatar_gender else None) + await self.data.profile.set_profile_name(user_id, req.trainer_name_pending, req.avatar_gender if req.avatar_gender else None) for tut_flg in req.tutorial_progress_flag: tut_flgs.append(tut_flg) - self.data.profile.update_profile_tutorial_flags(user_id, tut_flgs) + await self.data.profile.update_profile_tutorial_flags(user_id, tut_flgs) for ach_flg in req.achievement_flag: ach_flgs.append(ach_flg) - self.data.profile.update_profile_tutorial_flags(user_id, ach_flg) + await self.data.profile.update_profile_tutorial_flags(user_id, ach_flg) for evt_flg in req.event_achievement_flag: evt_flgs.append(evt_flg) @@ -347,29 +347,29 @@ class PokkenBase: for evt_param in req.event_achievement_param: evt_params.append(evt_param) - self.data.profile.update_profile_event(user_id, evt_state, evt_flgs, evt_params, req.last_play_event_id) + await self.data.profile.update_profile_event(user_id, evt_state, evt_flgs, evt_params, req.last_play_event_id) for reward in req.reward_data: - self.data.item.add_reward(user_id, reward.get_category_id, reward.get_content_id, reward.get_type_id) + await self.data.item.add_reward(user_id, reward.get_category_id, reward.get_content_id, reward.get_type_id) - self.data.profile.add_profile_points(user_id, get_rank_pts, get_money, get_score_pts, grade_max) + await self.data.profile.add_profile_points(user_id, get_rank_pts, get_money, get_score_pts, grade_max) - self.data.profile.update_support_team(user_id, 1, req.support_set_1[0], req.support_set_1[1]) - self.data.profile.update_support_team(user_id, 2, req.support_set_2[0], req.support_set_2[1]) - self.data.profile.update_support_team(user_id, 3, req.support_set_3[0], req.support_set_3[1]) + await self.data.profile.update_support_team(user_id, 1, req.support_set_1[0], req.support_set_1[1]) + await self.data.profile.update_support_team(user_id, 2, req.support_set_2[0], req.support_set_2[1]) + await self.data.profile.update_support_team(user_id, 3, req.support_set_3[0], req.support_set_3[1]) - self.data.profile.put_pokemon(user_id, mon.char_id, mon.illustration_book_no, mon.bp_point_atk, mon.bp_point_res, mon.bp_point_def, mon.bp_point_sp) - self.data.profile.add_pokemon_xp(user_id, mon.char_id, mon.get_pokemon_exp) + await self.data.profile.put_pokemon(user_id, mon.char_id, mon.illustration_book_no, mon.bp_point_atk, mon.bp_point_res, mon.bp_point_def, mon.bp_point_sp) + await self.data.profile.add_pokemon_xp(user_id, mon.char_id, mon.get_pokemon_exp) for x in range(len(battle.play_mode)): - self.data.profile.put_pokemon_battle_result( + await self.data.profile.put_pokemon_battle_result( user_id, mon.char_id, PokkenConstants.BATTLE_TYPE(battle.play_mode[x]), PokkenConstants.BATTLE_RESULT(battle.result[x]) ) - self.data.profile.put_stats( + await self.data.profile.put_stats( user_id, battle.ex_ko_num, battle.wko_num, @@ -379,7 +379,7 @@ class PokkenBase: num_continues ) - self.data.profile.put_extra( + await self.data.profile.put_extra( user_id, extra_counter, evt_reward_get_flg, diff --git a/titles/pokken/schema/item.py b/titles/pokken/schema/item.py index f68d6d9..cc633d7 100644 --- a/titles/pokken/schema/item.py +++ b/titles/pokken/schema/item.py @@ -31,7 +31,7 @@ class PokkenItemData(BaseData): Items obtained as rewards """ - def add_reward(self, user_id: int, category: int, content: int, item_type: int) -> Optional[int]: + async def add_reward(self, user_id: int, category: int, content: int, item_type: int) -> Optional[int]: sql = insert(item).values( user=user_id, category=category, @@ -43,7 +43,7 @@ class PokkenItemData(BaseData): content=content, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"Failed to insert reward for user {user_id}: {category}-{content}-{item_type}") return None diff --git a/titles/pokken/schema/match.py b/titles/pokken/schema/match.py index c84ec63..1c597d5 100644 --- a/titles/pokken/schema/match.py +++ b/titles/pokken/schema/match.py @@ -39,14 +39,14 @@ class PokkenMatchData(BaseData): Match logs """ - def save_match(self, user_id: int, match_data: Dict) -> Optional[int]: + async def save_match(self, user_id: int, match_data: Dict) -> Optional[int]: pass - def get_match(self, match_id: int) -> Optional[Row]: + async def get_match(self, match_id: int) -> Optional[Row]: pass - def get_matches_by_user(self, user_id: int) -> Optional[List[Row]]: + async def get_matches_by_user(self, user_id: int) -> Optional[List[Row]]: pass - def get_matches(self, limit: int = 20) -> Optional[List[Row]]: + async def get_matches(self, limit: int = 20) -> Optional[List[Row]]: pass diff --git a/titles/pokken/schema/profile.py b/titles/pokken/schema/profile.py index ab81d77..b7237de 100644 --- a/titles/pokken/schema/profile.py +++ b/titles/pokken/schema/profile.py @@ -138,36 +138,36 @@ pokemon_data = Table( class PokkenProfileData(BaseData): - def touch_profile(self, user_id: int) -> Optional[int]: + async def touch_profile(self, user_id: int) -> Optional[int]: sql = select([profile.c.id]).where(profile.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone()['id'] - def create_profile(self, user_id: int) -> Optional[int]: + async def create_profile(self, user_id: int) -> Optional[int]: sql = insert(profile).values(user=user_id) conflict = sql.on_duplicate_key_update(user=user_id) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error(f"Failed to create pokken profile for user {user_id}!") return None return result.lastrowid - def set_profile_name(self, user_id: int, new_name: str, gender: Union[int, None] = None) -> None: + async def set_profile_name(self, user_id: int, new_name: str, gender: Union[int, None] = None) -> None: sql = update(profile).where(profile.c.user == user_id).values( trainer_name=new_name, avatar_gender=gender if gender is not None else profile.c.avatar_gender ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"Failed to update pokken profile name for user {user_id}!" ) - def put_extra( + async def put_extra( self, user_id: int, extra_counter: int, @@ -190,44 +190,44 @@ class PokkenProfileData(BaseData): last_play_event_id=last_evt ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error(f"Failed to put extra data for user {user_id}") - def update_profile_tutorial_flags(self, user_id: int, tutorial_flags: List) -> None: + async def update_profile_tutorial_flags(self, user_id: int, tutorial_flags: List) -> None: sql = update(profile).where(profile.c.user == user_id).values( tutorial_progress_flag=tutorial_flags, ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"Failed to update pokken profile tutorial flags for user {user_id}!" ) - def update_profile_achievement_flags(self, user_id: int, achievement_flags: List) -> None: + async def update_profile_achievement_flags(self, user_id: int, achievement_flags: List) -> None: sql = update(profile).where(profile.c.user == user_id).values( achievement_flag=achievement_flags, ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"Failed to update pokken profile achievement flags for user {user_id}!" ) - def update_profile_event(self, user_id: int, event_state: List, event_flags: List[int], event_param: List[int], last_evt: int = None) -> None: + async def update_profile_event(self, user_id: int, event_state: List, event_flags: List[int], event_param: List[int], last_evt: int = None) -> None: sql = update(profile).where(profile.c.user == user_id).values( event_state=event_state, event_achievement_flag=event_flags, event_achievement_param=event_param, last_play_event_id=last_evt if last_evt is not None else profile.c.last_play_event_id, ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"Failed to update pokken profile event state for user {user_id}!" ) - def add_profile_points( + async def add_profile_points( self, user_id: int, rank_pts: int, money: int, score_pts: int, grade_max: int ) -> None: sql = update(profile).where(profile.c.user == user_id).values( @@ -237,14 +237,14 @@ class PokkenProfileData(BaseData): grade_max_num = grade_max ) - def get_profile(self, user_id: int) -> Optional[Row]: + async def get_profile(self, user_id: int) -> Optional[Row]: sql = profile.select(profile.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def put_pokemon( + async def put_pokemon( self, user_id: int, pokemon_id: int, @@ -281,13 +281,13 @@ class PokkenProfileData(BaseData): bp_point_sp=pokemon_data.c.bp_point_sp + sp, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"Failed to insert pokemon ID {pokemon_id} for user {user_id}") return None return result.lastrowid - def add_pokemon_xp( + async def add_pokemon_xp( self, user_id: int, pokemon_id: int, @@ -297,25 +297,25 @@ class PokkenProfileData(BaseData): pokemon_exp=coalesce(pokemon_data.c.pokemon_exp, 0) + xp ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.warning(f"Failed to add {xp} XP to pokemon ID {pokemon_id} for user {user_id}") - def get_pokemon_data(self, user_id: int, pokemon_id: int) -> Optional[Row]: + async def get_pokemon_data(self, user_id: int, pokemon_id: int) -> Optional[Row]: sql = pokemon_data.select(and_(pokemon_data.c.user == user_id, pokemon_data.c.char_id == pokemon_id)) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_all_pokemon_data(self, user_id: int) -> Optional[List[Row]]: + async def get_all_pokemon_data(self, user_id: int) -> Optional[List[Row]]: sql = pokemon_data.select(pokemon_data.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def put_pokemon_battle_result( + async def put_pokemon_battle_result( self, user_id: int, pokemon_id: int, match_type: PokkenConstants.BATTLE_TYPE, match_result: PokkenConstants.BATTLE_RESULT ) -> None: """ @@ -336,11 +336,11 @@ class PokkenProfileData(BaseData): win_vs_wan=coalesce(pokemon_data.c.win_vs_wan, 0) + 1 if match_type==PokkenConstants.BATTLE_TYPE.WAN and match_result==PokkenConstants.BATTLE_RESULT.WIN else coalesce(pokemon_data.c.win_vs_wan, 0), ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.warning(f"Failed to record match stats for user {user_id}'s pokemon {pokemon_id} (type {match_type.name} | result {match_result.name})") - def put_stats( + async def put_stats( self, user_id: int, exkos: int, @@ -362,11 +362,11 @@ class PokkenProfileData(BaseData): continue_num=continues, ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.warning(f"Failed to update stats for user {user_id}") - def update_support_team(self, user_id: int, support_id: int, support1: int = None, support2: int = None) -> None: + async def update_support_team(self, user_id: int, support_id: int, support1: int = None, support2: int = None) -> None: sql = update(profile).where(profile.c.user==user_id).values( support_set_1_1=support1 if support_id == 1 else profile.c.support_set_1_1, support_set_1_2=support2 if support_id == 1 else profile.c.support_set_1_2, @@ -376,6 +376,6 @@ class PokkenProfileData(BaseData): support_set_3_2=support2 if support_id == 3 else profile.c.support_set_3_2, ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.warning(f"Failed to update support team {support_id} for user {user_id}") diff --git a/titles/sao/handlers/base.py b/titles/sao/handlers/base.py index 4178640..5b63f78 100644 --- a/titles/sao/handlers/base.py +++ b/titles/sao/handlers/base.py @@ -16,7 +16,7 @@ class SaoRequestHeader: self.game_id: int = collection[4] self.version_id: int = collection[5] self.hash: str = collection[6] - self.data_len: str = collection[7] + self.data._len: str = collection[7] class SaoBaseRequest: def __init__(self, header: SaoRequestHeader, data: bytes) -> None: @@ -118,7 +118,7 @@ class SaoMasterDataVersionCheckResponse(SaoBaseResponse): super().__init__(cmd) self.result = 1 self.update_flag = 0 - self.data_version = 100 + self.data._version = 100 def make(self) -> bytes: # create a resp struct @@ -131,7 +131,7 @@ class SaoMasterDataVersionCheckResponse(SaoBaseResponse): resp_data = resp_struct.build(dict( result=self.result, update_flag=self.update_flag, - data_version=self.data_version, + data_version=self.data._version, )) self.length = len(resp_data) @@ -145,7 +145,7 @@ class SaoCommonGetAppVersionsRequest(SaoBaseResponse): def __init__(self, cmd) -> None: super().__init__(cmd) self.result = 1 - self.data_list_size = 1 # Number of arrays + self.data._list_size = 1 # Number of arrays self.version_app_id = 1 self.applying_start_date = "20230520193000" @@ -163,7 +163,7 @@ class SaoCommonGetAppVersionsRequest(SaoBaseResponse): resp_data = resp_struct.build(dict( result=self.result, - data_list_size=self.data_list_size, + data_list_size=self.data._list_size, version_app_id=self.version_app_id, applying_start_date_size=len(self.applying_start_date) * 2, @@ -2121,7 +2121,7 @@ class SaoGetYuiMedalBonusUserDataResponse(SaoBaseResponse): def __init__(self, cmd) -> None: super().__init__(cmd) self.result = 1 - self.data_size = 1 # number of arrays + self.data._size = 1 # number of arrays self.elapsed_days = 1 self.loop_num = 1 @@ -2144,7 +2144,7 @@ class SaoGetYuiMedalBonusUserDataResponse(SaoBaseResponse): resp_data = resp_struct.build(dict( result=self.result, - data_size=self.data_size, + data_size=self.data._size, elapsed_days=self.elapsed_days, loop_num=self.loop_num, @@ -3150,11 +3150,11 @@ class GetGashaMedalShopUserDataListResponse(SaoBaseResponse): def __init__(self, cmd_id: int) -> None: super().__init__(cmd_id) self.result = 1 # byte - self.data_list: List[GashaMedalShopUserData] = [] + self.data._list: List[GashaMedalShopUserData] = [] def make(self) -> bytes: ret = encode_byte(self.result) - ret += encode_arr_cls(self.data_list) + ret += encode_arr_cls(self.data._list) self.header.length = len(ret) return super().make() + ret @@ -3168,11 +3168,11 @@ class GetMYuiMedalShopDataResponse(SaoBaseResponse): def __init__(self, cmd_id: int) -> None: super().__init__(cmd_id) self.result = 1 # byte - self.data_list: List[YuiMedalShopData] = [] + self.data._list: List[YuiMedalShopData] = [] def make(self) -> bytes: ret = encode_byte(self.result) - ret += encode_arr_cls(self.data_list) + ret += encode_arr_cls(self.data._list) self.header.length = len(ret) return super().make() + ret @@ -3186,11 +3186,11 @@ class GetMYuiMedalShopItemsResponse(SaoBaseResponse): def __init__(self, cmd_id: int) -> None: super().__init__(cmd_id) self.result = 1 # byte - self.data_list: List[YuiMedalShopItemData] = [] + self.data._list: List[YuiMedalShopItemData] = [] def make(self) -> bytes: ret = encode_byte(self.result) - ret += encode_arr_cls(self.data_list) + ret += encode_arr_cls(self.data._list) self.header.length = len(ret) return super().make() + ret @@ -3204,11 +3204,11 @@ class GetMGashaMedalShopsResponse(SaoBaseResponse): def __init__(self, cmd_id: int) -> None: super().__init__(cmd_id) self.result = 1 # byte - self.data_list: List[GashaMedalShop] = [] + self.data._list: List[GashaMedalShop] = [] def make(self) -> bytes: ret = encode_byte(self.result) - ret += encode_arr_cls(self.data_list) + ret += encode_arr_cls(self.data._list) self.header.length = len(ret) return super().make() + ret @@ -3222,11 +3222,11 @@ class GetMResEarnCampaignShopsResponse(SaoBaseResponse): def __init__(self, cmd_id: int) -> None: super().__init__(cmd_id) self.result = 1 # byte - self.data_list: List[ResEarnCampaignShop] = [] + self.data._list: List[ResEarnCampaignShop] = [] def make(self) -> bytes: ret = encode_byte(self.result) - ret += encode_arr_cls(self.data_list) + ret += encode_arr_cls(self.data._list) self.header.length = len(ret) return super().make() + ret diff --git a/titles/sao/read.py b/titles/sao/read.py index dafb450..92aad8c 100644 --- a/titles/sao/read.py +++ b/titles/sao/read.py @@ -29,17 +29,15 @@ class SaoReader(BaseReader): self.logger.error(f"Invalid project SAO version {version}") exit(1) - def read(self) -> None: - pull_bin_ram = True + async def read(self) -> None: + if path.exists(self.bin_dir): + await self.read_csv(f"{self.bin_dir}") - if not path.exists(f"{self.bin_dir}"): - self.logger.warning(f"Couldn't find csv file in {self.bin_dir}, skipping") - pull_bin_ram = False + else: + self.logger.warn("Directory not found, nothing to import") + - if pull_bin_ram: - self.read_csv(f"{self.bin_dir}") - - def read_csv(self, bin_dir: str) -> None: + async def read_csv(self, bin_dir: str) -> None: self.logger.info(f"Read csv from {bin_dir}") self.logger.info("Now reading QuestScene.csv") @@ -56,7 +54,7 @@ class SaoReader(BaseReader): self.logger.info(f"Added quest {questSceneId} | Name: {name}") try: - self.data.static.put_quest( + await self.data.static.put_quest( questSceneId, 0, sortNo, @@ -86,7 +84,7 @@ class SaoReader(BaseReader): self.logger.info(f"Added hero {heroLogId} | Name: {name}") try: - self.data.static.put_hero( + await self.data.static.put_hero( 0, heroLogId, name, @@ -119,7 +117,7 @@ class SaoReader(BaseReader): self.logger.info(f"Added equipment {equipmentId} | Name: {name}") try: - self.data.static.put_equipment( + await self.data.static.put_equipment( 0, equipmentId, name, @@ -150,7 +148,7 @@ class SaoReader(BaseReader): self.logger.info(f"Added item {itemId} | Name: {name}") try: - self.data.static.put_item( + await self.data.static.put_item( 0, itemId, name, @@ -181,7 +179,7 @@ class SaoReader(BaseReader): self.logger.info(f"Added support log {supportLogId} | Name: {name}") try: - self.data.static.put_support_log( + await self.data.static.put_support_log( 0, supportLogId, charaId, @@ -213,7 +211,7 @@ class SaoReader(BaseReader): if len(titleId) > 5: try: - self.data.static.put_title( + await self.data.static.put_title( 0, titleId, displayName, @@ -242,7 +240,7 @@ class SaoReader(BaseReader): self.logger.info(f"Added rare drop {questRareDropId} | Reward: {commonRewardId}") try: - self.data.static.put_rare_drop( + await self.data.static.put_rare_drop( 0, questRareDropId, commonRewardId, diff --git a/titles/sao/schema/item.py b/titles/sao/schema/item.py index 11adf27..c490d9d 100644 --- a/titles/sao/schema/item.py +++ b/titles/sao/schema/item.py @@ -139,7 +139,7 @@ end_sessions = Table( ) class SaoItemData(BaseData): - def create_session(self, user_id: int, user_party_team_id: int, episode_id: int, play_mode: int, quest_drop_boost_apply_flag: int) -> Optional[int]: + async def create_session(self, user_id: int, user_party_team_id: int, episode_id: int, play_mode: int, quest_drop_boost_apply_flag: int) -> Optional[int]: sql = insert(sessions).values( user=user_id, user_party_team_id=user_party_team_id, @@ -150,13 +150,13 @@ class SaoItemData(BaseData): conflict = sql.on_duplicate_key_update(user=user_id) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error(f"Failed to create SAO session for user {user_id}!") return None return result.lastrowid - def create_end_session(self, user_id: int, quest_id: int, play_result_flag: bool, reward_data: JSON) -> Optional[int]: + async def create_end_session(self, user_id: int, quest_id: int, play_result_flag: bool, reward_data: JSON) -> Optional[int]: sql = insert(end_sessions).values( user=user_id, quest_id=quest_id, @@ -166,13 +166,13 @@ class SaoItemData(BaseData): conflict = sql.on_duplicate_key_update(user=user_id) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error(f"Failed to create SAO end session for user {user_id}!") return None return result.lastrowid - def put_item(self, user_id: int, item_id: int) -> Optional[int]: + async def put_item(self, user_id: int, item_id: int) -> Optional[int]: sql = insert(item_data).values( user=user_id, item_id=item_id, @@ -182,7 +182,7 @@ class SaoItemData(BaseData): item_id=item_id, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"{__name__} failed to insert item! user: {user_id}, item_id: {item_id}" @@ -191,7 +191,7 @@ class SaoItemData(BaseData): return result.lastrowid - def put_equipment_data(self, user_id: int, equipment_id: int, enhancement_value: int, enhancement_exp: int, awakening_exp: int, awakening_stage: int, possible_awakening_flag: int) -> Optional[int]: + async def put_equipment_data(self, user_id: int, equipment_id: int, enhancement_value: int, enhancement_exp: int, awakening_exp: int, awakening_stage: int, possible_awakening_flag: int) -> Optional[int]: sql = insert(equipment_data).values( user=user_id, equipment_id=equipment_id, @@ -210,7 +210,7 @@ class SaoItemData(BaseData): possible_awakening_flag=possible_awakening_flag, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"{__name__} failed to insert equipment! user: {user_id}, equipment_id: {equipment_id}" @@ -219,7 +219,7 @@ class SaoItemData(BaseData): return result.lastrowid - def put_hero_log(self, user_id: int, user_hero_log_id: int, log_level: int, log_exp: int, main_weapon: int, sub_equipment: int, skill_slot1_skill_id: int, skill_slot2_skill_id: int, skill_slot3_skill_id: int, skill_slot4_skill_id: int, skill_slot5_skill_id: int) -> Optional[int]: + async def put_hero_log(self, user_id: int, user_hero_log_id: int, log_level: int, log_exp: int, main_weapon: int, sub_equipment: int, skill_slot1_skill_id: int, skill_slot2_skill_id: int, skill_slot3_skill_id: int, skill_slot4_skill_id: int, skill_slot5_skill_id: int) -> Optional[int]: sql = insert(hero_log_data).values( user=user_id, user_hero_log_id=user_hero_log_id, @@ -246,7 +246,7 @@ class SaoItemData(BaseData): skill_slot5_skill_id=skill_slot5_skill_id, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"{__name__} failed to insert hero! user: {user_id}, user_hero_log_id: {user_hero_log_id}" @@ -255,7 +255,7 @@ class SaoItemData(BaseData): return result.lastrowid - def put_hero_party(self, user_id: int, user_party_team_id: int, user_hero_log_id_1: int, user_hero_log_id_2: int, user_hero_log_id_3: int) -> Optional[int]: + async def put_hero_party(self, user_id: int, user_party_team_id: int, user_hero_log_id_1: int, user_hero_log_id_2: int, user_hero_log_id_3: int) -> Optional[int]: sql = insert(hero_party).values( user=user_id, user_party_team_id=user_party_team_id, @@ -270,7 +270,7 @@ class SaoItemData(BaseData): user_hero_log_id_3=user_hero_log_id_3, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"{__name__} failed to insert hero party! user: {user_id}, user_party_team_id: {user_party_team_id}" @@ -279,7 +279,7 @@ class SaoItemData(BaseData): return result.lastrowid - def put_player_quest(self, user_id: int, episode_id: int, quest_clear_flag: bool, clear_time: int, combo_num: int, total_damage: int, concurrent_destroying_num: int) -> Optional[int]: + async def put_player_quest(self, user_id: int, episode_id: int, quest_clear_flag: bool, clear_time: int, combo_num: int, total_damage: int, concurrent_destroying_num: int) -> Optional[int]: sql = insert(quest).values( user=user_id, episode_id=episode_id, @@ -298,7 +298,7 @@ class SaoItemData(BaseData): concurrent_destroying_num=concurrent_destroying_num ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"{__name__} failed to insert quest! user: {user_id}, episode_id: {episode_id}" @@ -307,15 +307,15 @@ class SaoItemData(BaseData): return result.lastrowid - def get_user_equipment(self, user_id: int, equipment_id: int) -> Optional[Dict]: + async def get_user_equipment(self, user_id: int, equipment_id: int) -> Optional[Dict]: sql = equipment_data.select(equipment_data.c.user == user_id and equipment_data.c.equipment_id == equipment_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_user_equipments( + async def get_user_equipments( self, user_id: int ) -> Optional[List[Row]]: """ @@ -327,12 +327,12 @@ class SaoItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_user_items( + async def get_user_items( self, user_id: int ) -> Optional[List[Row]]: """ @@ -344,12 +344,12 @@ class SaoItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_hero_log( + async def get_hero_log( self, user_id: int, user_hero_log_id: int = None ) -> Optional[List[Row]]: """ @@ -362,12 +362,12 @@ class SaoItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_hero_logs( + async def get_hero_logs( self, user_id: int ) -> Optional[List[Row]]: """ @@ -379,12 +379,12 @@ class SaoItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_hero_party( + async def get_hero_party( self, user_id: int, user_party_team_id: int = None ) -> Optional[List[Row]]: sql = hero_party.select( @@ -394,12 +394,12 @@ class SaoItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_quest_log( + async def get_quest_log( self, user_id: int, episode_id: int = None ) -> Optional[List[Row]]: """ @@ -412,12 +412,12 @@ class SaoItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_quest_logs( + async def get_quest_logs( self, user_id: int ) -> Optional[List[Row]]: """ @@ -429,12 +429,12 @@ class SaoItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_session( + async def get_session( self, user_id: int = None ) -> Optional[List[Row]]: sql = sessions.select( @@ -445,12 +445,12 @@ class SaoItemData(BaseData): sessions.c.play_date.asc() ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_end_session( + async def get_end_session( self, user_id: int = None ) -> Optional[List[Row]]: sql = end_sessions.select( @@ -461,12 +461,12 @@ class SaoItemData(BaseData): end_sessions.c.play_date.asc() ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def remove_hero_log(self, user_id: int, user_hero_log_id: int) -> None: + async def remove_hero_log(self, user_id: int, user_hero_log_id: int) -> None: sql = hero_log_data.delete( and_( hero_log_data.c.user == user_id, @@ -474,31 +474,31 @@ class SaoItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"{__name__} failed to remove hero log! profile: {user_id}, user_hero_log_id: {user_hero_log_id}" ) return None - def remove_equipment(self, user_id: int, equipment_id: int) -> None: + async def remove_equipment(self, user_id: int, equipment_id: int) -> None: sql = equipment_data.delete( and_(equipment_data.c.user == user_id, equipment_data.c.equipment_id == equipment_id) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"{__name__} failed to remove equipment! profile: {user_id}, equipment_id: {equipment_id}" ) return None - def remove_item(self, user_id: int, item_id: int) -> None: + async def remove_item(self, user_id: int, item_id: int) -> None: sql = item_data.delete( and_(item_data.c.user == user_id, item_data.c.item_id == item_id) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"{__name__} failed to remove item! profile: {user_id}, item_id: {item_id}" diff --git a/titles/sao/schema/profile.py b/titles/sao/schema/profile.py index d7320cc..8368afb 100644 --- a/titles/sao/schema/profile.py +++ b/titles/sao/schema/profile.py @@ -30,17 +30,17 @@ profile = Table( ) class SaoProfileData(BaseData): - def create_profile(self, user_id: int) -> Optional[int]: + async def create_profile(self, user_id: int) -> Optional[int]: sql = insert(profile).values(user=user_id) conflict = sql.on_duplicate_key_update(user=user_id) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error(f"Failed to create SAO profile for user {user_id}!") return None return result.lastrowid - def put_profile(self, user_id: int, user_type: int, nick_name: str, rank_num: int, rank_exp: int, own_col: int, own_vp: int, own_yui_medal: int, setting_title_id: int) -> Optional[int]: + async def put_profile(self, user_id: int, user_type: int, nick_name: str, rank_num: int, rank_exp: int, own_col: int, own_vp: int, own_yui_medal: int, setting_title_id: int) -> Optional[int]: sql = insert(profile).values( user=user_id, user_type=user_type, @@ -62,7 +62,7 @@ class SaoProfileData(BaseData): setting_title_id=setting_title_id ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"{__name__} failed to insert profile! user: {user_id}" @@ -71,9 +71,9 @@ class SaoProfileData(BaseData): return result.lastrowid - def get_profile(self, user_id: int) -> Optional[Row]: + async def get_profile(self, user_id: int) -> Optional[Row]: sql = profile.select(profile.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() \ No newline at end of file diff --git a/titles/sao/schema/static.py b/titles/sao/schema/static.py index ce9a6a9..8b5b1a1 100644 --- a/titles/sao/schema/static.py +++ b/titles/sao/schema/static.py @@ -128,25 +128,25 @@ title = Table( ) class SaoStaticData(BaseData): - def put_quest( self, questSceneId: int, version: int, sortNo: int, name: str, enabled: bool ) -> Optional[int]: + async def put_quest( self, questSceneId: int, version: int, sortNo: int, name: str, enabled: bool ) -> Optional[int]: sql = insert(quest).values( questSceneId=questSceneId, version=version, sortNo=sortNo, name=name, - tutorial=tutorial, + enabled=enabled, ) conflict = sql.on_duplicate_key_update( name=name, questSceneId=questSceneId, version=version ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def put_hero( self, version: int, heroLogId: int, name: str, nickname: str, rarity: int, skillTableSubId: int, awakeningExp: int, flavorText: str, enabled: bool ) -> Optional[int]: + async def put_hero( self, version: int, heroLogId: int, name: str, nickname: str, rarity: int, skillTableSubId: int, awakeningExp: int, flavorText: str, enabled: bool ) -> Optional[int]: sql = insert(hero).values( version=version, heroLogId=heroLogId, @@ -163,12 +163,12 @@ class SaoStaticData(BaseData): name=name, heroLogId=heroLogId ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def put_equipment( self, version: int, equipmentId: int, name: str, equipmentType: int, weaponTypeId:int, rarity: int, flavorText: str, enabled: bool ) -> Optional[int]: + async def put_equipment( self, version: int, equipmentId: int, name: str, equipmentType: int, weaponTypeId:int, rarity: int, flavorText: str, enabled: bool ) -> Optional[int]: sql = insert(equipment).values( version=version, equipmentId=equipmentId, @@ -184,12 +184,12 @@ class SaoStaticData(BaseData): name=name, equipmentId=equipmentId ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def put_item( self, version: int, itemId: int, name: str, itemTypeId: int, rarity: int, flavorText: str, enabled: bool ) -> Optional[int]: + async def put_item( self, version: int, itemId: int, name: str, itemTypeId: int, rarity: int, flavorText: str, enabled: bool ) -> Optional[int]: sql = insert(item).values( version=version, itemId=itemId, @@ -204,12 +204,12 @@ class SaoStaticData(BaseData): name=name, itemId=itemId ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def put_support_log( self, version: int, supportLogId: int, charaId: int, name: str, rarity: int, salePrice: int, skillName: str, enabled: bool ) -> Optional[int]: + async def put_support_log( self, version: int, supportLogId: int, charaId: int, name: str, rarity: int, salePrice: int, skillName: str, enabled: bool ) -> Optional[int]: sql = insert(support).values( version=version, supportLogId=supportLogId, @@ -225,12 +225,12 @@ class SaoStaticData(BaseData): name=name, supportLogId=supportLogId ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def put_rare_drop( self, version: int, questRareDropId: int, commonRewardId: int, enabled: bool ) -> Optional[int]: + async def put_rare_drop( self, version: int, questRareDropId: int, commonRewardId: int, enabled: bool ) -> Optional[int]: sql = insert(rare_drop).values( version=version, questRareDropId=questRareDropId, @@ -242,12 +242,12 @@ class SaoStaticData(BaseData): questRareDropId=questRareDropId, commonRewardId=commonRewardId, version=version ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def put_title( self, version: int, titleId: int, displayName: str, requirement: int, rank: int, imageFilePath: str, enabled: bool ) -> Optional[int]: + async def put_title( self, version: int, titleId: int, displayName: str, requirement: int, rank: int, imageFilePath: str, enabled: bool ) -> Optional[int]: sql = insert(title).values( version=version, titleId=titleId, @@ -262,107 +262,107 @@ class SaoStaticData(BaseData): displayName=displayName, titleId=titleId ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: return None return result.lastrowid - def get_quests_id(self, sortNo: int) -> Optional[Dict]: + async def get_quests_id(self, sortNo: int) -> Optional[Dict]: sql = quest.select(quest.c.sortNo == sortNo) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_quests_ids(self, version: int, enabled: bool) -> Optional[List[Dict]]: + async def get_quests_ids(self, version: int, enabled: bool) -> Optional[List[Dict]]: sql = quest.select(quest.c.version == version and quest.c.enabled == enabled).order_by( quest.c.questSceneId.asc() ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return [list[2] for list in result.fetchall()] - def get_hero_id(self, heroLogId: int) -> Optional[Dict]: + async def get_hero_id(self, heroLogId: int) -> Optional[Dict]: sql = hero.select(hero.c.heroLogId == heroLogId) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_hero_ids(self, version: int, enabled: bool) -> Optional[List[Dict]]: + async def get_hero_ids(self, version: int, enabled: bool) -> Optional[List[Dict]]: sql = hero.select(hero.c.version == version and hero.c.enabled == enabled).order_by( hero.c.heroLogId.asc() ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return [list[2] for list in result.fetchall()] - def get_equipment_id(self, equipmentId: int) -> Optional[Dict]: + async def get_equipment_id(self, equipmentId: int) -> Optional[Dict]: sql = equipment.select(equipment.c.equipmentId == equipmentId) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_equipment_ids(self, version: int, enabled: bool) -> Optional[List[Dict]]: + async def get_equipment_ids(self, version: int, enabled: bool) -> Optional[List[Dict]]: sql = equipment.select(equipment.c.version == version and equipment.c.enabled == enabled).order_by( equipment.c.equipmentId.asc() ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return [list[2] for list in result.fetchall()] - def get_item_id(self, itemId: int) -> Optional[Dict]: + async def get_item_id(self, itemId: int) -> Optional[Dict]: sql = item.select(item.c.itemId == itemId) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_rare_drop_id(self, questRareDropId: int) -> Optional[Dict]: + async def get_rare_drop_id(self, questRareDropId: int) -> Optional[Dict]: sql = rare_drop.select(rare_drop.c.questRareDropId == questRareDropId) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_item_ids(self, version: int, enabled: bool) -> Optional[List[Dict]]: + async def get_item_ids(self, version: int, enabled: bool) -> Optional[List[Dict]]: sql = item.select(item.c.version == version and item.c.enabled == enabled).order_by( item.c.itemId.asc() ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return [list[2] for list in result.fetchall()] - def get_support_log_ids(self, version: int, enabled: bool) -> Optional[List[Dict]]: + async def get_support_log_ids(self, version: int, enabled: bool) -> Optional[List[Dict]]: sql = support.select(support.c.version == version and support.c.enabled == enabled).order_by( support.c.supportLogId.asc() ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return [list[2] for list in result.fetchall()] - def get_title_ids(self, version: int, enabled: bool) -> Optional[List[Dict]]: + async def get_title_ids(self, version: int, enabled: bool) -> Optional[List[Dict]]: sql = title.select(title.c.version == version and title.c.enabled == enabled).order_by( title.c.titleId.asc() ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return [list[2] for list in result.fetchall()] \ No newline at end of file diff --git a/titles/wacca/base.py b/titles/wacca/base.py index c0be449..8a37c2b 100644 --- a/titles/wacca/base.py +++ b/titles/wacca/base.py @@ -98,9 +98,9 @@ class WaccaBase: req = HousingStartRequestV1(data) allnet_region_id = None - machine = self.data.arcade.get_machine(req.chipId) + machine = await self.data.arcade.get_machine(req.chipId) if machine is not None: - arcade = self.data.arcade.get_arcade(machine["arcade"]) + arcade = await self.data.arcade.get_arcade(machine["arcade"]) allnet_region_id = arcade["region_id"] if req.appVersion.country == AllnetCountryCode.JAPAN.value: @@ -139,7 +139,7 @@ class WaccaBase: req = UserStatusGetRequest(data) resp = UserStatusGetV1Response() - profile = self.data.profile.get_profile(aime_id=req.aimeId) + profile = await self.data.profile.get_profile(aime_id=req.aimeId) if profile is None: self.logger.info(f"No user exists for aime id {req.aimeId}") resp.profileStatus = ProfileStatus.ProfileRegister @@ -159,14 +159,14 @@ class WaccaBase: resp.userStatus.wp = profile["wp"] resp.userStatus.useCount = profile["login_count"] - set_title_id = self.data.profile.get_options( + set_title_id = await self.data.profile.get_options( WaccaConstants.OPTIONS["set_title_id"], profile["user"] ) if set_title_id is None: set_title_id = self.OPTIONS_DEFAULTS["set_title_id"] resp.setTitleId = set_title_id - set_icon_id = self.data.profile.get_options( + set_icon_id = await self.data.profile.get_options( WaccaConstants.OPTIONS["set_title_id"], profile["user"] ) if set_icon_id is None: @@ -191,7 +191,7 @@ class WaccaBase: resp.lastLoginDate = 0 else: - profile = self.data.profile.get_profile(req.userId) + profile = await self.data.profile.get_profile(req.userId) if profile is None: self.logger.warning( f"Unknown user id {req.userId} attempted login from {req.chipId}" @@ -215,7 +215,7 @@ class WaccaBase: if midnight_today_ts - last_login_time > 86400: is_consec_day = False - self.data.profile.session_login( + await self.data.profile.session_login( req.userId, resp.firstLoginDaily, is_consec_day ) @@ -230,7 +230,7 @@ class WaccaBase: async def handle_user_status_create_request(self, data: Dict) -> Dict: req = UserStatusCreateRequest(data) - profileId = self.data.profile.create_profile( + profileId = await self.data.profile.create_profile( req.aimeId, req.username, self.version ) @@ -239,30 +239,30 @@ class WaccaBase: if profileId == 0: # We've already made this profile, just return success - new_user = self.data.profile.get_profile(aime_id=req.aimeId) + new_user = await self.data.profile.get_profile(aime_id=req.aimeId) profileId = new_user['id'] # Insert starting items - self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["title"], 104001) - self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["title"], 104002) - self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["title"], 104003) - self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["title"], 104005) + await self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["title"], 104001) + await self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["title"], 104002) + await self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["title"], 104003) + await self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["title"], 104005) - self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["icon"], 102001) - self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["icon"], 102002) + await self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["icon"], 102001) + await self.data.item.put_item(req.aimeId, WaccaConstants.ITEM_TYPES["icon"], 102002) - self.data.item.put_item( + await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["note_color"], 103001 ) - self.data.item.put_item( + await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["note_color"], 203001 ) - self.data.item.put_item( + await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["note_sound"], 105001 ) - self.data.item.put_item( + await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210001 ) @@ -272,7 +272,7 @@ class WaccaBase: req = UserStatusGetDetailRequest(data) resp = UserStatusGetDetailResponseV1() - profile = self.data.profile.get_profile(req.userId) + profile = await self.data.profile.get_profile(req.userId) if profile is None: self.logger.warning(f"Unknown profile {req.userId}") return resp.make() @@ -280,12 +280,12 @@ class WaccaBase: self.logger.info(f"Get detail for profile {req.userId}") user_id = profile["user"] - profile_scores = self.data.score.get_best_scores(user_id) - profile_items = self.data.item.get_items(user_id) - profile_song_unlocks = self.data.item.get_song_unlocks(user_id) - profile_options = self.data.profile.get_options(user_id) - profile_trophies = self.data.item.get_trophies(user_id) - profile_tickets = self.data.item.get_tickets(user_id) + profile_scores = await self.data.score.get_best_scores(user_id) + profile_items = await self.data.item.get_items(user_id) + profile_song_unlocks = await self.data.item.get_song_unlocks(user_id) + profile_options = await self.data.profile.get_options(user_id) + profile_trophies = await self.data.item.get_trophies(user_id) + profile_tickets = await self.data.item.get_tickets(user_id) resp.songUpdateTime = int(profile["last_login_date"].timestamp()) resp.songPlayStatus = [profile["last_song_id"], 1] @@ -437,7 +437,7 @@ class WaccaBase: req = UserTrialGetRequest(data) resp = UserTrialGetResponse() - user_id = self.data.profile.profile_to_aime_user(req.profileId) + user_id = await self.data.profile.profile_to_aime_user(req.profileId) if user_id is None: self.logger.error( f"handle_user_trial_get_request: No profile with id {req.profileId}" @@ -445,7 +445,7 @@ class WaccaBase: return resp.make() self.logger.info(f"Get trial info for user {req.profileId}") - stages = self.data.score.get_stageup(user_id, self.version) + stages = await self.data.score.get_stageup(user_id, self.version) if stages is None: stages = [] @@ -485,15 +485,15 @@ class WaccaBase: while len(req.songScores) < 3: req.songScores.append(0) - profile = self.data.profile.get_profile(req.profileId) + profile = await self.data.profile.get_profile(req.profileId) user_id = profile["user"] - old_stage = self.data.score.get_stageup_stage( + old_stage = await self.data.score.get_stageup_stage( user_id, self.version, req.stageId ) if old_stage is None: - self.data.score.put_stageup( + await self.data.score.put_stageup( user_id, self.version, req.stageId, @@ -519,7 +519,7 @@ class WaccaBase: best_score2 = old_stage["song2_score"] best_score3 = old_stage["song3_score"] - self.data.score.put_stageup( + await self.data.score.put_stageup( user_id, self.version, req.stageId, @@ -537,17 +537,17 @@ class WaccaBase: req.stageLevel == profile["dan_level"] and req.clearType.value > profile["dan_type"] ): - self.data.profile.update_profile_dan( + await self.data.profile.update_profile_dan( req.profileId, req.stageLevel, req.clearType.value ) - self.util_put_items(req.profileId, user_id, req.itemsObtained) + await self.util_put_items(req.profileId, user_id, req.itemsObtained) # user/status/update isn't called after stageup so we have to do some things now - current_icon = self.data.profile.get_options( + current_icon = await self.data.profile.get_options( user_id, WaccaConstants.OPTIONS["set_icon_id"] ) - current_nav = self.data.profile.get_options( + current_nav = await self.data.profile.get_options( user_id, WaccaConstants.OPTIONS["set_nav_id"] ) @@ -560,13 +560,13 @@ class WaccaBase: else: current_nav = current_nav["value"] - self.data.item.put_item( + await self.data.item.put_item( user_id, WaccaConstants.ITEM_TYPES["icon"], current_icon ) - self.data.item.put_item( + await self.data.item.put_item( user_id, WaccaConstants.ITEM_TYPES["navigator"], current_nav ) - self.data.profile.update_profile_playtype( + await self.data.profile.update_profile_playtype( req.profileId, 4, data["appVersion"][:7] ) return BaseResponse().make() @@ -583,16 +583,16 @@ class WaccaBase: req = UserSugarokuUpdateRequestV2(data) mission_flg = req.mission_flag - user_id = self.data.profile.profile_to_aime_user(req.profileId) + user_id = await self.data.profile.profile_to_aime_user(req.profileId) if user_id is None: self.logger.info( f"handle_user_sugoroku_update_request unknwon profile ID {req.profileId}" ) return resp.make() - self.util_put_items(req.profileId, user_id, req.itemsObtainted) + await self.util_put_items(req.profileId, user_id, req.itemsObtainted) - self.data.profile.update_gate( + await self.data.profile.update_gate( user_id, req.gateId, req.page, @@ -609,13 +609,13 @@ class WaccaBase: async def handle_user_music_unlock_request(self, data: Dict) -> Dict: req = UserMusicUnlockRequest(data) - profile = self.data.profile.get_profile(req.profileId) + profile = await self.data.profile.get_profile(req.profileId) if profile is None: return BaseResponse().make() user_id = profile["user"] current_wp = profile["wp"] - tickets = self.data.item.get_tickets(user_id) + tickets = await self.data.item.get_tickets(user_id) new_tickets: List[TicketItem] = [] for ticket in tickets: @@ -628,7 +628,7 @@ class WaccaBase: ): if current_wp >= item.quantity: current_wp -= item.quantity - self.data.profile.spend_wp(req.profileId, item.quantity) + await self.data.profile.spend_wp(req.profileId, item.quantity) else: return BaseResponse().make() @@ -641,21 +641,21 @@ class WaccaBase: self.logger.debug( f"Remove ticket ID {new_tickets[x].userTicketId} type {new_tickets[x].ticketId} from {user_id}" ) - self.data.item.spend_ticket(new_tickets[x].userTicketId) + await self.data.item.spend_ticket(new_tickets[x].userTicketId) new_tickets.pop(x) break # wp, ticket info if req.difficulty > WaccaConstants.Difficulty.HARD.value: - old_score = self.data.score.get_best_score( + old_score = await self.data.score.get_best_score( user_id, req.songId, req.difficulty ) if not old_score: - self.data.score.put_best_score( + await self.data.score.put_best_score( user_id, req.songId, req.difficulty, 0, [0] * 5, [0] * 13, 0, 0 ) - self.data.item.unlock_song( + await self.data.item.unlock_song( user_id, req.songId, req.difficulty @@ -698,7 +698,7 @@ class WaccaBase: ) return resp.make() - profile = self.data.profile.get_profile(req.profileId) + profile = await self.data.profile.get_profile(req.profileId) if profile is None: self.logger.warning( @@ -707,7 +707,7 @@ class WaccaBase: return resp.make() user_id = profile["user"] - self.util_put_items(req.profileId, user_id, req.itemsObtained) + await self.util_put_items(req.profileId, user_id, req.itemsObtained) playlog_clear_status = ( req.songDetail.flagCleared @@ -716,7 +716,7 @@ class WaccaBase: + req.songDetail.flagAllMarvelous ) - self.data.score.put_playlog( + await self.data.score.put_playlog( user_id, req.songDetail.songId, req.songDetail.difficulty, @@ -733,7 +733,7 @@ class WaccaBase: self.season, ) - old_score = self.data.score.get_best_score( + old_score = await self.data.score.get_best_score( user_id, req.songDetail.songId, req.songDetail.difficulty ) @@ -749,7 +749,7 @@ class WaccaBase: grades[req.songDetail.grade.value - 1] = 1 - self.data.score.put_best_score( + await self.data.score.put_best_score( user_id, req.songDetail.songId, req.songDetail.difficulty, @@ -805,7 +805,7 @@ class WaccaBase: old_score["rating"], ) - self.data.score.put_best_score( + await self.data.score.put_best_score( user_id, req.songDetail.songId, req.songDetail.difficulty, @@ -846,17 +846,17 @@ class WaccaBase: req = UserMissionUpdateRequest(data) page_status = req.params[1][1] - profile = self.data.profile.get_profile(req.profileId) + profile = await self.data.profile.get_profile(req.profileId) if profile is None: return BaseResponse().make() if len(req.itemsObtained) > 0: - self.util_put_items(req.profileId, profile["user"], req.itemsObtained) + await self.util_put_items(req.profileId, profile["user"], req.itemsObtained) - self.data.profile.update_bingo( + await self.data.profile.update_bingo( profile["user"], req.bingoDetail.pageNumber, page_status ) - self.data.profile.update_tutorial_flags(req.profileId, req.params[3]) + await self.data.profile.update_tutorial_flags(req.profileId, req.params[3]) return BaseResponse().make() @@ -864,7 +864,7 @@ class WaccaBase: req = UserGoodsPurchaseRequest(data) resp = UserGoodsPurchaseResponse() - profile = self.data.profile.get_profile(req.profileId) + profile = await self.data.profile.get_profile(req.profileId) if profile is None: return BaseResponse().make() @@ -876,20 +876,20 @@ class WaccaBase: and not self.game_config.mods.infinite_wp ): resp.currentWp -= req.cost - self.data.profile.spend_wp(req.profileId, req.cost) + await self.data.profile.spend_wp(req.profileId, req.cost) elif req.purchaseType == PurchaseType.PurchaseTypeCredit: self.logger.info( f"User {req.profileId} Purchased item {req.itemObtained.itemType} id {req.itemObtained.itemId} for {req.cost} credits on machine {req.chipId}" ) - self.util_put_items(req.profileId, user_id, [req.itemObtained]) + await self.util_put_items(req.profileId, user_id, [req.itemObtained]) if self.game_config.mods.infinite_tickets: for x in range(5): resp.tickets.append(TicketItem(x, 106002, 0)) else: - tickets = self.data.item.get_tickets(user_id) + tickets = await self.data.item.get_tickets(user_id) for ticket in tickets: resp.tickets.append( @@ -914,7 +914,7 @@ class WaccaBase: async def handle_user_rating_update_request(self, data: Dict) -> Dict: req = UserRatingUpdateRequest(data) - user_id = self.data.profile.profile_to_aime_user(req.profileId) + user_id = await self.data.profile.profile_to_aime_user(req.profileId) if user_id is None: self.logger.error( @@ -923,33 +923,33 @@ class WaccaBase: return BaseResponse().make() for song in req.songs: - self.data.score.update_song_rating( + await self.data.score.update_song_rating( user_id, song.songId, song.difficulty, song.rating ) - self.data.profile.update_user_rating(req.profileId, req.totalRating) + await self.data.profile.update_user_rating(req.profileId, req.totalRating) return BaseResponse().make() async def handle_user_status_update_request(self, data: Dict) -> Dict: req = UserStatusUpdateRequestV1(data) - user_id = self.data.profile.profile_to_aime_user(req.profileId) + user_id = await self.data.profile.profile_to_aime_user(req.profileId) if user_id is None: self.logger.info( f"handle_user_status_update_request: No profile with ID {req.profileId}" ) return BaseResponse().make() - self.util_put_items(req.profileId, user_id, req.itemsRecieved) - self.data.profile.update_profile_playtype( + await self.util_put_items(req.profileId, user_id, req.itemsRecieved) + await self.data.profile.update_profile_playtype( req.profileId, req.playType.value, data["appVersion"][:7] ) - current_icon = self.data.profile.get_options( + current_icon = await self.data.profile.get_options( user_id, WaccaConstants.OPTIONS["set_icon_id"] ) - current_nav = self.data.profile.get_options( + current_nav = await self.data.profile.get_options( user_id, WaccaConstants.OPTIONS["set_nav_id"] ) @@ -962,10 +962,10 @@ class WaccaBase: else: current_nav = current_nav["value"] - self.data.item.put_item( + await self.data.item.put_item( user_id, WaccaConstants.ITEM_TYPES["icon"], current_icon ) - self.data.item.put_item( + await self.data.item.put_item( user_id, WaccaConstants.ITEM_TYPES["navigator"], current_nav ) return BaseResponse().make() @@ -973,19 +973,19 @@ class WaccaBase: async def handle_user_info_update_request(self, data: Dict) -> Dict: req = UserInfoUpdateRequest(data) - user_id = self.data.profile.profile_to_aime_user(req.profileId) + user_id = await self.data.profile.profile_to_aime_user(req.profileId) for opt in req.optsUpdated: - self.data.profile.update_option(user_id, opt.optId, opt.optVal) + await self.data.profile.update_option(user_id, opt.optId, opt.optVal) for update in req.datesUpdated: pass for fav in req.favoritesAdded: - self.data.profile.add_favorite_song(user_id, fav) + await self.data.profile.add_favorite_song(user_id, fav) for unfav in req.favoritesRemoved: - self.data.profile.remove_favorite_song(user_id, unfav) + await self.data.profile.remove_favorite_song(user_id, unfav) return BaseResponse().make() @@ -993,7 +993,7 @@ class WaccaBase: req = UserVipGetRequest(data) resp = UserVipGetResponse() - profile = self.data.profile.get_profile(req.profileId) + profile = await self.data.profile.get_profile(req.profileId) if profile is None: self.logger.warning( f"handle_user_vip_get_request no profile with ID {req.profileId}" @@ -1024,7 +1024,7 @@ class WaccaBase: async def handle_user_vip_start_request(self, data: Dict) -> Dict: req = UserVipStartRequest(data) - profile = self.data.profile.get_profile(req.profileId) + profile = await self.data.profile.get_profile(req.profileId) if profile is None: return BaseResponse().make() @@ -1040,10 +1040,10 @@ class WaccaBase: ).make() vip_exp_time = self.srvtime + timedelta(days=req.days) - self.data.profile.update_vip_time(req.profileId, vip_exp_time) + await self.data.profile.update_vip_time(req.profileId, vip_exp_time) return UserVipStartResponse(int(vip_exp_time.timestamp())).make() - def util_put_items( + async def util_put_items( self, profile_id: int, user_id: int, items_obtained: List[GenericItemRecv] ) -> None: if user_id is None or profile_id <= 0: @@ -1052,10 +1052,10 @@ class WaccaBase: if items_obtained: for item in items_obtained: if item.itemType == WaccaConstants.ITEM_TYPES["xp"]: - self.data.profile.add_xp(profile_id, item.quantity) + await self.data.profile.add_xp(profile_id, item.quantity) elif item.itemType == WaccaConstants.ITEM_TYPES["wp"]: - self.data.profile.add_wp(profile_id, item.quantity) + await self.data.profile.add_wp(profile_id, item.quantity) elif ( item.itemType @@ -1063,11 +1063,11 @@ class WaccaBase: or item.itemType == WaccaConstants.ITEM_TYPES["music_unlock"] ): if item.quantity > WaccaConstants.Difficulty.HARD.value: - old_score = self.data.score.get_best_score( + old_score = await self.data.score.get_best_score( user_id, item.itemId, item.quantity ) if not old_score: - self.data.score.put_best_score( + await self.data.score.put_best_score( user_id, item.itemId, item.quantity, @@ -1080,18 +1080,18 @@ class WaccaBase: if item.quantity == 0: item.quantity = WaccaConstants.Difficulty.HARD.value - self.data.item.unlock_song(user_id, item.itemId, item.quantity) + await self.data.item.unlock_song(user_id, item.itemId, item.quantity) elif item.itemType == WaccaConstants.ITEM_TYPES["ticket"]: - self.data.item.add_ticket(user_id, item.itemId) + await self.data.item.add_ticket(user_id, item.itemId) elif item.itemType == WaccaConstants.ITEM_TYPES["trophy"]: - self.data.item.update_trophy( + await self.data.item.update_trophy( user_id, item.itemId, self.season, item.quantity, 0 ) else: - self.data.item.put_item(user_id, item.itemType, item.itemId) + await self.data.item.put_item(user_id, item.itemType, item.itemId) def util_calc_song_rating(self, score: int, difficulty: float) -> int: if score >= 990000: diff --git a/titles/wacca/frontend.py b/titles/wacca/frontend.py index fb55de1..cd915bb 100644 --- a/titles/wacca/frontend.py +++ b/titles/wacca/frontend.py @@ -24,7 +24,7 @@ class WaccaFrontend(FE_Base): ) self.nav_name = "Wacca" - def render_GET(self, request: Request) -> bytes: + async def render_GET(self, request: Request) -> bytes: template = self.environment.get_template( "titles/wacca/frontend/wacca_index.jinja" ) diff --git a/titles/wacca/lily.py b/titles/wacca/lily.py index d92e417..9f3f817 100644 --- a/titles/wacca/lily.py +++ b/titles/wacca/lily.py @@ -45,20 +45,20 @@ class WaccaLily(WaccaS): req = UserStatusCreateRequest(data) ret = await super().handle_user_status_create_request(data) - new_user = self.data.profile.get_profile(aime_id=req.aimeId) + new_user = await self.data.profile.get_profile(aime_id=req.aimeId) if new_user is None: return BaseResponse().make() - self.data.item.put_item( + await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["user_plate"], 211001 ) # Added lily - self.data.item.put_item( + await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["note_sound"], 205005 ) # Added lily - self.data.item.put_item( + await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210002 ) # Lily, Added Lily @@ -68,13 +68,13 @@ class WaccaLily(WaccaS): req = UserStatusGetRequest(data) resp = UserStatusGetV2Response() - profile = self.data.profile.get_profile(aime_id=req.aimeId) + profile = await self.data.profile.get_profile(aime_id=req.aimeId) if profile is None: self.logger.info(f"No user exists for aime id {req.aimeId}") resp.profileStatus = ProfileStatus.ProfileRegister return resp.make() - opts = self.data.profile.get_options(req.aimeId) + opts = await self.data.profile.get_options(req.aimeId) self.logger.info(f"User preview for {req.aimeId} from {req.chipId}") if profile["last_game_ver"] is None: @@ -94,14 +94,14 @@ class WaccaLily(WaccaS): resp.userStatus.loginsToday = profile["login_count_today"] resp.userStatus.rating = profile["rating"] - set_title_id = self.data.profile.get_options( + set_title_id = await self.data.profile.get_options( WaccaConstants.OPTIONS["set_title_id"], profile["user"] ) if set_title_id is None: set_title_id = self.OPTIONS_DEFAULTS["set_title_id"] resp.setTitleId = set_title_id - set_icon_id = self.data.profile.get_options( + set_icon_id = await self.data.profile.get_options( WaccaConstants.OPTIONS["set_title_id"], profile["user"] ) if set_icon_id is None: @@ -155,7 +155,7 @@ class WaccaLily(WaccaS): resp.lastLoginDate = 0 else: - profile = self.data.profile.get_profile(req.userId) + profile = await self.data.profile.get_profile(req.userId) if profile is None: self.logger.warning( f"Unknown user id {req.userId} attempted login from {req.chipId}" @@ -179,7 +179,7 @@ class WaccaLily(WaccaS): if midnight_today_ts - last_login_time > 86400: is_consec_day = False - self.data.profile.session_login( + await self.data.profile.session_login( req.userId, resp.firstLoginDaily, is_consec_day ) resp.vipInfo.pageYear = datetime.now().year @@ -196,7 +196,7 @@ class WaccaLily(WaccaS): else: resp = UserStatusGetDetailResponseV2() - profile = self.data.profile.get_profile(req.userId) + profile = await self.data.profile.get_profile(req.userId) if profile is None: self.logger.warning(f"Unknown profile {req.userId}") return resp.make() @@ -204,14 +204,14 @@ class WaccaLily(WaccaS): self.logger.info(f"Get detail for profile {req.userId}") user_id = profile["user"] - profile_scores = self.data.score.get_best_scores(user_id) - profile_items = self.data.item.get_items(user_id) - profile_song_unlocks = self.data.item.get_song_unlocks(user_id) - profile_options = self.data.profile.get_options(user_id) - profile_favorites = self.data.profile.get_favorite_songs(user_id) - profile_gates = self.data.profile.get_gates(user_id) - profile_trophies = self.data.item.get_trophies(user_id) - profile_tickets = self.data.item.get_tickets(user_id) + profile_scores = await self.data.score.get_best_scores(user_id) + profile_items = await self.data.item.get_items(user_id) + profile_song_unlocks = await self.data.item.get_song_unlocks(user_id) + profile_options = await self.data.profile.get_options(user_id) + profile_favorites = await self.data.profile.get_favorite_songs(user_id) + profile_gates = await self.data.profile.get_gates(user_id) + profile_trophies = await self.data.item.get_trophies(user_id) + profile_tickets = await self.data.item.get_tickets(user_id) if profile["vip_expire_time"] is None: resp.userStatus.vipExpireTime = 0 @@ -446,7 +446,7 @@ class WaccaLily(WaccaS): async def handle_user_status_update_request(self, data: Dict) -> Dict: super().handle_user_status_update_request(data) req = UserStatusUpdateRequestV2(data) - self.data.profile.update_profile_lastplayed( + await self.data.profile.update_profile_lastplayed( req.profileId, req.lastSongInfo.lastSongId, req.lastSongInfo.lastSongDiff, diff --git a/titles/wacca/lilyr.py b/titles/wacca/lilyr.py index 83f9bf6..204a656 100644 --- a/titles/wacca/lilyr.py +++ b/titles/wacca/lilyr.py @@ -43,9 +43,9 @@ class WaccaLilyR(WaccaLily): req = HousingStartRequestV2(data) allnet_region_id = None - machine = self.data.arcade.get_machine(req.chipId) + machine = await self.data.arcade.get_machine(req.chipId) if machine is not None: - arcade = self.data.arcade.get_arcade(machine["arcade"]) + arcade = await self.data.arcade.get_arcade(machine["arcade"]) allnet_region_id = arcade["region_id"] if req.appVersion.country == AllnetCountryCode.JAPAN.value: @@ -75,28 +75,28 @@ class WaccaLilyR(WaccaLily): req = UserStatusCreateRequest(data) resp = await super().handle_user_status_create_request(data) - self.data.item.put_item( + await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210054 ) # Added lily r - self.data.item.put_item( + await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210055 ) # Added lily r - self.data.item.put_item( + await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210056 ) # Added lily r - self.data.item.put_item( + await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210057 ) # Added lily r - self.data.item.put_item( + await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210058 ) # Added lily r - self.data.item.put_item( + await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210059 ) # Added lily r - self.data.item.put_item( + await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210060 ) # Added lily r - self.data.item.put_item( + await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210061 ) # Added lily r diff --git a/titles/wacca/read.py b/titles/wacca/read.py index 8fe5b6a..91fae03 100644 --- a/titles/wacca/read.py +++ b/titles/wacca/read.py @@ -29,7 +29,7 @@ class WaccaReader(BaseReader): self.logger.error(f"Invalid wacca version {version}") exit(1) - def read(self) -> None: + async def read(self) -> None: if not ( path.exists(f"{self.bin_dir}/Table") and path.exists(f"{self.bin_dir}/Message") @@ -37,9 +37,9 @@ class WaccaReader(BaseReader): self.logger.error("Could not find Table or Message folder, nothing to read") return - self.read_music(f"{self.bin_dir}/Table", "MusicParameterTable") + await self.read_music(f"{self.bin_dir}/Table", "MusicParameterTable") - def read_music(self, base_dir: str, table: str) -> None: + async def read_music(self, base_dir: str, table: str) -> None: if not self.check_valid_pair(base_dir, table): self.logger.warning( f"Cannot find {table} uasset/uexp pair at {base_dir}, music will not be read" @@ -67,7 +67,7 @@ class WaccaReader(BaseReader): designer = wacca_data[str(key)]["NotesDesignerNormal"] if diff > 0: - self.data.static.put_music( + await self.data.static.put_music( self.version, song_id, 1, @@ -84,7 +84,7 @@ class WaccaReader(BaseReader): designer = wacca_data[str(key)]["NotesDesignerHard"] if diff > 0: - self.data.static.put_music( + await self.data.static.put_music( self.version, song_id, 2, @@ -101,7 +101,7 @@ class WaccaReader(BaseReader): designer = wacca_data[str(key)]["NotesDesignerExpert"] if diff > 0: - self.data.static.put_music( + await self.data.static.put_music( self.version, song_id, 3, @@ -118,7 +118,7 @@ class WaccaReader(BaseReader): designer = wacca_data[str(key)]["NotesDesignerInferno"] if diff > 0: - self.data.static.put_music( + await self.data.static.put_music( self.version, song_id, 4, diff --git a/titles/wacca/reverse.py b/titles/wacca/reverse.py index 51e8c63..be34024 100644 --- a/titles/wacca/reverse.py +++ b/titles/wacca/reverse.py @@ -56,7 +56,7 @@ class WaccaReverse(WaccaLilyR): req = UserStatusGetDetailRequest(data) resp = UserStatusGetDetailResponseV4() - profile = self.data.profile.get_profile(req.userId) + profile = await self.data.profile.get_profile(req.userId) if profile is None: self.logger.warning(f"Unknown profile {req.userId}") return resp.make() @@ -64,15 +64,15 @@ class WaccaReverse(WaccaLilyR): self.logger.info(f"Get detail for profile {req.userId}") user_id = profile["user"] - profile_scores = self.data.score.get_best_scores(user_id) - profile_items = self.data.item.get_items(user_id) - profile_song_unlocks = self.data.item.get_song_unlocks(user_id) - profile_options = self.data.profile.get_options(user_id) - profile_favorites = self.data.profile.get_favorite_songs(user_id) - profile_gates = self.data.profile.get_gates(user_id) - profile_bingo = self.data.profile.get_bingo(user_id) - profile_trophies = self.data.item.get_trophies(user_id) - profile_tickets = self.data.item.get_tickets(user_id) + profile_scores = await self.data.score.get_best_scores(user_id) + profile_items = await self.data.item.get_items(user_id) + profile_song_unlocks = await self.data.item.get_song_unlocks(user_id) + profile_options = await self.data.profile.get_options(user_id) + profile_favorites = await self.data.profile.get_favorite_songs(user_id) + profile_gates = await self.data.profile.get_gates(user_id) + profile_bingo = await self.data.profile.get_bingo(user_id) + profile_trophies = await self.data.item.get_trophies(user_id) + profile_tickets = await self.data.item.get_tickets(user_id) if profile["gate_tutorial_flags"] is not None: for x in profile["gate_tutorial_flags"]: @@ -309,17 +309,17 @@ class WaccaReverse(WaccaLilyR): req = UserStatusCreateRequest(data) resp = await super().handle_user_status_create_request(data) - self.data.item.put_item( + await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 310001 ) # Added reverse - self.data.item.put_item( + await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 310002 ) # Added reverse - self.data.item.put_item( + await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["touch_effect"], 312000 ) # Added reverse - self.data.item.put_item( + await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["touch_effect"], 312001 ) # Added reverse diff --git a/titles/wacca/schema/item.py b/titles/wacca/schema/item.py index df3380a..413b133 100644 --- a/titles/wacca/schema/item.py +++ b/titles/wacca/schema/item.py @@ -75,16 +75,16 @@ trophy = Table( class WaccaItemData(BaseData): - def get_song_unlocks(self, user_id: int) -> Optional[List[Row]]: + async def get_song_unlocks(self, user_id: int) -> Optional[List[Row]]: sql = song_unlock.select(song_unlock.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def unlock_song(self, user_id: int, song_id: int, difficulty: int) -> Optional[int]: + async def unlock_song(self, user_id: int, song_id: int, difficulty: int) -> Optional[int]: sql = insert(song_unlock).values( user=user_id, song_id=song_id, highest_difficulty=difficulty ) @@ -99,7 +99,7 @@ class WaccaItemData(BaseData): ) ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"{__name__} failed to unlock song! user: {user_id}, song_id: {song_id}, difficulty: {difficulty}" @@ -108,7 +108,7 @@ class WaccaItemData(BaseData): return result.lastrowid - def put_item(self, user_id: int, item_type: int, item_id: int) -> Optional[int]: + async def put_item(self, user_id: int, item_type: int, item_id: int) -> Optional[int]: sql = insert(item).values( user=user_id, item_id=item_id, @@ -117,7 +117,7 @@ class WaccaItemData(BaseData): conflict = sql.on_duplicate_key_update(use_count=item.c.use_count + 1) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"{__name__} failed to insert item! user: {user_id}, item_id: {item_id}, item_type: {item_type}" @@ -126,7 +126,7 @@ class WaccaItemData(BaseData): return result.lastrowid - def get_items( + async def get_items( self, user_id: int, item_type: int = None, item_id: int = None ) -> Optional[List[Row]]: """ @@ -140,23 +140,23 @@ class WaccaItemData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_tickets(self, user_id: int) -> Optional[List[Row]]: + async def get_tickets(self, user_id: int) -> Optional[List[Row]]: sql = select(ticket).where(ticket.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def add_ticket(self, user_id: int, ticket_id: int) -> None: + async def add_ticket(self, user_id: int, ticket_id: int) -> None: sql = insert(ticket).values(user=user_id, ticket_id=ticket_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"add_ticket: Failed to insert wacca ticket! user_id: {user_id} ticket_id {ticket_id}" @@ -164,15 +164,15 @@ class WaccaItemData(BaseData): return None return result.lastrowid - def spend_ticket(self, id: int) -> None: + async def spend_ticket(self, id: int) -> None: sql = delete(ticket).where(ticket.c.id == id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.warning(f"Failed to delete ticket id {id}") return None - def get_trophies(self, user_id: int, season: int = None) -> Optional[List[Row]]: + async def get_trophies(self, user_id: int, season: int = None) -> Optional[List[Row]]: if season is None: sql = select(trophy).where(trophy.c.user == user_id) else: @@ -180,12 +180,12 @@ class WaccaItemData(BaseData): and_(trophy.c.user == user_id, trophy.c.season == season) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def update_trophy( + async def update_trophy( self, user_id: int, trophy_id: int, season: int, progress: int, badge_type: int ) -> Optional[int]: sql = insert(trophy).values( @@ -198,7 +198,7 @@ class WaccaItemData(BaseData): conflict = sql.on_duplicate_key_update(progress=progress) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"update_trophy: Failed to insert wacca trophy! user_id: {user_id} trophy_id: {trophy_id} progress {progress}" diff --git a/titles/wacca/schema/profile.py b/titles/wacca/schema/profile.py index ff13782..9e09142 100644 --- a/titles/wacca/schema/profile.py +++ b/titles/wacca/schema/profile.py @@ -139,7 +139,7 @@ gate = Table( class WaccaProfileData(BaseData): - def create_profile( + async def create_profile( self, aime_id: int, username: str, version: int ) -> Optional[int]: """ @@ -149,7 +149,7 @@ class WaccaProfileData(BaseData): conflict = sql.on_duplicate_key_update(username=sql.inserted.username) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"{__name__} Failed to insert wacca profile! aime id: {aime_id} username: {username}" @@ -157,7 +157,7 @@ class WaccaProfileData(BaseData): return None return result.lastrowid - def update_profile_playtype( + async def update_profile_playtype( self, profile_id: int, play_type: int, game_version: str ) -> None: sql = profile.update(profile.c.id == profile_id).values( @@ -179,14 +179,14 @@ class WaccaProfileData(BaseData): last_game_ver=game_version, ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"update_profile: failed to update profile! profile: {profile_id}" ) return None - def update_profile_lastplayed( + async def update_profile_lastplayed( self, profile_id: int, last_song_id: int, @@ -202,21 +202,21 @@ class WaccaProfileData(BaseData): last_folder_id=last_folder_id, last_song_order=last_song_order, ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"update_profile_lastplayed: failed to update profile! profile: {profile_id}" ) return None - def update_profile_dan( + async def update_profile_dan( self, profile_id: int, dan_level: int, dan_type: int ) -> Optional[int]: sql = profile.update(profile.c.id == profile_id).values( dan_level=dan_level, dan_type=dan_type ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.warning( f"update_profile_dan: Failed to update! profile {profile_id}" @@ -224,7 +224,7 @@ class WaccaProfileData(BaseData): return None return result.lastrowid - def get_profile(self, profile_id: int = 0, aime_id: int = None) -> Optional[Row]: + async def get_profile(self, profile_id: int = 0, aime_id: int = None) -> Optional[Row]: """ Given a game version and either a profile or aime id, return the profile """ @@ -238,12 +238,12 @@ class WaccaProfileData(BaseData): ) return None - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_options(self, user_id: int, option_id: int = None) -> Optional[List[Row]]: + async def get_options(self, user_id: int, option_id: int = None) -> Optional[List[Row]]: """ Get a specific user option for a profile, or all of them if none specified """ @@ -254,7 +254,7 @@ class WaccaProfileData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None if option_id is not None: @@ -262,12 +262,12 @@ class WaccaProfileData(BaseData): else: return result.fetchall() - def update_option(self, user_id: int, option_id: int, value: int) -> Optional[int]: + async def update_option(self, user_id: int, option_id: int, value: int) -> Optional[int]: sql = insert(option).values(user=user_id, opt_id=option_id, value=value) conflict = sql.on_duplicate_key_update(value=value) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"{__name__} failed to insert option! profile: {user_id}, option: {option_id}, value: {value}" @@ -276,10 +276,10 @@ class WaccaProfileData(BaseData): return result.lastrowid - def add_favorite_song(self, user_id: int, song_id: int) -> Optional[int]: + async def add_favorite_song(self, user_id: int, song_id: int) -> Optional[int]: sql = favorite.insert().values(user=user_id, song_id=song_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"{__name__} failed to insert favorite! profile: {user_id}, song_id: {song_id}" @@ -287,35 +287,35 @@ class WaccaProfileData(BaseData): return None return result.lastrowid - def remove_favorite_song(self, user_id: int, song_id: int) -> None: + async def remove_favorite_song(self, user_id: int, song_id: int) -> None: sql = favorite.delete( and_(favorite.c.user == user_id, favorite.c.song_id == song_id) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"{__name__} failed to remove favorite! profile: {user_id}, song_id: {song_id}" ) return None - def get_favorite_songs(self, user_id: int) -> Optional[List[Row]]: + async def get_favorite_songs(self, user_id: int) -> Optional[List[Row]]: sql = favorite.select(favorite.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_gates(self, user_id: int) -> Optional[List[Row]]: + async def get_gates(self, user_id: int) -> Optional[List[Row]]: sql = select(gate).where(gate.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def update_gate( + async def update_gate( self, user_id: int, gate_id: int, @@ -343,7 +343,7 @@ class WaccaProfileData(BaseData): total_points=sql.inserted.total_points, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"{__name__} failed to update gate! user: {user_id}, gate_id: {gate_id}" @@ -351,18 +351,18 @@ class WaccaProfileData(BaseData): return None return result.lastrowid - def get_friends(self, user_id: int) -> Optional[List[Row]]: + async def get_friends(self, user_id: int) -> Optional[List[Row]]: sql = friend.select(friend.c.profile_sender == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def profile_to_aime_user(self, profile_id: int) -> Optional[int]: + async def profile_to_aime_user(self, profile_id: int) -> Optional[int]: sql = select(profile.c.user).where(profile.c.id == profile_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.info( f"profile_to_aime_user: No user found for profile {profile_id}" @@ -378,7 +378,7 @@ class WaccaProfileData(BaseData): return this_profile["user"] - def session_login( + async def session_login( self, profile_id: int, is_new_day: bool, is_consec_day: bool ) -> None: # TODO: Reset consec days counter @@ -395,127 +395,127 @@ class WaccaProfileData(BaseData): last_login_date=func.now(), ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"session_login: failed to update profile! profile: {profile_id}" ) return None - def session_logout(self, profile_id: int) -> None: + async def session_logout(self, profile_id: int) -> None: sql = profile.update(profile.c.id == id).values(login_count_consec=0) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"{__name__} failed to update profile! profile: {profile_id}" ) return None - def add_xp(self, profile_id: int, xp: int) -> None: + async def add_xp(self, profile_id: int, xp: int) -> None: sql = profile.update(profile.c.id == profile_id).values(xp=profile.c.xp + xp) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"add_xp: Failed to update profile! profile_id {profile_id} xp {xp}" ) return None - def add_wp(self, profile_id: int, wp: int) -> None: + async def add_wp(self, profile_id: int, wp: int) -> None: sql = profile.update(profile.c.id == profile_id).values( wp=profile.c.wp + wp, wp_total=profile.c.wp_total + wp, ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"add_wp: Failed to update profile! profile_id {profile_id} wp {wp}" ) return None - def spend_wp(self, profile_id: int, wp: int) -> None: + async def spend_wp(self, profile_id: int, wp: int) -> None: sql = profile.update(profile.c.id == profile_id).values( wp=profile.c.wp - wp, wp_spent=profile.c.wp_spent + wp, ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"spend_wp: Failed to update profile! profile_id {profile_id} wp {wp}" ) return None - def activate_vip(self, profile_id: int, expire_time) -> None: + async def activate_vip(self, profile_id: int, expire_time) -> None: sql = profile.update(profile.c.id == profile_id).values( vip_expire_time=expire_time ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"activate_vip: Failed to update profile! profile_id {profile_id} expire_time {expire_time}" ) return None - def update_user_rating(self, profile_id: int, new_rating: int) -> None: + async def update_user_rating(self, profile_id: int, new_rating: int) -> None: sql = profile.update(profile.c.id == profile_id).values(rating=new_rating) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"update_user_rating: Failed to update profile! profile_id {profile_id} new_rating {new_rating}" ) return None - def update_bingo(self, aime_id: int, page: int, progress: int) -> Optional[int]: + async def update_bingo(self, aime_id: int, page: int, progress: int) -> Optional[int]: sql = insert(bingo).values( user=aime_id, page_number=page, page_progress=progress ) conflict = sql.on_duplicate_key_update(page_number=page, page_progress=progress) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error(f"put_bingo: failed to update! aime_id: {aime_id}") return None return result.lastrowid - def get_bingo(self, aime_id: int) -> Optional[List[Row]]: + async def get_bingo(self, aime_id: int) -> Optional[List[Row]]: sql = select(bingo).where(bingo.c.user == aime_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_bingo_page(self, aime_id: int, page: Dict) -> Optional[List[Row]]: + async def get_bingo_page(self, aime_id: int, page: Dict) -> Optional[List[Row]]: sql = select(bingo).where( and_(bingo.c.user == aime_id, bingo.c.page_number == page) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def update_vip_time(self, profile_id: int, time_left) -> None: + async def update_vip_time(self, profile_id: int, time_left) -> None: sql = profile.update(profile.c.id == profile_id).values( vip_expire_time=time_left ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error(f"Failed to update VIP time for profile {profile_id}") - def update_tutorial_flags(self, profile_id: int, flags: Dict) -> None: + async def update_tutorial_flags(self, profile_id: int, flags: Dict) -> None: sql = profile.update(profile.c.id == profile_id).values( gate_tutorial_flags=flags ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"Failed to update tutorial flags for profile {profile_id}" diff --git a/titles/wacca/schema/score.py b/titles/wacca/schema/score.py index 5a3a53f..114cce3 100644 --- a/titles/wacca/schema/score.py +++ b/titles/wacca/schema/score.py @@ -95,7 +95,7 @@ stageup = Table( class WaccaScoreData(BaseData): - def put_best_score( + async def put_best_score( self, user_id: int, song_id: int, @@ -164,7 +164,7 @@ class WaccaScoreData(BaseData): lowest_miss_ct=lowest_miss_ct, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.error( f"{__name__}: failed to insert best score! profile: {user_id}, song: {song_id}, chart: {chart_id}" @@ -173,7 +173,7 @@ class WaccaScoreData(BaseData): return result.lastrowid - def put_playlog( + async def put_playlog( self, user_id: int, song_id: int, @@ -210,7 +210,7 @@ class WaccaScoreData(BaseData): season=season, ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"{__name__} failed to insert playlog! profile: {user_id}, song: {song_id}, chart: {chart_id}" @@ -219,7 +219,7 @@ class WaccaScoreData(BaseData): return result.lastrowid - def get_best_score( + async def get_best_score( self, user_id: int, song_id: int, chart_id: int ) -> Optional[Row]: sql = best_score.select( @@ -230,20 +230,20 @@ class WaccaScoreData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() - def get_best_scores(self, user_id: int) -> Optional[List[Row]]: + async def get_best_scores(self, user_id: int) -> Optional[List[Row]]: sql = best_score.select(best_score.c.user == user_id) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def update_song_rating( + async def update_song_rating( self, user_id: int, song_id: int, chart_id: int, new_rating: int ) -> None: sql = best_score.update( @@ -254,14 +254,14 @@ class WaccaScoreData(BaseData): ) ).values(rating=new_rating) - result = self.execute(sql) + result = await self.execute(sql) if result is None: self.logger.error( f"update_song_rating: failed to update rating! user_id: {user_id} song_id: {song_id} chart_id {chart_id} new_rating {new_rating}" ) return None - def put_stageup( + async def put_stageup( self, user_id: int, version: int, @@ -292,7 +292,7 @@ class WaccaScoreData(BaseData): play_ct=stageup.c.play_ct + 1, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning( f"put_stageup: failed to update! user_id: {user_id} version: {version} stage_id: {stage_id}" @@ -300,17 +300,17 @@ class WaccaScoreData(BaseData): return None return result.lastrowid - def get_stageup(self, user_id: int, version: int) -> Optional[List[Row]]: + async def get_stageup(self, user_id: int, version: int) -> Optional[List[Row]]: sql = select(stageup).where( and_(stageup.c.user == user_id, stageup.c.version == version) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchall() - def get_stageup_stage( + async def get_stageup_stage( self, user_id: int, version: int, stage_id: int ) -> Optional[Row]: sql = select(stageup).where( @@ -321,7 +321,7 @@ class WaccaScoreData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() diff --git a/titles/wacca/schema/static.py b/titles/wacca/schema/static.py index 18e4e23..1714016 100644 --- a/titles/wacca/schema/static.py +++ b/titles/wacca/schema/static.py @@ -28,7 +28,7 @@ music = Table( class WaccaStaticData(BaseData): - def put_music( + async def put_music( self, version: int, song_id: int, @@ -61,13 +61,13 @@ class WaccaStaticData(BaseData): jacketFile=jacket, ) - result = self.execute(conflict) + result = await self.execute(conflict) if result is None: self.logger.warning(f"Failed to insert music {song_id} chart {chart_id}") return None return result.lastrowid - def get_music_chart( + async def get_music_chart( self, version: int, song_id: int, chart_id: int ) -> Optional[List[Row]]: sql = select(music).where( @@ -78,7 +78,7 @@ class WaccaStaticData(BaseData): ) ) - result = self.execute(sql) + result = await self.execute(sql) if result is None: return None return result.fetchone() From e27ac4b81f8d1336896f6779e08f006a2c82d062 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 9 Jan 2024 14:46:43 -0500 Subject: [PATCH 13/32] fix dockerfile --- Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f946002..81252b1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,6 @@ COPY dbutils.py dbutils.py COPY read.py read.py ADD core core ADD titles titles -ADD config config ADD logs logs ADD cert cert From 9dab26b1224d3cae5dc98a16fb297000d5a7fe22 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 9 Jan 2024 15:54:34 -0500 Subject: [PATCH 14/32] port frontend changes from different project --- core/app.py | 4 +- core/config.py | 2 +- core/data/schema/card.py | 6 +- core/data/schema/user.py | 4 +- core/frontend.py | 866 +++++++++++------- core/frontend/arcade/index.jinja | 4 - core/frontend/machine/index.jinja | 5 - core/frontend/sys/index.jinja | 103 --- core/frontend/user/index.jinja | 41 - core/frontend/widgets/err_banner.jinja | 18 - core/templates/arcade/index.jinja | 4 + .../{frontend => templates}/gate/create.jinja | 2 +- core/{frontend => templates}/gate/gate.jinja | 4 +- core/{frontend => templates}/index.jinja | 2 +- core/templates/machine/index.jinja | 5 + core/templates/sys/index.jinja | 69 ++ core/templates/user/index.jinja | 175 ++++ core/templates/widgets/err_banner.jinja | 29 + .../widgets/topbar.jinja | 13 +- titles/idac/frontend.py | 48 +- .../{frontend => templates}/idac_index.jinja | 6 +- .../js/idac_scripts.js | 0 titles/ongeki/frontend.py | 54 +- .../js/ongeki_scripts.js | 0 .../ongeki_index.jinja | 6 +- titles/pokken/frontend.py | 80 +- titles/pokken/frontend/pokken_index.jinja | 4 - titles/pokken/templates/pokken_index.jinja | 48 + titles/wacca/frontend.py | 26 +- titles/wacca/frontend/wacca_index.jinja | 4 - titles/wacca/templates/wacca_index.jinja | 4 + 31 files changed, 1062 insertions(+), 574 deletions(-) delete mode 100644 core/frontend/arcade/index.jinja delete mode 100644 core/frontend/machine/index.jinja delete mode 100644 core/frontend/sys/index.jinja delete mode 100644 core/frontend/user/index.jinja delete mode 100644 core/frontend/widgets/err_banner.jinja create mode 100644 core/templates/arcade/index.jinja rename core/{frontend => templates}/gate/create.jinja (94%) rename core/{frontend => templates}/gate/gate.jinja (91%) rename core/{frontend => templates}/index.jinja (98%) create mode 100644 core/templates/machine/index.jinja create mode 100644 core/templates/sys/index.jinja create mode 100644 core/templates/user/index.jinja create mode 100644 core/templates/widgets/err_banner.jinja rename core/{frontend => templates}/widgets/topbar.jinja (55%) rename titles/idac/{frontend => templates}/idac_index.jinja (97%) rename titles/idac/{frontend => templates}/js/idac_scripts.js (100%) rename titles/ongeki/{frontend => templates}/js/ongeki_scripts.js (100%) rename titles/ongeki/{frontend => templates}/ongeki_index.jinja (94%) delete mode 100644 titles/pokken/frontend/pokken_index.jinja create mode 100644 titles/pokken/templates/pokken_index.jinja delete mode 100644 titles/wacca/frontend/wacca_index.jinja create mode 100644 titles/wacca/templates/wacca_index.jinja diff --git a/core/app.py b/core/app.py index f20c041..0a59112 100644 --- a/core/app.py +++ b/core/app.py @@ -76,10 +76,12 @@ if not cfg.billing.standalone: Route("/request/", billing.handle_billing_request, methods=["POST"]), ] -if not cfg.frontend.standalone: +if not cfg.frontend.standalone and cfg.frontend.secret: frontend = FrontendServlet(cfg, cfg_dir) route_lst += frontend.get_routes() else: + if not cfg.frontend.secret: + logger.error("Frontend secret not specified, cannot start frontend!") route_lst.append(Route("/", dummy_rt)) route_lst.append(Route("/robots.txt", FrontendServlet.robots)) diff --git a/core/config.py b/core/config.py index 0a95742..47a3ff3 100644 --- a/core/config.py +++ b/core/config.py @@ -212,7 +212,7 @@ class FrontendConfig: @property def secret(self) -> str: - CoreConfig.get_config_field( + return CoreConfig.get_config_field( self.__config, "core", "frontend", "secret", default="" ) diff --git a/core/data/schema/card.py b/core/data/schema/card.py index 6953b26..011efe1 100644 --- a/core/data/schema/card.py +++ b/core/data/schema/card.py @@ -58,7 +58,7 @@ class CardData(BaseData): """ Given a 20 digit access code as a string, get the user id associated with that card """ - card = self.get_card_by_access_code(access_code) + card = await self.get_card_by_access_code(access_code) if card is None: return None @@ -68,7 +68,7 @@ class CardData(BaseData): """ Given a 20 digit access code as a string, check if the card is banned """ - card = self.get_card_by_access_code(access_code) + card = await self.get_card_by_access_code(access_code) if card is None: return None if card["is_banned"]: @@ -78,7 +78,7 @@ class CardData(BaseData): """ Given a 20 digit access code as a string, check if the card is locked """ - card = self.get_card_by_access_code(access_code) + card = await self.get_card_by_access_code(access_code) if card is None: return None if card["is_locked"]: diff --git a/core/data/schema/user.py b/core/data/schema/user.py index 6b733e2..f97bf3f 100644 --- a/core/data/schema/user.py +++ b/core/data/schema/user.py @@ -64,8 +64,8 @@ class UserData(BaseData): return False return result.fetchone() - def check_password(self, user_id: int, passwd: bytes = None) -> bool: - usr = self.get_user(user_id) + async def check_password(self, user_id: int, passwd: bytes = None) -> bool: + usr = await self.get_user(user_id) if usr is None: return False diff --git a/core/frontend.py b/core/frontend.py index 26796d3..b180c71 100644 --- a/core/frontend.py +++ b/core/frontend.py @@ -1,30 +1,21 @@ import logging, coloredlogs -from typing import Any, Dict, List -from twisted.web import resource -from twisted.web.util import redirectTo +from typing import Any, Dict, List, Union, Optional from starlette.requests import Request -from starlette.routing import Route -from starlette.responses import Response, PlainTextResponse +from starlette.routing import Route, Mount +from starlette.responses import Response, PlainTextResponse, RedirectResponse from logging.handlers import TimedRotatingFileHandler -from twisted.web.server import Session -from zope.interface import Interface, Attribute, implementer -from twisted.python.components import registerAdapter import jinja2 import bcrypt import re +import jwt +from base64 import b64decode from enum import Enum from urllib import parse +from datetime import datetime, timezone from core import CoreConfig, Utils from core.data import Data - -class IUserSession(Interface): - userId = Attribute("User's ID") - current_ip = Attribute("User's current ip address") - permissions = Attribute("User's permission level") - ongeki_version = Attribute("User's selected Ongeki Version") - class PermissionOffset(Enum): USER = 0 # Regular user USERMOD = 1 # Can moderate other users @@ -33,255 +24,399 @@ class PermissionOffset(Enum): # 4 - 6 reserved for future use OWNER = 7 # Can do anything -@implementer(IUserSession) -class UserSession(object): - def __init__(self, session): - self.userId = 0 - self.current_ip = "0.0.0.0" - self.permissions = 0 - self.ongeki_version = 7 +class ShopPermissionOffset(Enum): + VIEW = 0 # View info and cabs + BOOKKEEP = 1 # View bookeeping info + EDITOR = 2 # Can edit name, settings + REGISTRAR = 3 # Can add cabs + # 4 - 6 reserved for future use + OWNER = 7 # Can do anything +class ShopOwner(): + def __init__(self, usr_id: int = 0, usr_name: str = "", perms: int = 0) -> None: + self.user_id = usr_id + self.username = usr_name + self.permissions = perms -class FrontendServlet(resource.Resource): - def getChild(self, name: bytes, request: Request): - self.logger.debug(f"{Utils.get_ip_addr(request)} -> {name.decode()}") - if name == b"": - return self - return resource.Resource.getChild(self, name, request) +class UserSession(): + def __init__(self, usr_id: int = 0, ip: str = "", perms: int = 0, ongeki_ver: int = 7): + self.user_id = usr_id + self.current_ip = ip + self.permissions = perms + self.ongeki_version = ongeki_ver +class FrontendServlet(): def __init__(self, cfg: CoreConfig, config_dir: str) -> None: self.config = cfg log_fmt_str = "[%(asctime)s] Frontend | %(levelname)s | %(message)s" log_fmt = logging.Formatter(log_fmt_str) - self.logger = logging.getLogger("frontend") self.environment = jinja2.Environment(loader=jinja2.FileSystemLoader(".")) - self.game_list: List[Dict[str, str]] = [] - self.children: Dict[str, Any] = {} + self.game_list: Dict[str, Dict[str, Any]] = {} + self.sn_cvt: Dict[str, str] = {} + + self.logger = logging.getLogger("frontend") + if not hasattr(self.logger, "inited"): + fileHandler = TimedRotatingFileHandler( + "{0}/{1}.log".format(self.config.server.log_dir, "frontend"), + when="d", + backupCount=10, + ) + fileHandler.setFormatter(log_fmt) - fileHandler = TimedRotatingFileHandler( - "{0}/{1}.log".format(self.config.server.log_dir, "frontend"), - when="d", - backupCount=10, - ) - fileHandler.setFormatter(log_fmt) + consoleHandler = logging.StreamHandler() + consoleHandler.setFormatter(log_fmt) - consoleHandler = logging.StreamHandler() - consoleHandler.setFormatter(log_fmt) + self.logger.addHandler(fileHandler) + self.logger.addHandler(consoleHandler) - self.logger.addHandler(fileHandler) - self.logger.addHandler(consoleHandler) + self.logger.setLevel(cfg.frontend.loglevel) + coloredlogs.install( + level=cfg.frontend.loglevel, logger=self.logger, fmt=log_fmt_str + ) - self.logger.setLevel(cfg.frontend.loglevel) - coloredlogs.install( - level=cfg.frontend.loglevel, logger=self.logger, fmt=log_fmt_str - ) - registerAdapter(UserSession, Session, IUserSession) - - fe_game = FE_Game(cfg, self.environment) + self.logger.inited = True + games = Utils.get_all_titles() for game_dir, game_mod in games.items(): - if hasattr(game_mod, "frontend"): + if hasattr(game_mod, "frontend") and hasattr(game_mod, "index") and hasattr(game_mod, "game_codes"): try: - game_fe = game_mod.frontend(cfg, self.environment, config_dir) - self.game_list.append({"url": game_dir, "name": game_fe.nav_name}) - fe_game.putChild(game_dir.encode(), game_fe) + if game_mod.index.is_game_enabled(game_mod.game_codes[0], self.config, config_dir): + game_fe = game_mod.frontend(cfg, self.environment, config_dir) + self.game_list[game_fe.nav_name] = {"url": f"/{game_dir}", "class": game_fe } + + if hasattr(game_fe, "SN_PREFIX") and hasattr(game_fe, "NETID_PREFIX"): + if len(game_fe.SN_PREFIX) == len(game_fe.NETID_PREFIX): + for x in range(len(game_fe.SN_PREFIX)): + self.sn_cvt[game_fe.SN_PREFIX[x]] = game_fe.NETID_PREFIX[x] except Exception as e: self.logger.error( f"Failed to import frontend from {game_dir} because {e}" ) - self.environment.globals["game_list"] = self.game_list - self.putChild(b"gate", FE_Gate(cfg, self.environment)) - self.putChild(b"user", FE_User(cfg, self.environment)) - self.putChild(b"sys", FE_System(cfg, self.environment)) - self.putChild(b"arcade", FE_Arcade(cfg, self.environment)) - self.putChild(b"cab", FE_Machine(cfg, self.environment)) - self.putChild(b"game", fe_game) - self.logger.info( - f"Ready on port {self.config.server.port} serving {len(fe_game.children)} games" - ) + self.environment.globals["game_list"] = self.game_list + self.environment.globals["sn_cvt"] = self.sn_cvt + self.base = FE_Base(cfg, self.environment) + self.gate = FE_Gate(cfg, self.environment) + self.user = FE_User(cfg, self.environment) + self.system = FE_System(cfg, self.environment) + self.arcade = FE_Arcade(cfg, self.environment) + self.machine = FE_Machine(cfg, self.environment) - def get_routes(self) -> List[Route]: - return [] + def get_routes(self) -> List[Route]: + g_routes = [] + for nav_name, g_data in self.environment.globals["game_list"].items(): + g_routes.append(Mount(g_data['url'], routes=g_data['class'].get_routes())) + return [ + Route("/", self.base.render_GET, methods=['GET']), + Mount("/user", routes=[ + Route("/", self.user.render_GET, methods=['GET']), + Route("/{user_id:int}", self.user.render_GET, methods=['GET']), + Route("/update.pw", self.user.render_POST, methods=['POST']), + Route("/update.name", self.user.update_username, methods=['POST']), + Route("/edit.card", self.user.edit_card, methods=['POST']), + Route("/add.card", self.user.add_card, methods=['POST']), + Route("/logout", self.user.render_logout, methods=['GET']), + ]), + Mount("/gate", routes=[ + Route("/", self.gate.render_GET, methods=['GET', 'POST']), + Route("/gate.login", self.gate.render_login, methods=['POST']), + Route("/gate.create", self.gate.render_create, methods=['POST']), + Route("/create", self.gate.render_create_get, methods=['GET']), + ]), + Mount("/sys", routes=[ + Route("/", self.system.render_GET, methods=['GET']), + Route("/lookup.user", self.system.lookup_user, methods=['GET']), + Route("/lookup.shop", self.system.lookup_shop, methods=['GET']), + ]), + Mount("/shop", routes=[ + Route("/", self.arcade.render_GET, methods=['GET']), + Route("/{shop_id:int}", self.arcade.render_GET, methods=['GET']), + ]), + Mount("/cab", routes=[ + Route("/", self.machine.render_GET, methods=['GET']), + Route("/{machine_id:int}", self.machine.render_GET, methods=['GET']), + ]), + Mount("/game", routes=g_routes), + Route("/robots.txt", self.robots) + ] + + def startup(self) -> None: + self.config.update({ + "frontend": { + "standalone": True, + "loglevel": CoreConfig.loglevel_to_str(self.config.frontend.loglevel), + "secret": self.config.frontend.secret + } + }) + self.logger.info(f"Serving {len(self.game_list)} games") @classmethod async def robots(cls, request: Request) -> PlainTextResponse: return PlainTextResponse("User-agent: *\nDisallow: /\n\nUser-agent: AdsBot-Google\nDisallow: /") - async def render_GET(self, request): - self.logger.debug(f"{Utils.get_ip_addr(request)} -> {request.uri.decode()}") - template = self.environment.get_template("core/frontend/index.jinja") - return template.render( - server_name=self.config.server.name, - title=self.config.server.name, - game_list=self.game_list, - sesh=vars(IUserSession(request.getSession())), - ).encode("utf-16") - - -class FE_Base(resource.Resource): +class FE_Base(): """ A Generic skeleton class that all frontend handlers should inherit from Initializes the environment, data, logger, config, and sets isLeaf to true It is expected that game implementations of this class overwrite many of these """ - - isLeaf = True - def __init__(self, cfg: CoreConfig, environment: jinja2.Environment) -> None: self.core_config = cfg self.data = Data(cfg) self.logger = logging.getLogger("frontend") self.environment = environment - self.nav_name = "nav_name" + self.nav_name = "index" + + async def render_GET(self, request: Request): + self.logger.debug(f"{Utils.get_ip_addr(request)} -> {request.url}") + template = self.environment.get_template("core/templates/index.jinja") + sesh = self.validate_session(request) + resp = Response(template.render( + server_name=self.core_config.server.name, + title=self.core_config.server.name, + game_list=self.environment.globals["game_list"], + sesh=vars(sesh) if sesh is not None else vars(UserSession()), + )) + + if sesh is None: + resp.delete_cookie("DIANA_SESH") + return resp + + def get_routes(self) -> List[Route]: + return [] + + @classmethod + def test_perm(cls, permission: int, offset: Union[PermissionOffset, ShopPermissionOffset]) -> bool: + logging.getLogger('frontend').debug(f"{permission} vs {1 << offset.value}") + return permission & 1 << offset.value == 1 << offset.value + + @classmethod + def test_perm_minimum(cls, permission: int, offset: Union[PermissionOffset, ShopPermissionOffset]) -> bool: + return permission >= 1 << offset.value + + def decode_session(self, token: str) -> UserSession: + sesh = UserSession() + if not token: return sesh + try: + tk = jwt.decode(token, b64decode(self.core_config.frontend.secret), options={"verify_signature": True}, algorithms=["HS256"]) + sesh.user_id = tk['user_id'] + sesh.current_ip = tk['current_ip'] + sesh.permissions = tk['permissions'] + + if sesh.user_id <= 0: + self.logger.error("User session failed to validate due to an invalid ID!") + return UserSession() + return sesh + except jwt.ExpiredSignatureError: + self.logger.error("User session failed to validate due to an expired signature!") + return sesh + except jwt.InvalidSignatureError: + self.logger.error("User session failed to validate due to an invalid signature!") + return sesh + except jwt.DecodeError as e: + self.logger.error(f"User session failed to decode! {e}") + return sesh + except jwt.InvalidTokenError as e: + self.logger.error(f"User session is invalid! {e}") + return sesh + except KeyError as e: + self.logger.error(f"{e} missing from User session!") + return UserSession() + except Exception as e: + self.logger.error(f"Unknown exception occoured when decoding User session! {e}") + return UserSession() + + def validate_session(self, request: Request) -> Optional[UserSession]: + sesh = request.cookies.get('DIANA_SESH', "") + if not sesh: + return None + + usr_sesh = self.decode_session(sesh) + req_ip = Utils.get_ip_addr(request) + + if usr_sesh.current_ip != req_ip: + self.logger.error(f"User session failed to validate due to mismatched IPs! {usr_sesh.current_ip} -> {req_ip}") + return None + if usr_sesh.permissions <= 0 or usr_sesh.permissions > 255: + self.logger.error(f"User session failed to validate due to an invalid permission value! {usr_sesh.permissions}") + return None + + return usr_sesh + + def encode_session(self, sesh: UserSession, exp_seconds: int = 86400) -> str: + try: + return jwt.encode({ "user_id": sesh.user_id, "current_ip": sesh.current_ip, "permissions": sesh.permissions, "ongeki_version": sesh.ongeki_version, "exp": int(datetime.now(tz=timezone.utc).timestamp()) + exp_seconds }, b64decode(self.core_config.frontend.secret), algorithm="HS256") + except jwt.InvalidKeyError: + self.logger.error("Failed to encode User session because the secret is invalid!") + return "" + except Exception as e: + self.logger.error(f"Unknown exception occoured when encoding User session! {e}") + return "" class FE_Gate(FE_Base): - def render_GET(self, request: Request): - self.logger.debug(f"{Utils.get_ip_addr(request)} -> {request.uri.decode()}") - uri: str = request.uri.decode() + async def render_GET(self, request: Request): + self.logger.debug(f"{Utils.get_ip_addr(request)} -> {request.url.path}") - sesh = request.getSession() - usr_sesh = IUserSession(sesh) - if usr_sesh.userId > 0: - return redirectTo(b"/user", request) - - if uri.startswith("/gate/create"): - return self.create_user(request) - - if b"e" in request.args: + usr_sesh = self.validate_session(request) + if usr_sesh and usr_sesh.user_id > 0: + return RedirectResponse("/user/", 303) + + + if "e" in request.query_params: try: - err = int(request.args[b"e"][0].decode()) + err = int(request.query_params.get("e", ["0"])[0]) except Exception: err = 0 else: err = 0 - template = self.environment.get_template("core/frontend/gate/gate.jinja") - return template.render( + template = self.environment.get_template("core/templates/gate/gate.jinja") + resp = Response(template.render( title=f"{self.core_config.server.name} | Login Gate", error=err, - sesh=vars(usr_sesh), - ).encode("utf-16") - - async def render_POST(self, request: Request): - uri = request.uri.decode() + sesh=vars(UserSession()), + )) + resp.delete_cookie("DIANA_SESH") + return resp + + async def render_login(self, request: Request): ip = Utils.get_ip_addr(request) + frm = await request.form() + access_code: str = frm.get("access_code", None) + if not access_code: + return RedirectResponse("/gate/?e=1", 303) + + passwd: bytes = frm.get("passwd", "").encode() + if passwd == b"": + passwd = None - if uri == "/gate/gate.login": - access_code: str = request.args[b"access_code"][0].decode() - passwd: bytes = request.args[b"passwd"][0] - if passwd == b"": - passwd = None + uid = await self.data.card.get_user_id_from_card(access_code) + if uid is None: + self.logger.debug(f"Failed to find user for card {access_code}") + return RedirectResponse("/gate/?e=1", 303) - uid = await self.data.card.get_user_id_from_card(access_code) - user = await self.data.user.get_user(uid) - if uid is None: - return redirectTo(b"/gate?e=1", request) + user = await self.data.user.get_user(uid) + if user is None: + self.logger.error(f"Failed to load user {uid}") + return RedirectResponse("/gate/?e=1", 303) - if passwd is None: - sesh = await self.data.user.check_password(uid) + if passwd is None: + sesh = await self.data.user.check_password(uid) - if sesh is not None: - return redirectTo( - f"/gate/create?ac={access_code}".encode(), request - ) - return redirectTo(b"/gate?e=1", request) + if sesh is not None: + return RedirectResponse(f"/gate/create?ac={access_code}") + + return RedirectResponse("/gate/?e=1", 303) - if not self.data.user.check_password(uid, passwd): - return redirectTo(b"/gate?e=1", request) + if not await self.data.user.check_password(uid, passwd): + self.logger.debug(f"Failed password for access code {access_code}") + return RedirectResponse("/gate/?e=1", 303) - self.logger.info(f"Successful login of user {uid} at {ip}") + self.logger.info(f"Successful login of user {uid} at {ip}") - sesh = request.getSession() - usr_sesh = IUserSession(sesh) - usr_sesh.userId = uid - usr_sesh.current_ip = ip - usr_sesh.permissions = user['permissions'] + sesh = UserSession() + sesh.user_id = uid + sesh.current_ip = ip + sesh.permissions = user['permissions'] + + usr_sesh = self.encode_session(sesh) + self.logger.debug(f"Created session with JWT {usr_sesh}") + resp = RedirectResponse("/user/", 303) + resp.set_cookie("DIANA_SESH", usr_sesh) - return redirectTo(b"/user", request) + return resp - elif uri == "/gate/gate.create": - access_code: str = request.args[b"access_code"][0].decode() - username: str = request.args[b"username"][0] - email: str = request.args[b"email"][0].decode() - passwd: bytes = request.args[b"passwd"][0] + async def render_create(self, request: Request): + ip = Utils.get_ip_addr(request) + access_code: str = request.query_params.get("access_code", "") + username: str = request.query_params.get("username", "") + email: str = request.query_params.get("email", "") + passwd: bytes = request.query_params.get("passwd", "").encode() - uid = await self.data.card.get_user_id_from_card(access_code) - if uid is None: - return redirectTo(b"/gate?e=1", request) + if not access_code or not username or not email or not passwd: + return RedirectResponse("/gate/?e=1", 303) - salt = bcrypt.gensalt() - hashed = bcrypt.hashpw(passwd, salt) + uid = await self.data.card.get_user_id_from_card(access_code) + if uid is None: + return RedirectResponse("/gate/?e=1", 303) - result = await self.data.user.create_user( - uid, username, email.lower(), hashed.decode(), 1 - ) - if result is None: - return redirectTo(b"/gate?e=3", request) + salt = bcrypt.gensalt() + hashed = bcrypt.hashpw(passwd, salt) - if not self.data.user.check_password(uid, passwd): - return redirectTo(b"/gate", request) + result = await self.data.user.create_user( + uid, username, email.lower(), hashed.decode(), 1 + ) + if result is None: + return RedirectResponse("/gate/?e=3", 303) - return redirectTo(b"/user", request) + if not await self.data.user.check_password(uid, passwd): + return RedirectResponse("/gate/", 303) + + sesh = UserSession() + sesh.user_id = uid + sesh.current_ip = ip + sesh.permissions = 1 + + usr_sesh = self.encode_session(sesh) + self.logger.debug(f"Created session with JWT {usr_sesh}") + resp = RedirectResponse("/user", 303) + resp.set_cookie("DIANA_SESH", usr_sesh) - else: - return b"" + return resp - async def create_user(self, request: Request): - if b"ac" not in request.args or len(request.args[b"ac"][0].decode()) != 20: - return redirectTo(b"/gate?e=2", request) + async def render_create_get(self, request: Request): + ac = request.query_params.get(b"ac", [b""])[0].decode() + if len(ac) != 20: + return RedirectResponse("/gate/?e=2", 303) - ac = request.args[b"ac"][0].decode() card = await self.data.card.get_card_by_access_code(ac) if card is None: - return redirectTo(b"/gate?e=1", request) + return RedirectResponse("/gate/?e=1", 303) user = await self.data.user.get_user(card['user']) if user is None: self.logger.warning(f"Card {ac} exists with no/invalid associated user ID {card['user']}") - return redirectTo(b"/gate?e=0", request) + return RedirectResponse("/gate/?e=0", 303) if user['password'] is not None: - return redirectTo(b"/gate?e=1", request) + return RedirectResponse("/gate/?e=1", 303) - template = self.environment.get_template("core/frontend/gate/create.jinja") - return template.render( + template = self.environment.get_template("core/templates/gate/create.jinja") + return Response(template.render( title=f"{self.core_config.server.name} | Create User", code=ac, - sesh={"userId": 0, "permissions": 0}, - ).encode("utf-16") - + sesh={"user_id": 0, "permissions": 0}, + )) class FE_User(FE_Base): async def render_GET(self, request: Request): - uri = request.uri.decode() - template = self.environment.get_template("core/frontend/user/index.jinja") + uri = request.url.path + user_id = request.path_params.get('user_id', None) + self.logger.debug(f"{Utils.get_ip_addr(request)} -> {uri}") + template = self.environment.get_template("core/templates/user/index.jinja") - sesh: Session = request.getSession() - usr_sesh = IUserSession(sesh) - if usr_sesh.userId == 0: - return redirectTo(b"/gate", request) + usr_sesh = self.validate_session(request) + if not usr_sesh: + return RedirectResponse("/gate/", 303) - m = re.match("\/user\/(\d*)", uri) - - if m is not None: - usrid = m.group(1) - if usr_sesh.permissions < 1 << PermissionOffset.USERMOD.value or not usrid == usr_sesh.userId: - return redirectTo(b"/user", request) + if user_id: + if not self.test_perm(usr_sesh.permissions, PermissionOffset.USERMOD) and user_id != usr_sesh.user_id: + self.logger.warn(f"User {usr_sesh.user_id} does not have permission to view user {user_id}") + return RedirectResponse("/user/", 303) else: - usrid = usr_sesh.userId + user_id = usr_sesh.user_id - user = await self.data.user.get_user(usrid) + user = await self.data.user.get_user(user_id) if user is None: - return redirectTo(b"/user", request) + self.logger.debug(f"User {user_id} not found") + return RedirectResponse("/user/", 303) - cards = await self.data.card.get_user_cards(usrid) - arcades = await self.data.arcade.get_arcades_managed_by_user(usrid) + cards = await self.data.card.get_user_cards(user_id) card_data = [] arcade_data = [] @@ -294,176 +429,285 @@ class FE_User(FE_Base): else: status = 'Active' - card_data.append({'access_code': c['access_code'], 'status': status}) - - for a in arcades: - arcade_data.append({'id': a['id'], 'name': a['name']}) + idm = c['idm'] + ac = c['access_code'] - return template.render( + if ac.startswith("5") or idm is not None: + c_type = "AmusementIC" + elif ac.startswith("3"): + c_type = "Banapass" + elif ac.startswith("010"): + c_type = "Unknown Aime" + desc, _ = self.data.card.get_aime_ac_key_desc(ac) + if desc is not None: + c_type = desc + else: + c_type = "Unknown" + + card_data.append({'access_code': ac, 'status': status, 'chip_id': None if c['chip_id'] is None else f"{c['chip_id']:X}", 'idm': idm, 'type': c_type, "memo": ""}) + + if "e" in request.query_params: + try: + err = int(request.query_params.get("e", 0)) + except Exception: + err = 0 + + else: + err = 0 + + if "s" in request.query_params: + try: + succ = int(request.query_params.get("s", 0)) + except Exception: + succ = 0 + + else: + succ = 0 + + return Response(template.render( title=f"{self.core_config.server.name} | Account", sesh=vars(usr_sesh), - cards=card_data, + cards=card_data, + error=err, + success=succ, username=user['username'], arcades=arcade_data - ).encode("utf-16") + )) + + async def render_logout(self, request: Request): + resp = RedirectResponse("/gate/", 303) + resp.delete_cookie("DIANA_SESH") + return resp + + async def edit_card(self, request: Request) -> RedirectResponse: + return RedirectResponse("/user/", 303) + + async def add_card(self, request: Request) -> RedirectResponse: + return RedirectResponse("/user/", 303) async def render_POST(self, request: Request): - pass + frm = await request.form() + usr_sesh = self.validate_session(request) + if not usr_sesh or not self.test_perm(usr_sesh.permissions, PermissionOffset.USERMOD): + return RedirectResponse("/gate/", 303) + + old_pw: str = frm.get('current_pw', None) + pw1: str = frm.get('password1', None) + pw2: str = frm.get('password2', None) + if old_pw is None or pw1 is None or pw2 is None: + return RedirectResponse("/user/?e=4", 303) + + if pw1 != pw2: + return RedirectResponse("/user/?e=6", 303) + + if not await self.data.user.check_password(usr_sesh.user_id, old_pw.encode()): + return RedirectResponse("/user/?e=5", 303) + + if len(pw1) < 10 or not any(ele.isupper() for ele in pw1) or not any(ele.islower() for ele in pw1) \ + or not any(ele.isdigit() for ele in pw1) or not any(not ele.isalnum() for ele in pw1): + return RedirectResponse("/user/?e=7", 303) + + salt = bcrypt.gensalt() + hashed = bcrypt.hashpw(pw1.encode(), salt) + if not await self.data.user.change_password(usr_sesh.user_id, hashed.decode()): + return RedirectResponse("/gate/?e=1", 303) + + return RedirectResponse("/user/?s=1", 303) + + async def update_username(self, request: Request): + frm = await request.form() + new_name: bytes = frm.get('new_name', "") + usr_sesh = self.validate_session(request) + if not usr_sesh or not self.test_perm(usr_sesh.permissions, PermissionOffset.USERMOD): + return RedirectResponse("/gate/", 303) + + if new_name is None or not new_name: + return RedirectResponse("/user/?e=4", 303) + + if len(new_name) > 10: + return RedirectResponse("/user/?e=8", 303) + + if not await self.data.user.change_username(usr_sesh.user_id, new_name): + return RedirectResponse("/user/?e=8", 303) + + return RedirectResponse("/user/?s=2", 303) class FE_System(FE_Base): async def render_GET(self, request: Request): - uri = request.uri.decode() - template = self.environment.get_template("core/frontend/sys/index.jinja") + template = self.environment.get_template("core/templates/sys/index.jinja") + self.logger.debug(f"{Utils.get_ip_addr(request)} -> {request.url.path}") + + usr_sesh = self.validate_session(request) + if not usr_sesh or not self.test_perm_minimum(usr_sesh.permissions, PermissionOffset.USERMOD): + return RedirectResponse("/gate/", 303) + + return Response(template.render( + title=f"{self.core_config.server.name} | System", + sesh=vars(usr_sesh), + usrlist=[], + )) + + async def lookup_user(self, request: Request): + template = self.environment.get_template("core/templates/sys/index.jinja") usrlist: List[Dict] = [] - aclist: List[Dict] = [] - cablist: List[Dict] = [] - - sesh: Session = request.getSession() - usr_sesh = IUserSession(sesh) - if usr_sesh.userId == 0 or usr_sesh.permissions < 1 << PermissionOffset.USERMOD.value: - return redirectTo(b"/gate", request) + usr_sesh = self.validate_session(request) + if not usr_sesh or not self.test_perm(usr_sesh.permissions, PermissionOffset.USERMOD): + return RedirectResponse("/gate/", 303) - if uri.startswith("/sys/lookup.user?"): - uri_parse = parse.parse_qs(uri.replace("/sys/lookup.user?", "")) # lop off the first bit - uid_search = uri_parse.get("usrId") - email_search = uri_parse.get("usrEmail") - uname_search = uri_parse.get("usrName") + uid_search = request.query_params.get("usrId", None) + email_search = request.query_params.get("usrEmail", None) + uname_search = request.query_params.get("usrName", None) - if uid_search is not None: - u = await self.data.user.get_user(uid_search[0]) - if u is not None: - usrlist.append(u._asdict()) + if uid_search: + u = await self.data.user.get_user(uid_search) + if u is not None: + usrlist.append(u._asdict()) - elif email_search is not None: - u = await self.data.user.find_user_by_email(email_search[0]) - if u is not None: - usrlist.append(u._asdict()) + elif email_search: + u = await self.data.user.find_user_by_email(email_search) + if u is not None: + usrlist.append(u._asdict()) - elif uname_search is not None: - ul = await self.data.user.find_user_by_username(uname_search[0]) - for u in ul: - usrlist.append(u._asdict()) + elif uname_search: + ul = await self.data.user.find_user_by_username(uname_search) + for u in ul: + usrlist.append(u._asdict()) - elif uri.startswith("/sys/lookup.arcade?"): - uri_parse = parse.parse_qs(uri.replace("/sys/lookup.arcade?", "")) # lop off the first bit - ac_id_search = uri_parse.get("arcadeId") - ac_name_search = uri_parse.get("arcadeName") - ac_user_search = uri_parse.get("arcadeUser") - ac_ip_search = uri_parse.get("arcadeIp") - - if ac_id_search is not None: - u = await self.data.arcade.get_arcade(ac_id_search[0]) - if u is not None: - aclist.append(u._asdict()) - - elif ac_name_search is not None: - ul = await self.data.arcade.get_arcade_by_name(ac_name_search[0]) - if ul is not None: - for u in ul: - aclist.append(u._asdict()) - - elif ac_user_search is not None: - ul = await self.data.arcade.get_arcades_managed_by_user(ac_user_search[0]) - if ul is not None: - for u in ul: - aclist.append(u._asdict()) - - elif ac_ip_search is not None: - ul = await self.data.arcade.get_arcades_by_ip(ac_ip_search[0]) - if ul is not None: - for u in ul: - aclist.append(u._asdict()) - - elif uri.startswith("/sys/lookup.cab?"): - uri_parse = parse.parse_qs(uri.replace("/sys/lookup.cab?", "")) # lop off the first bit - cab_id_search = uri_parse.get("cabId") - cab_serial_search = uri_parse.get("cabSerial") - cab_acid_search = uri_parse.get("cabAcId") - - if cab_id_search is not None: - u = await self.data.arcade.get_machine(id=cab_id_search[0]) - if u is not None: - cablist.append(u._asdict()) - - elif cab_serial_search is not None: - u = await self.data.arcade.get_machine(serial=cab_serial_search[0]) - if u is not None: - cablist.append(u._asdict()) - - elif cab_acid_search is not None: - ul = await self.data.arcade.get_arcade_machines(cab_acid_search[0]) - for u in ul: - cablist.append(u._asdict()) - - return template.render( + return Response(template.render( title=f"{self.core_config.server.name} | System", sesh=vars(usr_sesh), usrlist=usrlist, - aclist=aclist, - cablist=cablist, - ).encode("utf-16") - - -class FE_Game(FE_Base): - isLeaf = False - children: Dict[str, Any] = {} - - def getChild(self, name: bytes, request: Request): - if name == b"": - return self - return resource.Resource.getChild(self, name, request) - - async def render_GET(self, request: Request) -> bytes: - return redirectTo(b"/user", request) + shoplist=[], + )) + async def lookup_shop(self, request: Request): + shoplist = [] + template = self.environment.get_template("core/templates/sys/index.jinja") + + usr_sesh = self.validate_session(request) + if not usr_sesh or not self.test_perm(usr_sesh.permissions, PermissionOffset.ACMOD): + return RedirectResponse("/gate/", 303) + + shopid_search = request.query_params.get("shopId", None) + sn_search = request.query_params.get("serialNum", None) + + if shopid_search: + if shopid_search.isdigit(): + shopid_search = int(shopid_search) + try: + sinfo = await self.data.arcade.get_arcade(shopid_search) + except Exception as e: + self.logger.error(f"Failed to fetch shop info for shop {shopid_search} in lookup_shop - {e}") + sinfo = None + if sinfo: + shoplist.append({ + "name": sinfo.name, + "id": sinfo.allnet_id + }) + + else: + return Response(template.render( + title=f"{self.core_config.server.name} | System", + sesh=vars(usr_sesh), + usrlist=[], + shoplist=shoplist, + error=4 + )) + + if sn_search: + sn_search = sn_search.upper().replace("-", "").strip() + if sn_search.isdigit() and len(sn_search) == 12: + prefix = sn_search[:4] + suffix = sn_search[5:] + + netid_prefix = self.environment.globals["sn_cvt"].get(prefix, "") + sn_search = netid_prefix + suffix + + if re.match("^AB[D|G|L]N\d{7}$", sn_search): + cabinfo = await self.data.arcade.get_machine(sn_search) + if cabinfo is None: sinfo = None + else: + sinfo = await self.data.arcade.get_arcade(cabinfo['arcade']) + if sinfo: + shoplist.append({ + "name": sinfo['name'], + "id": sinfo['id'] + }) + + else: + return Response(template.render( + title=f"{self.core_config.server.name} | System", + sesh=vars(usr_sesh), + usrlist=[], + shoplist=shoplist, + error=10 + )) + + + return Response(template.render( + title=f"{self.core_config.server.name} | System", + sesh=vars(usr_sesh), + usrlist=[], + shoplist=shoplist, + )) class FE_Arcade(FE_Base): async def render_GET(self, request: Request): - uri = request.uri.decode() - template = self.environment.get_template("core/frontend/arcade/index.jinja") - managed = [] + template = self.environment.get_template("core/templates/arcade/index.jinja") + shop_id = request.path_params.get('shop_id', None) - sesh: Session = request.getSession() - usr_sesh = IUserSession(sesh) - if usr_sesh.userId == 0: - return redirectTo(b"/gate", request) + usr_sesh = self.validate_session(request) + if not usr_sesh or not self.test_perm(usr_sesh.permissions, PermissionOffset.ACMOD): + self.logger.warn(f"User {usr_sesh.user_id} does not have permission to view shops!") + return RedirectResponse("/gate/", 303) - m = re.match("\/arcade\/(\d*)", uri) + if not shop_id: + return Response(template.render( + title=f"{self.core_config.server.name} | Arcade", + sesh=vars(usr_sesh), + )) - if m is not None: - arcadeid = m.group(1) - perms = await self.data.arcade.get_manager_permissions(usr_sesh.userId, arcadeid) - arcade = await self.data.arcade.get_arcade(arcadeid) - - if perms is None: - perms = 0 + try: + sinfo = await self.data.arcade.get_arcade(shop_id) + except Exception as e: + self.logger.error(f"Failed to fetch shop info for shop {shop_id} in render_GET - {e}") + sinfo = None + if not sinfo: + return Response(template.render( + title=f"{self.core_config.server.name} | Arcade", + sesh=vars(usr_sesh), + )) - else: - return redirectTo(b"/user", request) - - return template.render( + return Response(template.render( title=f"{self.core_config.server.name} | Arcade", sesh=vars(usr_sesh), - error=0, - perms=perms, - arcade=arcade._asdict() - ).encode("utf-16") - + arcade={ + "name": sinfo['name'], + "id": sinfo['id'], + "cabs": [] + } + + )) class FE_Machine(FE_Base): async def render_GET(self, request: Request): - uri = request.uri.decode() - template = self.environment.get_template("core/frontend/machine/index.jinja") + template = self.environment.get_template("core/templates/machine/index.jinja") + cab_id = request.path_params.get('cab_id', None) - sesh: Session = request.getSession() - usr_sesh = IUserSession(sesh) - if usr_sesh.userId == 0: - return redirectTo(b"/gate", request) + usr_sesh = self.validate_session(request) + if not usr_sesh or not self.test_perm(usr_sesh.permissions, PermissionOffset.ACMOD): + self.logger.warn(f"User {usr_sesh.user_id} does not have permission to view shops!") + return RedirectResponse("/gate/", 303) - return template.render( + if not cab_id: + return Response(template.render( + title=f"{self.core_config.server.name} | Machine", + sesh=vars(usr_sesh), + )) + + return Response(template.render( title=f"{self.core_config.server.name} | Machine", - sesh=vars(usr_sesh), - arcade={}, - error=0, - ).encode("utf-16") \ No newline at end of file + sesh=vars(usr_sesh), + arcade={} + )) \ No newline at end of file diff --git a/core/frontend/arcade/index.jinja b/core/frontend/arcade/index.jinja deleted file mode 100644 index 20a1f46..0000000 --- a/core/frontend/arcade/index.jinja +++ /dev/null @@ -1,4 +0,0 @@ -{% extends "core/frontend/index.jinja" %} -{% block content %} -

{{ arcade.name }}

-{% endblock content %} \ No newline at end of file diff --git a/core/frontend/machine/index.jinja b/core/frontend/machine/index.jinja deleted file mode 100644 index 01e90a0..0000000 --- a/core/frontend/machine/index.jinja +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "core/frontend/index.jinja" %} -{% block content %} -{% include "core/frontend/widgets/err_banner.jinja" %} -

Machine Management

-{% endblock content %} \ No newline at end of file diff --git a/core/frontend/sys/index.jinja b/core/frontend/sys/index.jinja deleted file mode 100644 index 120051a..0000000 --- a/core/frontend/sys/index.jinja +++ /dev/null @@ -1,103 +0,0 @@ -{% extends "core/frontend/index.jinja" %} -{% block content %} -

System Management

- -
- {% if sesh.permissions >= 2 %} -
-
-

User Search

-
- - -
- OR -
- - -
- OR -
- - -
-
- -
-
- {% endif %} - {% if sesh.permissions >= 4 %} -
-
-

Arcade Search

-
- - -
- OR -
- - -
- OR -
- - -
- OR -
- - -
-
- -
-
-
-
-

Machine Search

-
- - -
- OR -
- - -
- OR -
- - -
-
- -
-
- {% endif %} -
-
- {% if sesh.permissions >= 2 %} - - {% endif %} - {% if sesh.permissions >= 4 %} - - {% endif %} -
-
- -
-{% endblock content %} \ No newline at end of file diff --git a/core/frontend/user/index.jinja b/core/frontend/user/index.jinja deleted file mode 100644 index 2f76b14..0000000 --- a/core/frontend/user/index.jinja +++ /dev/null @@ -1,41 +0,0 @@ -{% extends "core/frontend/index.jinja" %} -{% block content %} -

Management for {{ username }}

-

Cards

-
    -{% for c in cards %} -
  • {{ c.access_code }}: {{ c.status }} {% if c.status == 'Active'%}{% elif c.status == 'Locked' %}{% endif %} 
  • -{% endfor %} -
- -{% if arcades is defined %} -

Arcades

- -{% endif %} - - - -{% endblock content %} \ No newline at end of file diff --git a/core/frontend/widgets/err_banner.jinja b/core/frontend/widgets/err_banner.jinja deleted file mode 100644 index 6d239f6..0000000 --- a/core/frontend/widgets/err_banner.jinja +++ /dev/null @@ -1,18 +0,0 @@ -{% if error > 0 %} -
-

Error

-{% if error == 1 %} -Card not registered, or wrong password -{% elif error == 2 %} -Missing or malformed access code -{% elif error == 3 %} -Failed to create user -{% elif error == 4 %} -Arcade not found -{% elif error == 5 %} -Machine not found -{% else %} -An unknown error occoured -{% endif %} -
-{% endif %} \ No newline at end of file diff --git a/core/templates/arcade/index.jinja b/core/templates/arcade/index.jinja new file mode 100644 index 0000000..627f203 --- /dev/null +++ b/core/templates/arcade/index.jinja @@ -0,0 +1,4 @@ +{% extends "core/templates/index.jinja" %} +{% block content %} +

{{ arcade.name }}

+{% endblock content %} \ No newline at end of file diff --git a/core/frontend/gate/create.jinja b/core/templates/gate/create.jinja similarity index 94% rename from core/frontend/gate/create.jinja rename to core/templates/gate/create.jinja index f8b5e51..1dfa2f8 100644 --- a/core/frontend/gate/create.jinja +++ b/core/templates/gate/create.jinja @@ -1,4 +1,4 @@ -{% extends "core/frontend/index.jinja" %} +{% extends "core/templates/index.jinja" %} {% block content %}

Create User

diff --git a/core/frontend/gate/gate.jinja b/core/templates/gate/gate.jinja similarity index 91% rename from core/frontend/gate/gate.jinja rename to core/templates/gate/gate.jinja index 90abb98..ca3e2eb 100644 --- a/core/frontend/gate/gate.jinja +++ b/core/templates/gate/gate.jinja @@ -1,7 +1,7 @@ -{% extends "core/frontend/index.jinja" %} +{% extends "core/templates/index.jinja" %} {% block content %}

Gate

-{% include "core/frontend/widgets/err_banner.jinja" %} +{% include "core/templates/widgets/err_banner.jinja" %} - {% include "core/frontend/widgets/topbar.jinja" %} + {% include "core/templates/widgets/topbar.jinja" %} {% block content %}

{{ server_name }}

{% endblock content %} diff --git a/core/templates/machine/index.jinja b/core/templates/machine/index.jinja new file mode 100644 index 0000000..38a40a7 --- /dev/null +++ b/core/templates/machine/index.jinja @@ -0,0 +1,5 @@ +{% extends "core/templates/index.jinja" %} +{% block content %} +{% include "core/templates/widgets/err_banner.jinja" %} +

Machine Management

+{% endblock content %} \ No newline at end of file diff --git a/core/templates/sys/index.jinja b/core/templates/sys/index.jinja new file mode 100644 index 0000000..08443c0 --- /dev/null +++ b/core/templates/sys/index.jinja @@ -0,0 +1,69 @@ +{% extends "core/templates/index.jinja" %} +{% block content %} +

System Management

+{% if error is defined %} +{% include "core/templates/widgets/err_banner.jinja" %} +{% endif %} +
+ {% if "{:08b}".format(sesh.permissions)[6] == "1" %} +
+ +

User Search

+
+ + +
+ OR +
+ + +
+ OR +
+ + +
+
+ + +
+ {% endif %} + {% if "{:08b}".format(sesh.permissions)[5] == "1" %} +
+
+

Shop search

+
+ + +
+ OR +
+ + +
+
+ +
+
+ {% endif %} +
+
+ {% if "{:08b}".format(sesh.permissions)[6] == "1" %} +
+ {% for usr in usrlist %} +
{{ usr.username if usr.username is not none else "No Name Set"}}
+ {% endfor %} +
+ {% endif %} + {% if "{:08b}".format(sesh.permissions)[5] == "1" %} +
+ {% for shop in shoplist %} +
{{ shop.name if shop.name else "No Name Set"}}
+ {% endfor %} +
+ {% endif %} +
+
+ +
+{% endblock content %} \ No newline at end of file diff --git a/core/templates/user/index.jinja b/core/templates/user/index.jinja new file mode 100644 index 0000000..4276a81 --- /dev/null +++ b/core/templates/user/index.jinja @@ -0,0 +1,175 @@ +{% extends "core/templates/index.jinja" %} +{% block content %} + +

Management for {{ username }} 

+{% if error is defined %} +{% include "core/templates/widgets/err_banner.jinja" %} +{% endif %} +{% if success is defined and success == 2 %} +
+Update successful +
+{% endif %} + +

+

Cards

+{% if success is defined and success == 3 %} +
+Card added successfully +
+{% endif %} + +
    +{% for c in cards %} +
  • {{ c.access_code }} ({{ c.type}}): {{ c.status }}  {% if c.status == 'Active'%}{% elif c.status == 'Locked' %}{% endif %} 
  • +{% endfor %} +
+ +

Reset Password

+{% if success is defined and success == 1 %} +
+Update successful +
+{% endif %} +
+
+ + +
+
+ + +
Password must be at least 10 characters long, contain an upper and lowercase character, number, and special character
+
+
+ + +
+ +
+ +{% if arcades is defined and arcades|length > 0 %} +

Arcades

+
    + {% for a in arcades %} +
  • {{ a.name }}

    + {% if a.machines|length > 0 %} + + + {% for m in a.machines %} + + {% endfor %} +
    SerialGameLast Seen
    {{ m.serial }}{{ m.game }}{{ m.last_seen }}
    + {% endif %} +
  • + {% endfor %} +
+{% endif %} + + + +{% endblock content %} \ No newline at end of file diff --git a/core/templates/widgets/err_banner.jinja b/core/templates/widgets/err_banner.jinja new file mode 100644 index 0000000..bb0e0eb --- /dev/null +++ b/core/templates/widgets/err_banner.jinja @@ -0,0 +1,29 @@ +{% if error > 0 %} +
+

Error

+{% if error == 1 %} +Card not registered, or wrong password +{% elif error == 2 %} +Missing or malformed access code +{% elif error == 3 %} +Failed to create user +{% elif error == 4 %} +Required field not filled or invalid +{% elif error == 5 %} +Incorrect old password +{% elif error == 6 %} +Passwords don't match +{% elif error == 7 %} +New password not acceptable +{% elif error == 8 %} +New Nickname too long +{% elif error == 9 %} +You must be logged in to preform this action +New Nickname too long +{% elif error == 10 %} +Invalid serial number +{% else %} +An unknown error occoured +{% endif %} +
+{% endif %} \ No newline at end of file diff --git a/core/frontend/widgets/topbar.jinja b/core/templates/widgets/topbar.jinja similarity index 55% rename from core/frontend/widgets/topbar.jinja rename to core/templates/widgets/topbar.jinja index 1a85873..332173d 100644 --- a/core/frontend/widgets/topbar.jinja +++ b/core/templates/widgets/topbar.jinja @@ -3,19 +3,20 @@
  - {% for game in game_list %} -   + {% for game, data in game_list|items %} +   {% endfor %}
{% if sesh is defined and sesh["permissions"] >= 2 %} - + {% endif %} - {% if sesh is defined and sesh["userId"] > 0 %} - + {% if sesh is defined and sesh["user_id"] > 0 %} + + {% else %} - + {% endif %}
\ No newline at end of file diff --git a/titles/idac/frontend.py b/titles/idac/frontend.py index b5ac3ad..88f2b44 100644 --- a/titles/idac/frontend.py +++ b/titles/idac/frontend.py @@ -1,12 +1,13 @@ import json +from typing import List +from starlette.routing import Route +from starlette.responses import Response, RedirectResponse import yaml import jinja2 from os import path -from twisted.web.util import redirectTo from starlette.requests import Request -from twisted.web.server import Session -from core.frontend import FE_Base, IUserSession +from core.frontend import FE_Base, UserSession from core.config import CoreConfig from titles.idac.database import IDACData from titles.idac.schema.profile import * @@ -26,7 +27,8 @@ class IDACFrontend(FE_Base): self.game_cfg.update( yaml.safe_load(open(f"{cfg_dir}/{IDACConstants.CONFIG_NAME}")) ) - self.nav_name = "頭文字D THE ARCADE" + #self.nav_name = "頭文字D THE ARCADE" + self.nav_name = "IDAC" # TODO: Add version list self.version = IDACConstants.VER_IDAC_SEASON_2 @@ -36,6 +38,11 @@ class IDACFrontend(FE_Base): 25: "full_tune_tickets", 34: "full_tune_fragments", } + + def get_routes(self) -> List[Route]: + return [ + Route("/", self.render_GET) + ] async def generate_all_tables_json(self, user_id: int): json_export = {} @@ -87,35 +94,32 @@ class IDACFrontend(FE_Base): return json.dumps(json_export, indent=4, default=str, ensure_ascii=False) async def render_GET(self, request: Request) -> bytes: - uri: str = request.uri.decode() + uri: str = request.url.path template = self.environment.get_template( - "titles/idac/frontend/idac_index.jinja" + "titles/idac/templates/idac_index.jinja" ) - sesh: Session = request.getSession() - usr_sesh = IUserSession(sesh) - user_id = usr_sesh.userId + usr_sesh = self.validate_session(request) + if not usr_sesh: + usr_sesh = UserSession() + user_id = usr_sesh.user_id # user_id = usr_sesh.user_id # profile export if uri.startswith("/game/idac/export"): if user_id == 0: - return redirectTo(b"/game/idac", request) + return RedirectResponse(b"/game/idac", request) # set the file name, content type and size to download the json content = await self.generate_all_tables_json(user_id).encode("utf-8") - request.responseHeaders.addRawHeader( - b"content-type", b"application/octet-stream" - ) - request.responseHeaders.addRawHeader( - b"content-disposition", b"attachment; filename=idac_profile.json" - ) - request.responseHeaders.addRawHeader( - b"content-length", str(len(content)).encode("utf-8") - ) self.logger.info(f"User {user_id} exported their IDAC data") - return content + return Response( + content, + 200, + {'content-disposition': 'attachment; filename=idac_profile.json'}, + "application/octet-stream" + ) profile_data, tickets, rank = None, None, None if user_id > 0: @@ -128,7 +132,7 @@ class IDACFrontend(FE_Base): for ticket in ticket_data } - return template.render( + return Response(template.render( title=f"{self.core_config.server.name} | {self.nav_name}", game_list=self.environment.globals["game_list"], profile=profile_data, @@ -136,4 +140,4 @@ class IDACFrontend(FE_Base): rank=rank, sesh=vars(usr_sesh), active_page="idac", - ).encode("utf-16") + )) diff --git a/titles/idac/frontend/idac_index.jinja b/titles/idac/templates/idac_index.jinja similarity index 97% rename from titles/idac/frontend/idac_index.jinja rename to titles/idac/templates/idac_index.jinja index eeecc65..d6e60ef 100644 --- a/titles/idac/frontend/idac_index.jinja +++ b/titles/idac/templates/idac_index.jinja @@ -1,8 +1,8 @@ -{% extends "core/frontend/index.jinja" %} +{% extends "core/templates/index.jinja" %} {% block content %}

頭文字D THE ARCADE

-{% if sesh is defined and sesh["userId"] > 0 %} +{% if sesh is defined and sesh["user_id"] > 0 %}
@@ -128,7 +128,7 @@
{% endblock content %} \ No newline at end of file diff --git a/titles/idac/frontend/js/idac_scripts.js b/titles/idac/templates/js/idac_scripts.js similarity index 100% rename from titles/idac/frontend/js/idac_scripts.js rename to titles/idac/templates/js/idac_scripts.js diff --git a/titles/ongeki/frontend.py b/titles/ongeki/frontend.py index c4d875b..4aa0088 100644 --- a/titles/ongeki/frontend.py +++ b/titles/ongeki/frontend.py @@ -1,11 +1,14 @@ +from typing import List +from starlette.routing import Route import yaml import jinja2 from starlette.requests import Request +from starlette.responses import Response, RedirectResponse from os import path from twisted.web.util import redirectTo from twisted.web.server import Session -from core.frontend import FE_Base, IUserSession +from core.frontend import FE_Base, UserSession from core.config import CoreConfig from titles.ongeki.config import OngekiConfig @@ -27,24 +30,31 @@ class OngekiFrontend(FE_Base): ) self.nav_name = "O.N.G.E.K.I." self.version_list = OngekiConstants.VERSION_NAMES + + def get_routes(self) -> List[Route]: + return [ + Route("/", self.render_GET) + ] async def render_GET(self, request: Request) -> bytes: template = self.environment.get_template( - "titles/ongeki/frontend/ongeki_index.jinja" + "titles/ongeki/templates/ongeki_index.jinja" ) - sesh: Session = request.getSession() - usr_sesh = IUserSession(sesh) + usr_sesh = self.validate_session(request) + if not usr_sesh: + usr_sesh = UserSession() + self.version = usr_sesh.ongeki_version if getattr(usr_sesh, "userId", 0) != 0: - profile_data =self.data.profile.get_profile_data(usr_sesh.userId, self.version) - rival_list = await self.data.profile.get_rivals(usr_sesh.userId) + profile_data =self.data.profile.get_profile_data(usr_sesh.user_id, self.version) + rival_list = await self.data.profile.get_rivals(usr_sesh.user_id) rival_data = { "userRivalList": rival_list, - "userId": usr_sesh.userId + "userId": usr_sesh.user_id } rival_info = OngekiBase.handle_get_user_rival_data_api_request(self, rival_data) - return template.render( + return Response(template.render( data=self.data.profile, title=f"{self.core_config.server.name} | {self.nav_name}", game_list=self.environment.globals["game_list"], @@ -54,34 +64,36 @@ class OngekiFrontend(FE_Base): version_list=self.version_list, version=self.version, sesh=vars(usr_sesh) - ).encode("utf-16") + )) else: - return redirectTo(b"/gate/", request) + return RedirectResponse("/gate/", 303) async def render_POST(self, request: Request): uri = request.uri.decode() - sesh: Session = request.getSession() - usr_sesh = IUserSession(sesh) - if hasattr(usr_sesh, "userId"): + usr_sesh = self.validate_session(request) + if not usr_sesh: + usr_sesh = UserSession() + + if usr_sesh.user_id > 0: if uri == "/game/ongeki/rival.add": rival_id = request.args[b"rivalUserId"][0].decode() - await self.data.profile.put_rival(usr_sesh.userId, rival_id) - # self.logger.info(f"{usr_sesh.userId} added a rival") - return redirectTo(b"/game/ongeki/", request) + await self.data.profile.put_rival(usr_sesh.user_id, rival_id) + # self.logger.info(f"{usr_sesh.user_id} added a rival") + return RedirectResponse(b"/game/ongeki/", 303) elif uri == "/game/ongeki/rival.delete": rival_id = request.args[b"rivalUserId"][0].decode() - await self.data.profile.delete_rival(usr_sesh.userId, rival_id) + await self.data.profile.delete_rival(usr_sesh.user_id, rival_id) # self.logger.info(f"{response}") - return redirectTo(b"/game/ongeki/", request) + return RedirectResponse(b"/game/ongeki/", 303) elif uri == "/game/ongeki/version.change": ongeki_version=request.args[b"version"][0].decode() if(ongeki_version.isdigit()): usr_sesh.ongeki_version=int(ongeki_version) - return redirectTo(b"/game/ongeki/", request) + return RedirectResponse("/game/ongeki/", 303) else: - return b"Something went wrong" + Response("Something went wrong", status_code=500) else: - return b"User is not logged in" + return RedirectResponse("/gate/", 303) diff --git a/titles/ongeki/frontend/js/ongeki_scripts.js b/titles/ongeki/templates/js/ongeki_scripts.js similarity index 100% rename from titles/ongeki/frontend/js/ongeki_scripts.js rename to titles/ongeki/templates/js/ongeki_scripts.js diff --git a/titles/ongeki/frontend/ongeki_index.jinja b/titles/ongeki/templates/ongeki_index.jinja similarity index 94% rename from titles/ongeki/frontend/ongeki_index.jinja rename to titles/ongeki/templates/ongeki_index.jinja index b7b5a90..e441c18 100644 --- a/titles/ongeki/frontend/ongeki_index.jinja +++ b/titles/ongeki/templates/ongeki_index.jinja @@ -1,7 +1,7 @@ -{% extends "core/frontend/index.jinja" %} +{% extends "core/templates/index.jinja" %} {% block content %} -{% if sesh is defined and sesh["userId"] > 0 %} +{% if sesh is defined and sesh["user_id"] > 0 %}


@@ -75,7 +75,7 @@
{% else %}

Not Currently Logged In

diff --git a/titles/pokken/frontend.py b/titles/pokken/frontend.py index be22c50..5672ffb 100644 --- a/titles/pokken/frontend.py +++ b/titles/pokken/frontend.py @@ -1,10 +1,12 @@ import yaml import jinja2 +from typing import List from starlette.requests import Request +from starlette.responses import Response, RedirectResponse +from starlette.routing import Route from os import path -from twisted.web.server import Session -from core.frontend import FE_Base, IUserSession +from core.frontend import FE_Base, UserSession from core.config import CoreConfig from .database import PokkenData from .config import PokkenConfig @@ -12,6 +14,8 @@ from .const import PokkenConstants class PokkenFrontend(FE_Base): + SN_PREFIX = PokkenConstants.SERIAL_IDENT + NETID_PREFIX = PokkenConstants.NETID_PREFIX def __init__( self, cfg: CoreConfig, environment: jinja2.Environment, cfg_dir: str ) -> None: @@ -23,17 +27,75 @@ class PokkenFrontend(FE_Base): yaml.safe_load(open(f"{cfg_dir}/{PokkenConstants.CONFIG_NAME}")) ) self.nav_name = "Pokken" + + def get_routes(self) -> List[Route]: + return [ + Route("/", self.render_GET, methods=['GET']), + Route("/update.name", self.change_name, methods=['POST']), + ] - def render_GET(self, request: Request) -> bytes: + async def render_GET(self, request: Request) -> Response: template = self.environment.get_template( - "titles/pokken/frontend/pokken_index.jinja" + "titles/pokken/templates/pokken_index.jinja" ) + pf = None + + usr_sesh = self.validate_session(request) + if not usr_sesh: + usr_sesh = UserSession() + + else: + profile = await self.data.profile.get_profile(usr_sesh.user_id) + if profile is not None and profile['trainer_name']: + pf = profile._asdict() + + if "e" in request.query_params: + try: + err = int(request.query_params.get("e", 0)) + except Exception: + err = 0 - sesh: Session = request.getSession() - usr_sesh = IUserSession(sesh) + else: + err = 0 + + if "s" in request.query_params: + try: + succ = int(request.query_params.get("s", 0)) + except Exception: + succ = 0 - return template.render( + else: + succ = 0 + + return Response(template.render( title=f"{self.core_config.server.name} | {self.nav_name}", game_list=self.environment.globals["game_list"], - sesh=vars(usr_sesh) - ).encode("utf-16") + sesh=vars(usr_sesh), + profile=pf, + error=err, + success=succ + )) + + async def change_name(self, request: Request) -> RedirectResponse: + usr_sesh = self.validate_session(request) + if not usr_sesh: + return RedirectResponse("/game/pokken/?e=9", 303) + + frm = await request.form() + new_name = frm.get("new_name") + gender = frm.get("new_gender", 1) + + if len(new_name) > 14: + return RedirectResponse("/game/pokken/?e=8", 303) + + if not gender.isdigit(): + return RedirectResponse("/game/pokken/?e=4", 303) + + gender = int(gender) + + if gender != 1 and gender != 2: + return RedirectResponse("/game/pokken/?e=4", 303) # no code for this yet, whatever + + await self.data.profile.set_profile_name(usr_sesh.user_id, new_name, gender) + + return RedirectResponse("/game/pokken/?s=1", 303) diff --git a/titles/pokken/frontend/pokken_index.jinja b/titles/pokken/frontend/pokken_index.jinja deleted file mode 100644 index 446893a..0000000 --- a/titles/pokken/frontend/pokken_index.jinja +++ /dev/null @@ -1,4 +0,0 @@ -{% extends "core/frontend/index.jinja" %} -{% block content %} -

Pokken

-{% endblock content %} \ No newline at end of file diff --git a/titles/pokken/templates/pokken_index.jinja b/titles/pokken/templates/pokken_index.jinja new file mode 100644 index 0000000..4752bc8 --- /dev/null +++ b/titles/pokken/templates/pokken_index.jinja @@ -0,0 +1,48 @@ +{% extends "core/templates/index.jinja" %} +{% block content %} +

Pokken

+{% if profile is defined and profile is not none and profile.id > 0 %} + +

Profile for {{ profile.trainer_name }} 

+{% if error is defined %} +{% include "core/templates/widgets/err_banner.jinja" %} +{% endif %} +{% if success is defined and success == 1 %} +
+Update successful +
+{% endif %} + +{% elif sesh is defined and sesh is not none and sesh.user_id > 0 %} +No profile information found for this account. +{% else %} +Login to view profile information. +{% endif %} +{% endblock content %} \ No newline at end of file diff --git a/titles/wacca/frontend.py b/titles/wacca/frontend.py index cd915bb..973c50b 100644 --- a/titles/wacca/frontend.py +++ b/titles/wacca/frontend.py @@ -1,10 +1,12 @@ +from typing import List +from starlette.routing import Route +from starlette.requests import Request +from starlette.responses import Response +from os import path import yaml import jinja2 -from starlette.requests import Request -from os import path -from twisted.web.server import Session -from core.frontend import FE_Base, IUserSession +from core.frontend import FE_Base, UserSession from core.config import CoreConfig from titles.wacca.database import WaccaData from titles.wacca.config import WaccaConfig @@ -24,15 +26,21 @@ class WaccaFrontend(FE_Base): ) self.nav_name = "Wacca" + def get_routes(self) -> List[Route]: + return [ + Route("/", self.render_GET, methods=['GET']) + ] + async def render_GET(self, request: Request) -> bytes: template = self.environment.get_template( - "titles/wacca/frontend/wacca_index.jinja" + "titles/wacca/templates/wacca_index.jinja" ) - sesh: Session = request.getSession() - usr_sesh = IUserSession(sesh) + usr_sesh = self.validate_session(request) + if not usr_sesh: + usr_sesh = UserSession() - return template.render( + return Response(template.render( title=f"{self.core_config.server.name} | {self.nav_name}", game_list=self.environment.globals["game_list"], sesh=vars(usr_sesh) - ).encode("utf-16") + )) \ No newline at end of file diff --git a/titles/wacca/frontend/wacca_index.jinja b/titles/wacca/frontend/wacca_index.jinja deleted file mode 100644 index 6a5f046..0000000 --- a/titles/wacca/frontend/wacca_index.jinja +++ /dev/null @@ -1,4 +0,0 @@ -{% extends "core/frontend/index.jinja" %} -{% block content %} -

Wacca

-{% endblock content %} \ No newline at end of file diff --git a/titles/wacca/templates/wacca_index.jinja b/titles/wacca/templates/wacca_index.jinja new file mode 100644 index 0000000..974e715 --- /dev/null +++ b/titles/wacca/templates/wacca_index.jinja @@ -0,0 +1,4 @@ +{% extends "core/templates/index.jinja" %} +{% block content %} +

Wacca

+{% endblock content %} \ No newline at end of file From f8c77e69ede7b2d702a1cf353ea1f00e98e3a1ba Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 9 Jan 2024 15:56:49 -0500 Subject: [PATCH 15/32] remove unused table --- core/data/schema/base.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/core/data/schema/base.py b/core/data/schema/base.py index 5549ab2..55eceaf 100644 --- a/core/data/schema/base.py +++ b/core/data/schema/base.py @@ -15,14 +15,6 @@ from core.config import CoreConfig metadata = MetaData() -schema_ver = Table( - "schema_versions", - metadata, - Column("game", String(4), primary_key=True, nullable=False), - Column("version", Integer, nullable=False, server_default="1"), - mysql_charset="utf8mb4", -) - event_log = Table( "event_log", metadata, From 2d95e29f3c431a65215de170bf814588ce50c1fc Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 9 Jan 2024 15:59:58 -0500 Subject: [PATCH 16/32] remove unused imports --- core/aimedb.py | 2 -- core/mucha.py | 3 +- titles/idac/echo.py | 1 - titles/idac/index.py | 2 -- titles/idac/matching.py | 72 --------------------------------------- titles/idz/echo.py | 4 --- titles/ongeki/frontend.py | 2 -- titles/pokken/services.py | 66 ----------------------------------- 8 files changed, 1 insertion(+), 151 deletions(-) delete mode 100644 titles/idac/matching.py delete mode 100644 titles/pokken/services.py diff --git a/core/aimedb.py b/core/aimedb.py index 727a310..f8db451 100644 --- a/core/aimedb.py +++ b/core/aimedb.py @@ -1,7 +1,5 @@ -from twisted.internet.protocol import Factory, Protocol import logging, coloredlogs from Crypto.Cipher import AES -import struct from typing import Dict, Tuple, Callable, Union, Optional import asyncio from logging.handlers import TimedRotatingFileHandler diff --git a/core/mucha.py b/core/mucha.py index 64f20aa..1cd39d1 100644 --- a/core/mucha.py +++ b/core/mucha.py @@ -1,7 +1,6 @@ -from typing import Dict, Any, Optional, List +from typing import Dict, Any, Optional import logging, coloredlogs from logging.handlers import TimedRotatingFileHandler -from twisted.web import resource from starlette.requests import Request from datetime import datetime from Crypto.Cipher import Blowfish diff --git a/titles/idac/echo.py b/titles/idac/echo.py index 5520392..0458159 100644 --- a/titles/idac/echo.py +++ b/titles/idac/echo.py @@ -1,7 +1,6 @@ import logging import socket -from twisted.internet.protocol import DatagramProtocol from socketserver import BaseRequestHandler, TCPServer from typing import Tuple diff --git a/titles/idac/index.py b/titles/idac/index.py index d923946..4e24491 100644 --- a/titles/idac/index.py +++ b/titles/idac/index.py @@ -19,8 +19,6 @@ from titles.idac.season2 import IDACSeason2 from titles.idac.config import IDACConfig from titles.idac.const import IDACConstants from titles.idac.echo import IDACEchoUDP -from titles.idac.matching import IDACMatching - class IDACServlet(BaseServlet): def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None: diff --git a/titles/idac/matching.py b/titles/idac/matching.py deleted file mode 100644 index 396eec8..0000000 --- a/titles/idac/matching.py +++ /dev/null @@ -1,72 +0,0 @@ -import json -import logging - -from typing import Dict -from twisted.web import resource - -from core import CoreConfig -from titles.idac.season2 import IDACBase -from titles.idac.config import IDACConfig - - -class IDACMatching(resource.Resource): - isLeaf = True - - def __init__(self, cfg: CoreConfig, game_cfg: IDACConfig) -> None: - self.core_config = cfg - self.game_config = game_cfg - self.base = IDACBase(cfg, game_cfg) - self.logger = logging.getLogger("idac") - - self.queue = 0 - - def get_matching_state(self): - if self.queue >= 1: - self.queue -= 1 - return 0 - else: - return 1 - - def render_POST(self, req) -> bytes: - url = req.uri.decode() - req_data = json.loads(req.content.getvalue().decode()) - header_application = self.decode_header(req.getAllHeaders()) - user_id = int(header_application["session"]) - - # self.getMatchingStatus(user_id) - - self.logger.info( - f"IDAC Matching request from {req.getClientIP()}: {url} - {req_data}" - ) - - resp = {"status_code": "0"} - if url == "/regist": - self.queue = self.queue + 1 - elif url == "/status": - if req_data.get("cancel_flag"): - self.queue = self.queue - 1 - self.logger.info( - f"IDAC Matching endpoint {req.getClientIP()} had quited" - ) - - resp = { - "status_code": "0", - # Only IPv4 is supported - "host": self.game_config.server.matching_host, - "port": self.game_config.server.matching_p2p, - "room_name": "INDTA", - "state": 1, - } - - self.logger.debug(f"Response {resp}") - return json.dumps(resp, ensure_ascii=False).encode("utf-8") - - def decode_header(self, data: Dict) -> Dict: - app: str = data[b"application"].decode() - ret = {} - - for x in app.split(", "): - y = x.split("=") - ret[y[0]] = y[1].replace('"', "") - - return ret diff --git a/titles/idz/echo.py b/titles/idz/echo.py index 82f7003..8141e45 100644 --- a/titles/idz/echo.py +++ b/titles/idz/echo.py @@ -1,9 +1,5 @@ -from twisted.internet.protocol import DatagramProtocol import logging -from core.config import CoreConfig -from .config import IDZConfig - class IDZEcho: def connection_made(self, transport): self.transport = transport diff --git a/titles/ongeki/frontend.py b/titles/ongeki/frontend.py index 4aa0088..c9896cd 100644 --- a/titles/ongeki/frontend.py +++ b/titles/ongeki/frontend.py @@ -5,8 +5,6 @@ import jinja2 from starlette.requests import Request from starlette.responses import Response, RedirectResponse from os import path -from twisted.web.util import redirectTo -from twisted.web.server import Session from core.frontend import FE_Base, UserSession from core.config import CoreConfig diff --git a/titles/pokken/services.py b/titles/pokken/services.py deleted file mode 100644 index 952c232..0000000 --- a/titles/pokken/services.py +++ /dev/null @@ -1,66 +0,0 @@ -from twisted.internet.interfaces import IAddress -from twisted.internet.protocol import Protocol -from autobahn.twisted.websocket import WebSocketServerProtocol, WebSocketServerFactory -from autobahn.websocket.types import ConnectionRequest -from typing import Dict -import logging -import json - -from core.config import CoreConfig -from .config import PokkenConfig -from .base import PokkenBase - -class PokkenAdmissionProtocol(WebSocketServerProtocol): - def __init__(self, cfg: CoreConfig, game_cfg: PokkenConfig): - super().__init__() - self.core_config = cfg - self.game_config = game_cfg - self.logger = logging.getLogger("pokken") - - self.base = PokkenBase(cfg, game_cfg) - - def onConnect(self, request: ConnectionRequest) -> None: - self.logger.debug(f"Admission: Connection from {request.peer}") - - def onClose(self, wasClean: bool, code: int, reason: str) -> None: - self.logger.debug(f"Admission: Connection with {self.transport.getPeer().host} closed {'cleanly ' if wasClean else ''}with code {code} - {reason}") - - def onMessage(self, payload, isBinary: bool) -> None: - msg: Dict = json.loads(payload) - self.logger.debug(f"Admission: Message from {self.transport.getPeer().host}:{self.transport.getPeer().port} - {msg}") - - api = msg.get("api", "noop") - handler = getattr(self.base, f"handle_admission_{api.lower()}") - resp = handler(msg, self.transport.getPeer().host) - - if resp is None: - resp = {} - - if "type" not in resp: - resp['type'] = "res" - if "data" not in resp: - resp['data'] = {} - if "api" not in resp: - resp['api'] = api - if "result" not in resp: - resp['result'] = 'true' - - self.logger.debug(f"Websocket response: {resp}") - self.sendMessage(json.dumps(resp).encode(), isBinary) - -class PokkenAdmissionFactory(WebSocketServerFactory): - protocol = PokkenAdmissionProtocol - - def __init__( - self, - cfg: CoreConfig, - game_cfg: PokkenConfig - ) -> None: - self.core_config = cfg - self.game_config = game_cfg - super().__init__(f"ws://{self.game_config.server.hostname}:{self.game_config.ports.admission}") - - def buildProtocol(self, addr: IAddress) -> Protocol: - p = self.protocol(self.core_config, self.game_config) - p.factory = self - return p From 37304500a56c220a9faa63a28cadb8ae34103844 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 9 Jan 2024 16:22:43 -0500 Subject: [PATCH 17/32] fix SDDT v5 rollback and v6 upgrade scripts --- core/data/schema/versions/SDDT_5_rollback.sql | 6 +- core/data/schema/versions/SDDT_6_upgrade.sql | 132 +++++++++--------- 2 files changed, 69 insertions(+), 69 deletions(-) diff --git a/core/data/schema/versions/SDDT_5_rollback.sql b/core/data/schema/versions/SDDT_5_rollback.sql index 007716c..61bb352 100644 --- a/core/data/schema/versions/SDDT_5_rollback.sql +++ b/core/data/schema/versions/SDDT_5_rollback.sql @@ -1,8 +1,8 @@ SET FOREIGN_KEY_CHECKS=0; ALTER TABLE ongeki_user_event_point DROP COLUMN version; -ALTER TABLE ongeki_user_event_point DROP COLUMN rank; -ALTER TABLE ongeki_user_event_point DROP COLUMN type; +ALTER TABLE ongeki_user_event_point DROP COLUMN `rank`; +ALTER TABLE ongeki_user_event_point DROP COLUMN `type`; ALTER TABLE ongeki_user_event_point DROP COLUMN date; ALTER TABLE ongeki_user_tech_event DROP COLUMN version; @@ -19,4 +19,4 @@ DROP TABLE ongeki_static_tech_music; DROP TABLE ongeki_static_client_testmode; DROP TABLE ongeki_static_game_point; -SET FOREIGN_KEY_CHECKS=1; +SET FOREIGN_KEY_CHECKS=1; \ No newline at end of file diff --git a/core/data/schema/versions/SDDT_6_upgrade.sql b/core/data/schema/versions/SDDT_6_upgrade.sql index 82d5336..1afa186 100644 --- a/core/data/schema/versions/SDDT_6_upgrade.sql +++ b/core/data/schema/versions/SDDT_6_upgrade.sql @@ -1,8 +1,8 @@ SET FOREIGN_KEY_CHECKS=0; ALTER TABLE ongeki_user_event_point ADD COLUMN version INTEGER NOT NULL; -ALTER TABLE ongeki_user_event_point ADD COLUMN rank INTEGER; -ALTER TABLE ongeki_user_event_point ADD COLUMN type INTEGER NOT NULL; +ALTER TABLE ongeki_user_event_point ADD COLUMN `rank` INTEGER; +ALTER TABLE ongeki_user_event_point ADD COLUMN `type` INTEGER NOT NULL; ALTER TABLE ongeki_user_event_point ADD COLUMN date VARCHAR(25); ALTER TABLE ongeki_user_tech_event ADD COLUMN version INTEGER NOT NULL; @@ -12,87 +12,87 @@ ALTER TABLE ongeki_user_mission_point ADD COLUMN version INTEGER NOT NULL; ALTER TABLE ongeki_static_events ADD COLUMN endDate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP; CREATE TABLE ongeki_tech_event_ranking ( - id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, - user INT NOT NULL, - version INT NOT NULL, - date VARCHAR(25), - eventId INT NOT NULL, - rank INT, - totalPlatinumScore INT NOT NULL, - totalTechScore INT NOT NULL, - UNIQUE KEY ongeki_tech_event_ranking_uk (user, eventId), - CONSTRAINT ongeki_tech_event_ranking_ibfk1 FOREIGN KEY (user) REFERENCES aime_user(id) ON DELETE CASCADE ON UPDATE CASCADE + id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + user INT NOT NULL, + version INT NOT NULL, + date VARCHAR(25), + eventId INT NOT NULL, + `rank` INT, + totalPlatinumScore INT NOT NULL, + totalTechScore INT NOT NULL, + UNIQUE KEY ongeki_tech_event_ranking_uk (user, eventId), + CONSTRAINT ongeki_tech_event_ranking_ibfk1 FOREIGN KEY (user) REFERENCES aime_user(id) ON DELETE CASCADE ON UPDATE CASCADE ); CREATE TABLE ongeki_static_music_ranking_list ( - id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, - version INT NOT NULL, - musicId INT NOT NULL, - point INT NOT NULL, - userName VARCHAR(255), - UNIQUE KEY ongeki_static_music_ranking_list_uk (version, musicId) + id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + version INT NOT NULL, + musicId INT NOT NULL, + point INT NOT NULL, + userName VARCHAR(255), + UNIQUE KEY ongeki_static_music_ranking_list_uk (version, musicId) ); CREATE TABLE ongeki_static_rewards ( - id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, - version INT NOT NULL, - rewardId INT NOT NULL, - rewardName VARCHAR(255) NOT NULL, - itemKind INT NOT NULL, - itemId INT NOT NULL, - UNIQUE KEY ongeki_tech_event_ranking_uk (version, rewardId) + id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + version INT NOT NULL, + rewardId INT NOT NULL, + rewardName VARCHAR(255) NOT NULL, + itemKind INT NOT NULL, + itemId INT NOT NULL, + UNIQUE KEY ongeki_tech_event_ranking_uk (version, rewardId) ); CREATE TABLE ongeki_static_present_list ( - id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, - version INT NOT NULL, - presentId INT NOT NULL, - presentName VARCHAR(255) NOT NULL, - rewardId INT NOT NULL, - stock INT NOT NULL, - message VARCHAR(255), - startDate VARCHAR(25) NOT NULL, - endDate VARCHAR(25) NOT NULL, - UNIQUE KEY ongeki_static_present_list_uk (version, presentId, rewardId) + id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + version INT NOT NULL, + presentId INT NOT NULL, + presentName VARCHAR(255) NOT NULL, + rewardId INT NOT NULL, + stock INT NOT NULL, + message VARCHAR(255), + startDate VARCHAR(25) NOT NULL, + endDate VARCHAR(25) NOT NULL, + UNIQUE KEY ongeki_static_present_list_uk (version, presentId, rewardId) ); CREATE TABLE ongeki_static_tech_music ( - id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, - version INT NOT NULL, - eventId INT NOT NULL, - musicId INT NOT NULL, - level INT NOT NULL, - UNIQUE KEY ongeki_static_tech_music_uk (version, musicId, eventId) + id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + version INT NOT NULL, + eventId INT NOT NULL, + musicId INT NOT NULL, + level INT NOT NULL, + UNIQUE KEY ongeki_static_tech_music_uk (version, musicId, eventId) ); CREATE TABLE ongeki_static_client_testmode ( - id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, - regionId INT NOT NULL, - placeId INT NOT NULL, - clientId VARCHAR(11) NOT NULL, - updateDate TIMESTAMP NOT NULL, - isDelivery BOOLEAN NOT NULL, - groupId INT NOT NULL, - groupRole INT NOT NULL, - continueMode INT NOT NULL, - selectMusicTime INT NOT NULL, - advertiseVolume INT NOT NULL, - eventMode INT NOT NULL, - eventMusicNum INT NOT NULL, - patternGp INT NOT NULL, - limitGp INT NOT NULL, - maxLeverMovable INT NOT NULL, - minLeverMovable INT NOT NULL, - UNIQUE KEY ongeki_static_client_testmode_uk (clientId) + id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + regionId INT NOT NULL, + placeId INT NOT NULL, + clientId VARCHAR(11) NOT NULL, + updateDate TIMESTAMP NOT NULL, + isDelivery BOOLEAN NOT NULL, + groupId INT NOT NULL, + groupRole INT NOT NULL, + continueMode INT NOT NULL, + selectMusicTime INT NOT NULL, + advertiseVolume INT NOT NULL, + eventMode INT NOT NULL, + eventMusicNum INT NOT NULL, + patternGp INT NOT NULL, + limitGp INT NOT NULL, + maxLeverMovable INT NOT NULL, + minLeverMovable INT NOT NULL, + UNIQUE KEY ongeki_static_client_testmode_uk (clientId) ); CREATE TABLE ongeki_static_game_point ( - id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, - type INT NOT NULL, - cost INT NOT NULL, - startDate VARCHAR(25) NOT NULL DEFAULT "2000-01-01 05:00:00.0", - endDate VARCHAR(25) NOT NULL DEFAULT "2099-01-01 05:00:00.0", - UNIQUE KEY ongeki_static_game_point_uk (type) + id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + `type` INT NOT NULL, + cost INT NOT NULL, + startDate VARCHAR(25) NOT NULL DEFAULT "2000-01-01 05:00:00.0", + endDate VARCHAR(25) NOT NULL DEFAULT "2099-01-01 05:00:00.0", + UNIQUE KEY ongeki_static_game_point_uk (`type`) ); -SET FOREIGN_KEY_CHECKS=1; +SET FOREIGN_KEY_CHECKS=1; \ No newline at end of file From 261d09aaef5594ce5869066e53248db6356b2f1c Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 9 Jan 2024 17:11:49 -0500 Subject: [PATCH 18/32] dbutils: add legacy migration --- core/data/database.py | 60 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/core/data/database.py b/core/data/database.py index 1629aaa..38a9b1f 100644 --- a/core/data/database.py +++ b/core/data/database.py @@ -8,6 +8,7 @@ import secrets, string import bcrypt from hashlib import sha256 import alembic.config +import glob from core.config import CoreConfig from core.data.schema import * @@ -146,6 +147,12 @@ class Data: self.logger.warn("No need to migrate as you have already migrated to alembic. If you are trying to upgrade the schema, use `upgrade` instead!") return + self.logger.info("Upgrading to latest with legacy system") + if not await self.legacy_upgrade(): + self.logger.warn("No need to migrate as you have already deleted the old schema_versions system. If you are trying to upgrade the schema, use `upgrade` instead!") + return + self.logger.info("Done") + self.logger.info("Stamp with initial revision") self.__alembic_cmd( "stamp", @@ -157,4 +164,57 @@ class Data: "upgrade", "head", ) + + async def legacy_upgrade(self) -> bool: + vers = await self.base.execute("SELECT * FROM schema_versions") + if vers is None: + self.logger.warn("Cannot legacy upgrade, schema_versions table unavailable!") + return False + + db_vers = {} + for x in vers: + db_vers[x['game']] = x['version'] + + core_now_ver = int(db_vers['CORE']) + 1 + while os.path.exists(f"core/data/schema/versions/CORE_{core_now_ver}_upgrade.sql"): + with open(f"core/data/schema/versions/CORE_{core_now_ver}_upgrade.sql", "r") as f: + result = await self.base.execute(f.read()) + + if result is None: + self.logger.error(f"Invalid upgrade script CORE_{core_now_ver}_upgrade.sql") + break + + result = await self.base.execute(f"UPDATE schema_versions SET version = {core_now_ver} WHERE game = 'CORE'") + if result is None: + self.logger.error(f"Failed to update schema version for CORE to {core_now_ver}") + break + + self.logger.info(f"Upgrade CORE to version {core_now_ver}") + core_now_ver += 1 + + for _, mod in Utils.get_all_titles().items(): + game_codes = getattr(mod, "game_codes", []) + for game in game_codes: + if game not in db_vers: + self.logger.warn(f"{game} does not have an antry in schema_versions, skipping") + continue + + now_ver = int(db_vers[game]) + 1 + while os.path.exists(f"core/data/schema/versions/{game}_{now_ver}_upgrade.sql"): + with open(f"core/data/schema/versions/{game}_{now_ver}_upgrade.sql", "r") as f: + result = await self.base.execute(f.read()) + + if result is None: + self.logger.error(f"Invalid upgrade script {game}_{now_ver}_upgrade.sql") + break + + result = await self.base.execute(f"UPDATE schema_versions SET version = {now_ver} WHERE game = '{game}'") + if result is None: + self.logger.error(f"Failed to update schema version for {game} to {now_ver}") + break + + self.logger.info(f"Upgrade {game} to version {now_ver}") + now_ver += 1 + + return True From c680c2d4e997f9158f1dfd4aee16f11960a1f43e Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 9 Jan 2024 17:49:18 -0500 Subject: [PATCH 19/32] readd get_title_port_ssl --- core/config.py | 10 ++++++++++ core/utils.py | 10 +++++++++- example_config/core.yaml | 1 + example_config/cxb.yaml | 3 ++- example_config/nginx_example.conf | 30 ++++-------------------------- titles/cxb/config.py | 6 ++++++ titles/cxb/index.py | 9 +++++---- titles/ongeki/index.py | 13 ++++++++++--- titles/pokken/index.py | 4 ++-- titles/sao/index.py | 7 ++++--- 10 files changed, 53 insertions(+), 40 deletions(-) diff --git a/core/config.py b/core/config.py index 47a3ff3..e5d0f35 100644 --- a/core/config.py +++ b/core/config.py @@ -84,6 +84,16 @@ class ServerConfig: self.__config, "core", "title", "proxy_port", default=0 ) + @property + def proxy_port_ssl(self) -> int: + """ + What port the proxy is listening for secure connections on. This will be sent + instead of 'port' if is_using_proxy is True and this value is non-zero + """ + return CoreConfig.get_config_field( + self.__config, "core", "title", "proxy_port_ssl", default=0 + ) + @property def log_dir(self) -> str: return CoreConfig.get_config_field( diff --git a/core/utils.py b/core/utils.py index 4dfb4dc..469a03f 100644 --- a/core/utils.py +++ b/core/utils.py @@ -40,9 +40,17 @@ class Utils: def get_title_port(cls, cfg: CoreConfig): if cls.real_title_port is not None: return cls.real_title_port - cls.real_title_port = cfg.server.proxy_port if cfg.server.is_using_proxy else cfg.server.port + cls.real_title_port = cfg.server.proxy_port if cfg.server.is_using_proxy and cfg.server.proxy_port else cfg.server.port return cls.real_title_port + + @classmethod + def get_title_port_ssl(cls, cfg: CoreConfig): + if cls.real_title_port_ssl is not None: return cls.real_title_port_ssl + + cls.real_title_port_ssl = cfg.server.proxy_port_ssl if cfg.server.is_using_proxy and cfg.server.proxy_port_ssl else Utils.get_title_port(cfg) + + return cls.real_title_port_ssl def create_sega_auth_key(aime_id: int, game: str, place_id: int, keychip_id: str, b64_secret: str, exp_seconds: int = 86400, err_logger: str = 'aimedb') -> Optional[str]: logger = logging.getLogger(err_logger) diff --git a/example_config/core.yaml b/example_config/core.yaml index ac3a69e..464da8b 100644 --- a/example_config/core.yaml +++ b/example_config/core.yaml @@ -10,6 +10,7 @@ server: is_develop: True is_using_proxy: False proxy_port: 0 + proxy_port_ssl: 0 log_dir: "logs" check_arcade_ip: False strict_ip_checking: False diff --git a/example_config/cxb.yaml b/example_config/cxb.yaml index 7723ff4..5cc4f90 100644 --- a/example_config/cxb.yaml +++ b/example_config/cxb.yaml @@ -1,3 +1,4 @@ server: enable: True - loglevel: "info" \ No newline at end of file + loglevel: "info" + use:https: True \ No newline at end of file diff --git a/example_config/nginx_example.conf b/example_config/nginx_example.conf index ef3b7d4..1790b84 100644 --- a/example_config/nginx_example.conf +++ b/example_config/nginx_example.conf @@ -6,7 +6,7 @@ server { location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass_request_headers on; - proxy_pass http://localhost:8000/; + proxy_pass http://localhost:8080/; } } @@ -42,7 +42,7 @@ server { } } -# Billing +# Billing, comment this out if running billing standalone server { listen 8443 ssl; server_name ib.naominet.jp; @@ -58,28 +58,6 @@ server { ssl_prefer_server_ciphers off; location / { - proxy_pass http://localhost:8444/; - } -} - -# Pokken, comment this out if you don't plan on serving pokken. -server { - listen 443 ssl; - server_name pokken.hostname.here; - - ssl_certificate /path/to/cert/pokken.pem; - ssl_certificate_key /path/to/cert/pokken.key; - ssl_session_timeout 1d; - ssl_session_cache shared:MozSSL:10m; - ssl_session_tickets off; - - ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; - ssl_ciphers "ALL:@SECLEVEL=0"; - ssl_prefer_server_ciphers off; - - location / { - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_pass_request_headers on; proxy_pass http://localhost:8080/; } } @@ -92,7 +70,7 @@ server { location / { return 301 https://$host$request_uri; # If you don't want https redirection, comment the line above and uncomment the line below - # proxy_pass http://localhost:8090/; + # proxy_pass http://localhost:8080/; } } @@ -118,6 +96,6 @@ server { location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass_request_headers on; - proxy_pass http://localhost:8090/; + proxy_pass http://localhost:8080/; } } diff --git a/titles/cxb/config.py b/titles/cxb/config.py index fa5a6a3..49ab7c0 100644 --- a/titles/cxb/config.py +++ b/titles/cxb/config.py @@ -18,6 +18,12 @@ class CxbServerConfig: self.__config, "cxb", "server", "loglevel", default="info" ) ) + + @property + def use_https(self) -> bool: + return CoreConfig.get_config_field( + self.__config, "cxb", "server", "use_https", default=True + ) class CxbConfig(dict): diff --git a/titles/cxb/index.py b/titles/cxb/index.py index 345aa04..53930b4 100644 --- a/titles/cxb/index.py +++ b/titles/cxb/index.py @@ -89,18 +89,19 @@ class CxbServlet(BaseServlet): title_port_int = Utils.get_title_port(self.core_cfg) title_port_ssl_int = Utils.get_title_port_ssl(self.core_cfg) - proto = "https" if title_port_ssl_int != 443 else "http" + proto = "https" if self.game_cfg.server.use_https else "http" if proto == "https": - t_port = f":{title_port_ssl_int}" if title_port_ssl_int and not self.core_cfg.server.is_using_proxy else "" + t_port = f":{title_port_ssl_int}" if title_port_ssl_int != 443 else "" else: - t_port = f":{title_port_int}" if title_port_int and not self.core_cfg.server.is_using_proxy else "" + t_port = f":{title_port_int}" if title_port_int != 80 else "" return ( - f"{proto}://{self.core_cfg.server.hostname}{t_port}", + f"{proto}://{self.core_cfg.title.hostname}{t_port}", "", ) + async def preprocess(self, req: Request) -> Dict: req_bytes = await req.body() diff --git a/titles/ongeki/index.py b/titles/ongeki/index.py index f80214b..b217b2e 100644 --- a/titles/ongeki/index.py +++ b/titles/ongeki/index.py @@ -129,14 +129,21 @@ class OngekiServlet(BaseServlet): def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]: title_port_int = Utils.get_title_port(self.core_cfg) + title_port_ssl_int = Utils.get_title_port_ssl(self.core_cfg) proto = "https" if self.game_cfg.server.use_https and game_ver >= 120 else "http" - t_port = f":{title_port_int}" if title_port_int and not self.core_cfg.server.is_using_proxy else "" + + if proto == "https": + t_port = f":{title_port_ssl_int}" if title_port_ssl_int != 443 else "" + + else: + t_port = f":{title_port_int}" if title_port_int != 80 else "" return ( - f"{proto}://{self.core_cfg.server.hostname}{t_port}/{game_code}/{game_ver}/", - f"{self.core_cfg.server.hostname}{t_port}/", + f"{proto}://{self.core_cfg.title.hostname}{t_port}/{game_code}/{game_ver}/", + f"{self.core_cfg.title.hostname}{t_port}/", ) + async def render_POST(self, request: Request) -> bytes: endpoint: str = request.path_params.get('endpoint', '') version: int = request.path_params.get('version', 0) diff --git a/titles/pokken/index.py b/titles/pokken/index.py index ffd916c..8e2ce70 100644 --- a/titles/pokken/index.py +++ b/titles/pokken/index.py @@ -78,8 +78,8 @@ class PokkenServlet(BaseServlet): def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]: return ( - f"https://{self.game_cfg.server.hostname}:{self.game_cfg.ports.game}/pokken/", - f"{self.game_cfg.server.hostname}:{self.game_cfg.ports.game}/pokken/", + f"https://{self.game_cfg.server.hostname}:{Utils.get_title_port_ssl(self.core_cfg)}/pokken/", + f"{self.game_cfg.server.hostname}:{Utils.get_title_port_ssl(self.core_cfg)}/pokken/", ) def get_mucha_info(self, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, str]: diff --git a/titles/sao/index.py b/titles/sao/index.py index 4cabe30..e12dcbb 100644 --- a/titles/sao/index.py +++ b/titles/sao/index.py @@ -81,13 +81,14 @@ class SaoServlet(BaseServlet): port_normal = Utils.get_title_port(self.core_cfg) proto = "http" - port = f":{port_normal}" if not self.core_cfg.server.is_using_proxy and port_normal != 80 else "" + port = f":{port_normal}" if port_normal != 80 else "" if self.game_cfg.server.use_https: proto = "https" - port = f":{port_ssl}" if not self.core_cfg.server.is_using_proxy and port_ssl != 443 else "" + port = f":{port_ssl}" if port_ssl != 443 else "" + + return (f"{proto}://{self.core_cfg.title.hostname}{port}/", "") - return (f"{proto}://{self.core_cfg.server.hostname}{port}/", "") def get_mucha_info(self, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, str]: if not self.game_cfg.server.enable: From be0e407ebedd2f552fa23caa58ddf9fd3b39513f Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Tue, 9 Jan 2024 18:49:43 -0500 Subject: [PATCH 20/32] wacca: fix hash --- titles/wacca/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/titles/wacca/index.py b/titles/wacca/index.py index 3f58a9d..e1428fa 100644 --- a/titles/wacca/index.py +++ b/titles/wacca/index.py @@ -97,7 +97,7 @@ class WaccaServlet(BaseServlet): async def render_POST(self, request: Request) -> bytes: def end(resp: Dict) -> bytes: hash = md5(json.dumps(resp, ensure_ascii=False).encode()).digest() - return JSONResponse(resp, headers=["X-Wacca-Hash", hash.hex()]) + return JSONResponse(resp, headers={"X-Wacca-Hash": hash.hex()}) api = request.path_params.get('api', '') branch = request.path_params.get('branch', '') From 5e6efbd092a03cbe1827ceec4b830c65ca3a1bc2 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Tue, 9 Jan 2024 19:10:24 -0500 Subject: [PATCH 21/32] wacca: fix network BAD --- titles/wacca/index.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/titles/wacca/index.py b/titles/wacca/index.py index e1428fa..9a87d7c 100644 --- a/titles/wacca/index.py +++ b/titles/wacca/index.py @@ -25,7 +25,6 @@ from .base import WaccaBase from .handlers.base import BaseResponse, BaseRequest from .handlers.helpers import Version - class WaccaServlet(BaseServlet): def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None: self.core_cfg = core_cfg @@ -97,7 +96,9 @@ class WaccaServlet(BaseServlet): async def render_POST(self, request: Request) -> bytes: def end(resp: Dict) -> bytes: hash = md5(json.dumps(resp, ensure_ascii=False).encode()).digest() - return JSONResponse(resp, headers={"X-Wacca-Hash": hash.hex()}) + j_Resp = Response(json.dumps(resp, ensure_ascii=False)) + j_Resp.raw_headers.append((b"X-Wacca-Hash", hash.hex().encode())) + return j_Resp api = request.path_params.get('api', '') branch = request.path_params.get('branch', '') From 8a99f94c93bac0f3f922cce2a4c410bc1bb0d63f Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Tue, 9 Jan 2024 19:10:39 -0500 Subject: [PATCH 22/32] aimedb: fix felica lookups failing --- core/aimedb.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/aimedb.py b/core/aimedb.py index f8db451..8d2c68e 100644 --- a/core/aimedb.py +++ b/core/aimedb.py @@ -231,7 +231,7 @@ class AimedbServlette(): be fine. """ req = ADBFelicaLookupRequest(data) - ac = await self.data.card.to_access_code(req.idm) + ac = self.data.card.to_access_code(req.idm) self.logger.info( f"idm {req.idm} ipm {req.pmm} -> access_code {ac}" ) @@ -242,7 +242,7 @@ class AimedbServlette(): I've never seen this used. """ req = ADBFelicaLookupRequest(data) - ac = await self.data.card.to_access_code(req.idm) + ac = self.data.card.to_access_code(req.idm) if self.config.server.allow_user_registration: user_id = await self.data.user.create_user() @@ -271,7 +271,7 @@ class AimedbServlette(): async def handle_felica_lookup_ex(self, data: bytes, resp_code: int) -> bytes: req = ADBFelicaLookup2Request(data) - access_code = await self.data.card.to_access_code(req.idm) + access_code = self.data.card.to_access_code(req.idm) user_id = await self.data.card.get_user_id_from_card(access_code=access_code) if user_id is None: From 805b8f5b3e4b49a90f251719b88c440b6976db43 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Tue, 9 Jan 2024 19:10:54 -0500 Subject: [PATCH 23/32] allnet: fix error parsing dli files --- core/allnet.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/allnet.py b/core/allnet.py index 219f331..aa82071 100644 --- a/core/allnet.py +++ b/core/allnet.py @@ -345,7 +345,7 @@ class AllnetServlet: return PlainTextResponse(res_str) async def handle_dlorder_ini(self, request: Request) -> bytes: - req_file = request.path_params.get("file", "").replace("%0A", "") + req_file = request.path_params.get("file", "").replace("%0A", "").replace("\n", "") if not req_file: return PlainTextResponse(status_code=404) @@ -355,7 +355,7 @@ class AllnetServlet: await self.data.base.log_event("allnet", "DLORDER_INI_SENT", logging.INFO, f"{Utils.get_ip_addr(request)} successfully recieved {req_file}") return PlainTextResponse(open( - f"{self.config.allnet.update_cfg_folder}/{req_file}", "r" + f"{self.config.allnet.update_cfg_folder}/{req_file}", "r", encoding="utf-8" ).read()) self.logger.info(f"DL INI File {req_file} not found") From d672edb26659a867b800bc9fd5e2dc7b0f91e1a0 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Tue, 9 Jan 2024 19:45:39 -0500 Subject: [PATCH 24/32] fix searching for an arcade by serial --- core/frontend.py | 21 +++++++++++++++------ core/templates/sys/index.jinja | 2 +- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/core/frontend.py b/core/frontend.py index b180c71..caf5c74 100644 --- a/core/frontend.py +++ b/core/frontend.py @@ -429,10 +429,10 @@ class FE_User(FE_Base): else: status = 'Active' - idm = c['idm'] + #idm = c['idm'] ac = c['access_code'] - if ac.startswith("5") or idm is not None: + if ac.startswith("5"): #or idm is not None: c_type = "AmusementIC" elif ac.startswith("3"): c_type = "Banapass" @@ -441,10 +441,19 @@ class FE_User(FE_Base): desc, _ = self.data.card.get_aime_ac_key_desc(ac) if desc is not None: c_type = desc + elif ac.startswith("0008"): + c_type = "Generated AIC" else: c_type = "Unknown" - card_data.append({'access_code': ac, 'status': status, 'chip_id': None if c['chip_id'] is None else f"{c['chip_id']:X}", 'idm': idm, 'type': c_type, "memo": ""}) + card_data.append({ + 'access_code': ac, + 'status': status, + 'chip_id': "", #None if c['chip_id'] is None else f"{c['chip_id']:X}", + 'idm': "", + 'type': c_type, + "memo": "" + }) if "e" in request.query_params: try: @@ -602,8 +611,8 @@ class FE_System(FE_Base): sinfo = None if sinfo: shoplist.append({ - "name": sinfo.name, - "id": sinfo.allnet_id + "name": sinfo['name'], + "id": sinfo['id'] }) else: @@ -624,7 +633,7 @@ class FE_System(FE_Base): netid_prefix = self.environment.globals["sn_cvt"].get(prefix, "") sn_search = netid_prefix + suffix - if re.match("^AB[D|G|L]N\d{7}$", sn_search): + if re.match(r"^AB[DGL]N\d{7}$", sn_search) or re.match(r"^A\d{2}[EX]\d{2}[A-Z]\d{4,8}$", sn_search): cabinfo = await self.data.arcade.get_machine(sn_search) if cabinfo is None: sinfo = None else: diff --git a/core/templates/sys/index.jinja b/core/templates/sys/index.jinja index 08443c0..92dd864 100644 --- a/core/templates/sys/index.jinja +++ b/core/templates/sys/index.jinja @@ -39,7 +39,7 @@ OR
- +

From 0a56207e90149f7cf7ae7a73c088f1a4c07eb02e Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Tue, 9 Jan 2024 20:08:10 -0500 Subject: [PATCH 25/32] frontend: fix cab list on arcade page --- core/frontend.py | 18 ++++++++++++------ core/templates/arcade/index.jinja | 15 +++++++++++++++ core/templates/machine/index.jinja | 1 - 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/core/frontend.py b/core/frontend.py index caf5c74..17b058f 100644 --- a/core/frontend.py +++ b/core/frontend.py @@ -677,24 +677,30 @@ class FE_Arcade(FE_Base): sesh=vars(usr_sesh), )) - try: - sinfo = await self.data.arcade.get_arcade(shop_id) - except Exception as e: - self.logger.error(f"Failed to fetch shop info for shop {shop_id} in render_GET - {e}") - sinfo = None + sinfo = await self.data.arcade.get_arcade(shop_id) if not sinfo: return Response(template.render( title=f"{self.core_config.server.name} | Arcade", sesh=vars(usr_sesh), )) + cabs = await self.data.arcade.get_arcade_machines(shop_id) + cablst = [] + if cabs: + for x in cabs: + cablst.append({ + "id": x['id'], + "serial": x['serial'], + "game": x['game'], + }) + return Response(template.render( title=f"{self.core_config.server.name} | Arcade", sesh=vars(usr_sesh), arcade={ "name": sinfo['name'], "id": sinfo['id'], - "cabs": [] + "cabs": cablst } )) diff --git a/core/templates/arcade/index.jinja b/core/templates/arcade/index.jinja index 627f203..1de4301 100644 --- a/core/templates/arcade/index.jinja +++ b/core/templates/arcade/index.jinja @@ -1,4 +1,19 @@ {% extends "core/templates/index.jinja" %} {% block content %} +{% if arcade is defined %}

{{ arcade.name }}

+

PCBs assigned to this arcade

+{% if success is defined and success == 3 %} +
+Cab added successfully +
+{% endif %} + +{% else %} +

Arcade Not Found

+{% endif %} {% endblock content %} \ No newline at end of file diff --git a/core/templates/machine/index.jinja b/core/templates/machine/index.jinja index 38a40a7..3e122f3 100644 --- a/core/templates/machine/index.jinja +++ b/core/templates/machine/index.jinja @@ -1,5 +1,4 @@ {% extends "core/templates/index.jinja" %} {% block content %} -{% include "core/templates/widgets/err_banner.jinja" %}

Machine Management

{% endblock content %} \ No newline at end of file From d01ceab92f18a06bd55c2ca20d2e400be221c829 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Tue, 9 Jan 2024 21:03:58 -0500 Subject: [PATCH 26/32] idac: remove hanging "'s" on frontend if the person viewing the page doesn't have a profile --- titles/idac/frontend.py | 9 +++++---- titles/idac/templates/idac_index.jinja | 6 +++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/titles/idac/frontend.py b/titles/idac/frontend.py index 88f2b44..e331a24 100644 --- a/titles/idac/frontend.py +++ b/titles/idac/frontend.py @@ -127,10 +127,11 @@ class IDACFrontend(FE_Base): ticket_data = await self.data.item.get_tickets(user_id) rank = await self.data.profile.get_profile_rank(user_id, self.version) - tickets = { - self.ticket_names[ticket["ticket_id"]]: ticket["ticket_cnt"] - for ticket in ticket_data - } + if ticket_data: + tickets = { + self.ticket_names[ticket["ticket_id"]]: ticket["ticket_cnt"] + for ticket in ticket_data + } return Response(template.render( title=f"{self.core_config.server.name} | {self.nav_name}", diff --git a/titles/idac/templates/idac_index.jinja b/titles/idac/templates/idac_index.jinja index d6e60ef..caa7770 100644 --- a/titles/idac/templates/idac_index.jinja +++ b/titles/idac/templates/idac_index.jinja @@ -4,9 +4,10 @@ {% if sesh is defined and sesh["user_id"] > 0 %}
-
+
+ {% if profile is defined and profile is not none %}
-
+

{{ sesh["username"] }}'s Profile

@@ -18,7 +19,6 @@
- {% if profile is defined and profile is not none %}
From 06e7288cad377058b06708991344a3729f3abcba Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Tue, 9 Jan 2024 21:16:22 -0500 Subject: [PATCH 27/32] ongeki: fix frontend page --- core/templates/user/index.jinja | 2 +- titles/ongeki/frontend.py | 6 ++- titles/ongeki/templates/ongeki_index.jinja | 45 +++++++++++++--------- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/core/templates/user/index.jinja b/core/templates/user/index.jinja index 4276a81..1b6ec1d 100644 --- a/core/templates/user/index.jinja +++ b/core/templates/user/index.jinja @@ -146,7 +146,7 @@ Update successful