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 typing import Dict, Tuple from os import path from core import CoreConfig, Utils from titles.wacca.config import WaccaConfig from titles.wacca.config import WaccaConfig from titles.wacca.const import WaccaConstants from titles.wacca.reverse import WaccaReverse from titles.wacca.lilyr import WaccaLilyR from titles.wacca.lily import WaccaLily from titles.wacca.s import WaccaS from titles.wacca.base import WaccaBase from titles.wacca.handlers.base import BaseResponse from titles.wacca.handlers.helpers import Version class WaccaServlet: def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None: self.core_cfg = core_cfg self.game_cfg = WaccaConfig() if path.exists(f"{cfg_dir}/{WaccaConstants.CONFIG_NAME}"): self.game_cfg.update( yaml.safe_load(open(f"{cfg_dir}/{WaccaConstants.CONFIG_NAME}")) ) self.versions = [ WaccaBase(core_cfg, self.game_cfg), WaccaS(core_cfg, self.game_cfg), WaccaLily(core_cfg, self.game_cfg), WaccaLilyR(core_cfg, self.game_cfg), WaccaReverse(core_cfg, self.game_cfg), ] self.logger = logging.getLogger("wacca") log_fmt_str = "[%(asctime)s] Wacca | %(levelname)s | %(message)s" log_fmt = logging.Formatter(log_fmt_str) fileHandler = TimedRotatingFileHandler( "{0}/{1}.log".format(self.core_cfg.server.log_dir, "wacca"), encoding="utf8", 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.game_cfg.server.loglevel) coloredlogs.install( level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str ) @classmethod def get_allnet_info( cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str ) -> Tuple[bool, str, str]: game_cfg = WaccaConfig() if path.exists(f"{cfg_dir}/{WaccaConstants.CONFIG_NAME}"): game_cfg.update( yaml.safe_load(open(f"{cfg_dir}/{WaccaConstants.CONFIG_NAME}")) ) if not game_cfg.server.enable: return (False, "", "") if core_cfg.server.is_develop: return ( True, f"http://{core_cfg.title.hostname}:{core_cfg.title.port}/{game_code}/$v", "", ) return (True, f"http://{core_cfg.title.hostname}/{game_code}/$v", "") def render_POST(self, request: Request, version: int, url_path: str) -> 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() client_ip = Utils.get_ip_addr(request) try: req_json = json.loads(request.content.getvalue()) version_full = Version(req_json["appVersion"]) except Exception: self.logger.error( f"Failed to parse request to {url_path} -> {request.content.getvalue()}" ) resp = BaseResponse() resp.status = 1 resp.message = "不正なリクエスト エラーです" return end(resp.make()) if "api/" in url_path: func_to_find = ( "handle_" + url_path.partition("api/")[2].replace("/", "_") + "_request" ) else: self.logger.error(f"Malformed url {url_path}") resp = BaseResponse() resp.status = 1 resp.message = "Bad URL" return end(resp.make()) ver_search = int(version_full) if ver_search < 15000: internal_ver = WaccaConstants.VER_WACCA elif ver_search >= 15000 and ver_search < 20000: internal_ver = WaccaConstants.VER_WACCA_S elif ver_search >= 20000 and ver_search < 25000: internal_ver = WaccaConstants.VER_WACCA_LILY elif ver_search >= 25000 and ver_search < 30000: internal_ver = WaccaConstants.VER_WACCA_LILY_R elif ver_search >= 30000: internal_ver = WaccaConstants.VER_WACCA_REVERSE else: self.logger.warning( f"Unsupported version ({req_json['appVersion']}) request {url_path} - {req_json}" ) resp = BaseResponse() resp.status = 1 resp.message = "不正なアプリバージョンエラーです" return end(resp.make()) self.logger.info( f"v{req_json['appVersion']} {url_path} request from {client_ip} with chipId {req_json['chipId']}" ) self.logger.debug(req_json) if not hasattr(self.versions[internal_ver], func_to_find): self.logger.warn( f"{req_json['appVersion']} has no handler for {func_to_find}" ) resp = BaseResponse().make() return end(resp) try: handler = getattr(self.versions[internal_ver], func_to_find) resp = handler(req_json) self.logger.debug(f"{req_json['appVersion']} response {resp}") return end(resp) except Exception as e: self.logger.error( f"{req_json['appVersion']} Error handling method {url_path} -> {e}" ) if self.core_cfg.server.is_develop: raise resp = BaseResponse() resp.status = 1 resp.message = "A server error occoured." return end(resp)