forked from Hay1tsme/artemis
change to using txroutes
This commit is contained in:
parent
32879491f4
commit
f18e939dd0
@ -1,4 +1,5 @@
|
|||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
from core.allnet import AllnetServlet, BillingServlet
|
from core.allnet import AllnetServlet
|
||||||
from core.aimedb import AimedbFactory
|
from core.aimedb import AimedbFactory
|
||||||
from core.title import TitleServlet
|
from core.title import TitleServlet
|
||||||
|
from core.utils import Utils
|
||||||
|
@ -212,7 +212,7 @@ class AimedbFactory(Factory):
|
|||||||
log_fmt = logging.Formatter(log_fmt_str)
|
log_fmt = logging.Formatter(log_fmt_str)
|
||||||
self.logger = logging.getLogger("aimedb")
|
self.logger = logging.getLogger("aimedb")
|
||||||
|
|
||||||
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.config.server.logs, "aimedb"), when="d", backupCount=10)
|
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.config.server.log_dir, "aimedb"), when="d", backupCount=10)
|
||||||
fileHandler.setFormatter(log_fmt)
|
fileHandler.setFormatter(log_fmt)
|
||||||
|
|
||||||
consoleHandler = logging.StreamHandler()
|
consoleHandler = logging.StreamHandler()
|
||||||
|
232
core/allnet.py
232
core/allnet.py
@ -1,12 +1,17 @@
|
|||||||
from twisted.web import resource
|
from typing import Dict, List, Any, Optional
|
||||||
import logging, coloredlogs
|
import logging, coloredlogs
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
from twisted.web.http import Request
|
||||||
|
from datetime import datetime
|
||||||
|
import pytz
|
||||||
|
import base64
|
||||||
|
import zlib
|
||||||
|
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
from core.data import Data
|
from core.data import Data
|
||||||
|
from core.utils import Utils
|
||||||
|
|
||||||
class AllnetServlet(resource.Resource):
|
class AllnetServlet():
|
||||||
isLeaf = True
|
|
||||||
def __init__(self, core_cfg: CoreConfig, cfg_folder: str):
|
def __init__(self, core_cfg: CoreConfig, cfg_folder: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.config = core_cfg
|
self.config = core_cfg
|
||||||
@ -14,26 +19,211 @@ class AllnetServlet(resource.Resource):
|
|||||||
self.data = Data(core_cfg)
|
self.data = Data(core_cfg)
|
||||||
|
|
||||||
self.logger = logging.getLogger("allnet")
|
self.logger = logging.getLogger("allnet")
|
||||||
log_fmt_str = "[%(asctime)s] Allnet | %(levelname)s | %(message)s"
|
if not hasattr(self.logger, "initialized"):
|
||||||
log_fmt = logging.Formatter(log_fmt_str)
|
log_fmt_str = "[%(asctime)s] Allnet | %(levelname)s | %(message)s"
|
||||||
|
log_fmt = logging.Formatter(log_fmt_str)
|
||||||
|
|
||||||
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.config.server.log_dir, "allnet"), when="d", backupCount=10)
|
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.config.server.log_dir, "allnet"), when="d", backupCount=10)
|
||||||
fileHandler.setFormatter(log_fmt)
|
fileHandler.setFormatter(log_fmt)
|
||||||
|
|
||||||
consoleHandler = logging.StreamHandler()
|
consoleHandler = logging.StreamHandler()
|
||||||
consoleHandler.setFormatter(log_fmt)
|
consoleHandler.setFormatter(log_fmt)
|
||||||
|
|
||||||
self.logger.addHandler(fileHandler)
|
self.logger.addHandler(fileHandler)
|
||||||
self.logger.addHandler(consoleHandler)
|
self.logger.addHandler(consoleHandler)
|
||||||
|
|
||||||
self.logger.setLevel(core_cfg.allnet.loglevel)
|
self.logger.setLevel(core_cfg.allnet.loglevel)
|
||||||
coloredlogs.install(level=core_cfg.allnet.loglevel, logger=self.logger, fmt=log_fmt_str)
|
coloredlogs.install(level=core_cfg.allnet.loglevel, logger=self.logger, fmt=log_fmt_str)
|
||||||
|
self.logger.initialized = True
|
||||||
|
|
||||||
class BillingServlet(resource.Resource):
|
if "game_registry" not in globals():
|
||||||
isLeaf = True
|
globals()["game_registry"] = Utils.get_all_titles()
|
||||||
def __init__(self, core_cfg: CoreConfig, cfg_folder: str):
|
|
||||||
super().__init__()
|
if len(globals()["game_registry"]) == 0:
|
||||||
self.config = core_cfg
|
self.logger.error("No games detected!")
|
||||||
self.config_folder = cfg_folder
|
|
||||||
self.data = Data(core_cfg)
|
def handle_poweron(self, request: Request):
|
||||||
self.logger = logging.getLogger('allnet')
|
try:
|
||||||
|
req = AllnetPowerOnRequest(self.allnet_req_to_dict(request.content.getvalue()))
|
||||||
|
# Validate the request. Currently we only validate the fields we plan on using
|
||||||
|
|
||||||
|
if not req.game_id or not req.ver or not req.token or not req.serial or not req.ip:
|
||||||
|
raise AllnetRequestException(f"Bad request params {vars(req)}")
|
||||||
|
except AllnetRequestException as e:
|
||||||
|
self.logger.error(e)
|
||||||
|
return b""
|
||||||
|
|
||||||
|
def handle_dlorder(self, request: Request):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def handle_billing_request(self, request: Request):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def kvp_to_dict(self, *kvp: str) -> List[Dict[str, Any]]:
|
||||||
|
ret: List[Dict[str, Any]] = []
|
||||||
|
for x in kvp:
|
||||||
|
items = x.split('&')
|
||||||
|
tmp = {}
|
||||||
|
|
||||||
|
for item in items:
|
||||||
|
kvp = item.split('=')
|
||||||
|
if len(kvp) == 2:
|
||||||
|
tmp[kvp[0]] = kvp[1]
|
||||||
|
|
||||||
|
ret.append(tmp)
|
||||||
|
|
||||||
|
def allnet_req_to_dict(self, data: bytes):
|
||||||
|
"""
|
||||||
|
Parses an billing request string into a python dictionary
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
decomp = zlib.decompressobj(-zlib.MAX_WBITS)
|
||||||
|
unzipped = decomp.decompress(data)
|
||||||
|
sections = unzipped.decode('ascii').split('\r\n')
|
||||||
|
|
||||||
|
return Utils.kvp_to_dict(sections)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def billing_req_to_dict(self, data: str) -> Optional[List[Dict[str, Any]]]:
|
||||||
|
"""
|
||||||
|
Parses an allnet request string into a python dictionary
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
zipped = base64.b64decode(data)
|
||||||
|
unzipped = zlib.decompress(zipped)
|
||||||
|
sections = unzipped.decode('utf-8').split('\r\n')
|
||||||
|
|
||||||
|
return Utils.kvp_to_dict(sections)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def dict_to_http_form_string(self, data:List[Dict[str, Any]], crlf: bool = False, trailing_newline: bool = True) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
Takes a python dictionary and parses it into an allnet response string
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
urlencode = ""
|
||||||
|
for item in data:
|
||||||
|
for k,v in item.items():
|
||||||
|
urlencode += f"{k}={v}&"
|
||||||
|
|
||||||
|
if crlf:
|
||||||
|
urlencode = urlencode[:-1] + "\r\n"
|
||||||
|
else:
|
||||||
|
urlencode = urlencode[:-1] + "\n"
|
||||||
|
|
||||||
|
if not trailing_newline:
|
||||||
|
if crlf:
|
||||||
|
urlencode = urlencode[:-2]
|
||||||
|
else:
|
||||||
|
urlencode = urlencode[:-1]
|
||||||
|
|
||||||
|
return urlencode
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return None
|
||||||
|
|
||||||
|
class AllnetPowerOnRequest():
|
||||||
|
def __init__(self, req: Dict) -> None:
|
||||||
|
if req is None:
|
||||||
|
raise AllnetRequestException("Request processing failed")
|
||||||
|
self.game_id: str = req["game_id"] if "game_id" in req else ""
|
||||||
|
self.ver: str = req["ver"] if "ver" in req else ""
|
||||||
|
self.serial: str = req["serial"] if "serial" in req else ""
|
||||||
|
self.ip: str = req["ip"] if "ip" in req else ""
|
||||||
|
self.firm_ver: str = req["firm_ver"] if "firm_ver" in req else ""
|
||||||
|
self.boot_ver: str = req["boot_ver"] if "boot_ver" in req else ""
|
||||||
|
self.encode: str = req["encode"] if "encode" in req else ""
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.hops = int(req["hops"]) if "hops" in req else 0
|
||||||
|
self.format_ver = int(req["format_ver"]) if "format_ver" in req else 2
|
||||||
|
self.token = int(req["token"]) if "token" in req else 0
|
||||||
|
except ValueError as e:
|
||||||
|
raise AllnetRequestException(f"Failed to parse int: {e}")
|
||||||
|
|
||||||
|
class AllnetPowerOnResponse3():
|
||||||
|
def __init__(self, token) -> None:
|
||||||
|
self.stat = 1
|
||||||
|
self.uri = ""
|
||||||
|
self.host = ""
|
||||||
|
self.place_id = "123"
|
||||||
|
self.name = ""
|
||||||
|
self.nickname = ""
|
||||||
|
self.region0 = "1"
|
||||||
|
self.region_name0 = "W"
|
||||||
|
self.region_name1 = ""
|
||||||
|
self.region_name2 = ""
|
||||||
|
self.region_name3 = ""
|
||||||
|
self.country = "JPN"
|
||||||
|
self.allnet_id = "123"
|
||||||
|
self.client_timezone = "+0900"
|
||||||
|
self.utc_time = datetime.now(tz=pytz.timezone('UTC')).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||||
|
self.setting = ""
|
||||||
|
self.res_ver = "3"
|
||||||
|
self.token = str(token)
|
||||||
|
|
||||||
|
class AllnetPowerOnResponse2():
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.stat = 1
|
||||||
|
self.uri = ""
|
||||||
|
self.host = ""
|
||||||
|
self.place_id = "123"
|
||||||
|
self.name = "Test"
|
||||||
|
self.nickname = "Test123"
|
||||||
|
self.region0 = "1"
|
||||||
|
self.region_name0 = "W"
|
||||||
|
self.region_name1 = "X"
|
||||||
|
self.region_name2 = "Y"
|
||||||
|
self.region_name3 = "Z"
|
||||||
|
self.country = "JPN"
|
||||||
|
self.year = datetime.now().year
|
||||||
|
self.month = datetime.now().month
|
||||||
|
self.day = datetime.now().day
|
||||||
|
self.hour = datetime.now().hour
|
||||||
|
self.minute = datetime.now().minute
|
||||||
|
self.second = datetime.now().second
|
||||||
|
self.setting = "1"
|
||||||
|
self.timezone = "+0900"
|
||||||
|
self.res_class = "PowerOnResponseV2"
|
||||||
|
|
||||||
|
class AllnetDownloadOrderRequest():
|
||||||
|
def __init__(self, req: Dict) -> None:
|
||||||
|
self.game_id = req["game_id"] if "game_id" in req else ""
|
||||||
|
self.ver = req["ver"] if "ver" in req else ""
|
||||||
|
self.serial = req["serial"] if "serial" in req else ""
|
||||||
|
self.encode = req["encode"] if "encode" in req else ""
|
||||||
|
|
||||||
|
class AllnetDownloadOrderResponse():
|
||||||
|
def __init__(self, stat: int = 1, serial: str = "", uri: str = "null") -> None:
|
||||||
|
self.stat = stat
|
||||||
|
self.serial = serial
|
||||||
|
self.uri = uri
|
||||||
|
|
||||||
|
class BillingResponse():
|
||||||
|
def __init__(self, playlimit: str, playlimit_sig: str, nearfull: str, nearfull_sig: str,
|
||||||
|
playhistory: str = "000000/0:000000/0:000000/0") -> None:
|
||||||
|
|
||||||
|
self.result = "0"
|
||||||
|
self.waitime = "100"
|
||||||
|
self.linelimit = "1"
|
||||||
|
self.message = ""
|
||||||
|
self.playlimit = playlimit
|
||||||
|
self.playlimitsig = playlimit_sig
|
||||||
|
self.protocolver = "1.000"
|
||||||
|
self.nearfull = nearfull
|
||||||
|
self.nearfullsig = nearfull_sig
|
||||||
|
self.fixlogincnt = "0"
|
||||||
|
self.fixinterval = "5"
|
||||||
|
self.playhistory = playhistory
|
||||||
|
# playhistory -> YYYYMM/C:...
|
||||||
|
# YYYY -> 4 digit year, MM -> 2 digit month, C -> Playcount during that period
|
||||||
|
|
||||||
|
class AllnetRequestException(Exception):
|
||||||
|
pass
|
||||||
|
@ -7,27 +7,27 @@ class ServerConfig:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def listen_address(self) -> str:
|
def listen_address(self) -> str:
|
||||||
return CoreConfig.get_config_field(self.__config, '127.0.0.1', 'core', 'server', 'listen_address')
|
return CoreConfig.get_config_field(self.__config, 'core', 'server', 'listen_address', default='127.0.0.1')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def allow_user_registration(self) -> bool:
|
def allow_user_registration(self) -> bool:
|
||||||
return CoreConfig.get_config_field(self.__config, True, 'core', 'server', 'allow_user_registration')
|
return CoreConfig.get_config_field(self.__config, 'core', 'server', 'allow_user_registration', default=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def allow_unregistered_games(self) -> bool:
|
def allow_unregistered_games(self) -> bool:
|
||||||
return CoreConfig.get_config_field(self.__config, True, 'core', 'server', 'allow_unregistered_games')
|
return CoreConfig.get_config_field(self.__config, 'core', 'server', 'allow_unregistered_games', default=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
return CoreConfig.get_config_field(self.__config, "ARTEMiS", 'core', 'server', 'name')
|
return CoreConfig.get_config_field(self.__config, 'core', 'server', 'name', default="ARTEMiS")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_develop(self) -> bool:
|
def is_develop(self) -> bool:
|
||||||
return CoreConfig.get_config_field(self.__config, True, 'core', 'server', 'is_develop')
|
return CoreConfig.get_config_field(self.__config, 'core', 'server', 'is_develop', default=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def log_dir(self) -> str:
|
def log_dir(self) -> str:
|
||||||
return CoreConfig.get_config_field(self.__config, 'logs', 'core', 'server', 'log_dir')
|
return CoreConfig.get_config_field(self.__config, 'core', 'server', 'log_dir', default='logs')
|
||||||
|
|
||||||
class TitleConfig:
|
class TitleConfig:
|
||||||
def __init__(self, parent_config: "CoreConfig") -> None:
|
def __init__(self, parent_config: "CoreConfig") -> None:
|
||||||
@ -35,15 +35,15 @@ class TitleConfig:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def loglevel(self) -> int:
|
def loglevel(self) -> int:
|
||||||
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, "info", 'core', 'title', 'loglevel'))
|
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'core', 'title', 'loglevel', default="info"))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hostname(self) -> str:
|
def hostname(self) -> str:
|
||||||
return CoreConfig.get_config_field(self.__config, "localhost", 'core', 'title', 'hostname')
|
return CoreConfig.get_config_field(self.__config, 'core', 'title', 'hostname', default="localhost")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def port(self) -> int:
|
def port(self) -> int:
|
||||||
return CoreConfig.get_config_field(self.__config, 8080, 'core', 'title', 'port')
|
return CoreConfig.get_config_field(self.__config, 'core', 'title', 'port', default=8080)
|
||||||
|
|
||||||
class DatabaseConfig:
|
class DatabaseConfig:
|
||||||
def __init__(self, parent_config: "CoreConfig") -> None:
|
def __init__(self, parent_config: "CoreConfig") -> None:
|
||||||
@ -51,43 +51,43 @@ class DatabaseConfig:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def host(self) -> str:
|
def host(self) -> str:
|
||||||
return CoreConfig.get_config_field(self.__config, "localhost", 'core', 'database', 'host')
|
return CoreConfig.get_config_field(self.__config, 'core', 'database', 'host', default="localhost")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def username(self) -> str:
|
def username(self) -> str:
|
||||||
return CoreConfig.get_config_field(self.__config, 'aime', 'core', 'database', 'username')
|
return CoreConfig.get_config_field(self.__config, 'core', 'database', 'username', default='aime')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def password(self) -> str:
|
def password(self) -> str:
|
||||||
return CoreConfig.get_config_field(self.__config, 'aime', 'core', 'database', 'password')
|
return CoreConfig.get_config_field(self.__config, 'core', 'database', 'password', default='aime')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
return CoreConfig.get_config_field(self.__config, 'aime', 'core', 'database', 'name')
|
return CoreConfig.get_config_field(self.__config, 'core', 'database', 'name', default='aime')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def port(self) -> int:
|
def port(self) -> int:
|
||||||
return CoreConfig.get_config_field(self.__config, 3306, 'core', 'database', 'port')
|
return CoreConfig.get_config_field(self.__config, 'core', 'database', 'port', default=3306)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def protocol(self) -> str:
|
def protocol(self) -> str:
|
||||||
return CoreConfig.get_config_field(self.__config, "mysql", 'core', 'database', 'type')
|
return CoreConfig.get_config_field(self.__config, 'core', 'database', 'type', default="mysql")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sha2_password(self) -> bool:
|
def sha2_password(self) -> bool:
|
||||||
return CoreConfig.get_config_field(self.__config, False, 'core', 'database', 'sha2_password')
|
return CoreConfig.get_config_field(self.__config, 'core', 'database', 'sha2_password', default=False)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def loglevel(self) -> int:
|
def loglevel(self) -> int:
|
||||||
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, "info", 'core', 'database', 'loglevel'))
|
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'core', 'database', 'loglevel', default="info"))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def user_table_autoincrement_start(self) -> int:
|
def user_table_autoincrement_start(self) -> int:
|
||||||
return CoreConfig.get_config_field(self.__config, 10000, 'core', 'database', 'user_table_autoincrement_start')
|
return CoreConfig.get_config_field(self.__config, 'core', 'database', 'user_table_autoincrement_start', default=10000)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def memcached_host(self) -> str:
|
def memcached_host(self) -> str:
|
||||||
return CoreConfig.get_config_field(self.__config, "localhost", 'core', 'database', 'memcached_host')
|
return CoreConfig.get_config_field(self.__config, 'core', 'database', 'memcached_host', default="localhost")
|
||||||
|
|
||||||
class FrontendConfig:
|
class FrontendConfig:
|
||||||
def __init__(self, parent_config: "CoreConfig") -> None:
|
def __init__(self, parent_config: "CoreConfig") -> None:
|
||||||
@ -95,15 +95,15 @@ class FrontendConfig:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def enable(self) -> int:
|
def enable(self) -> int:
|
||||||
return CoreConfig.get_config_field(self.__config, False, 'core', 'frontend', 'enable')
|
return CoreConfig.get_config_field(self.__config, 'core', 'frontend', 'enable', default=False)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def port(self) -> int:
|
def port(self) -> int:
|
||||||
return CoreConfig.get_config_field(self.__config, 8090, 'core', 'frontend', 'port')
|
return CoreConfig.get_config_field(self.__config, 'core', 'frontend', 'port', default=8090)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def loglevel(self) -> int:
|
def loglevel(self) -> int:
|
||||||
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'core', 'frontend', 'loglevel', "info"))
|
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'core', 'frontend', 'loglevel', default="info"))
|
||||||
|
|
||||||
class AllnetConfig:
|
class AllnetConfig:
|
||||||
def __init__(self, parent_config: "CoreConfig") -> None:
|
def __init__(self, parent_config: "CoreConfig") -> None:
|
||||||
@ -111,15 +111,15 @@ class AllnetConfig:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def loglevel(self) -> int:
|
def loglevel(self) -> int:
|
||||||
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, "info", 'core', 'allnet', 'loglevel'))
|
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'core', 'allnet', 'loglevel', default="info"))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def port(self) -> int:
|
def port(self) -> int:
|
||||||
return CoreConfig.get_config_field(self.__config, 80, 'core', 'allnet', 'port')
|
return CoreConfig.get_config_field(self.__config, 'core', 'allnet', 'port', default=80)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def allow_online_updates(self) -> int:
|
def allow_online_updates(self) -> int:
|
||||||
return CoreConfig.get_config_field(self.__config, False, 'core', 'allnet', 'allow_online_updates')
|
return CoreConfig.get_config_field(self.__config, 'core', 'allnet', 'allow_online_updates', default=False)
|
||||||
|
|
||||||
class BillingConfig:
|
class BillingConfig:
|
||||||
def __init__(self, parent_config: "CoreConfig") -> None:
|
def __init__(self, parent_config: "CoreConfig") -> None:
|
||||||
@ -127,19 +127,19 @@ class BillingConfig:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def port(self) -> int:
|
def port(self) -> int:
|
||||||
return CoreConfig.get_config_field(self.__config, 8443, 'core', 'billing', 'port')
|
return CoreConfig.get_config_field(self.__config, 'core', 'billing', 'port', default=8443)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ssl_key(self) -> str:
|
def ssl_key(self) -> str:
|
||||||
return CoreConfig.get_config_field(self.__config, "cert/server.key", 'core', 'billing', 'ssl_key')
|
return CoreConfig.get_config_field(self.__config, 'core', 'billing', 'ssl_key', default="cert/server.key")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ssl_cert(self) -> str:
|
def ssl_cert(self) -> str:
|
||||||
return CoreConfig.get_config_field(self.__config, "cert/server.pem", 'core', 'billing', 'ssl_cert')
|
return CoreConfig.get_config_field(self.__config, 'core', 'billing', 'ssl_cert', default="cert/server.pem")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def signing_key(self) -> str:
|
def signing_key(self) -> str:
|
||||||
return CoreConfig.get_config_field(self.__config, "cert/billing.key", 'core', 'billing', 'signing_key')
|
return CoreConfig.get_config_field(self.__config, 'core', 'billing', 'signing_key', default="cert/billing.key")
|
||||||
|
|
||||||
class AimedbConfig:
|
class AimedbConfig:
|
||||||
def __init__(self, parent_config: "CoreConfig") -> None:
|
def __init__(self, parent_config: "CoreConfig") -> None:
|
||||||
@ -147,15 +147,15 @@ class AimedbConfig:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def loglevel(self) -> int:
|
def loglevel(self) -> int:
|
||||||
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, "info", 'core', 'aimedb', 'loglevel'))
|
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'core', 'aimedb', 'loglevel', default="info"))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def port(self) -> int:
|
def port(self) -> int:
|
||||||
return CoreConfig.get_config_field(self.__config, 22345, 'core', 'aimedb', 'port')
|
return CoreConfig.get_config_field(self.__config, 'core', 'aimedb', 'port', default=22345)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def key(self) -> str:
|
def key(self) -> str:
|
||||||
return CoreConfig.get_config_field(self.__config, "", 'core', 'aimedb', 'key')
|
return CoreConfig.get_config_field(self.__config, 'core', 'aimedb', 'key', default="")
|
||||||
|
|
||||||
class CoreConfig(dict):
|
class CoreConfig(dict):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
@ -179,8 +179,8 @@ class CoreConfig(dict):
|
|||||||
return logging.INFO
|
return logging.INFO
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_config_field(cls, __config: dict, default: Any, *path: str) -> Any:
|
def get_config_field(cls, __config: dict, module, *path: str, default: Any = "") -> Any:
|
||||||
envKey = 'CFG_'
|
envKey = f'CFG_{module}_'
|
||||||
for arg in path:
|
for arg in path:
|
||||||
envKey += arg + '_'
|
envKey += arg + '_'
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ class Data:
|
|||||||
|
|
||||||
# Prevent the logger from adding handlers multiple times
|
# Prevent the logger from adding handlers multiple times
|
||||||
if not getattr(self.logger, 'handler_set', None):
|
if not getattr(self.logger, 'handler_set', None):
|
||||||
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.config.server.logs, "db"), encoding="utf-8",
|
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.config.server.log_dir, "db"), encoding="utf-8",
|
||||||
when="d", backupCount=10)
|
when="d", backupCount=10)
|
||||||
fileHandler.setFormatter(log_fmt)
|
fileHandler.setFormatter(log_fmt)
|
||||||
|
|
||||||
|
@ -1,14 +1,42 @@
|
|||||||
from twisted.web import resource
|
from twisted.web import resource
|
||||||
import logging, coloredlogs
|
import logging, coloredlogs
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
from twisted.web.http import Request
|
||||||
|
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
from core.data import Data
|
from core.data import Data
|
||||||
|
from core.utils import Utils
|
||||||
|
|
||||||
class TitleServlet(resource.Resource):
|
class TitleServlet():
|
||||||
isLeaf = True
|
|
||||||
def __init__(self, core_cfg: CoreConfig, cfg_folder: str):
|
def __init__(self, core_cfg: CoreConfig, cfg_folder: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.config = core_cfg
|
self.config = core_cfg
|
||||||
self.config_folder = cfg_folder
|
self.config_folder = cfg_folder
|
||||||
self.data = Data(core_cfg)
|
self.data = Data(core_cfg)
|
||||||
|
|
||||||
|
self.logger = logging.getLogger("title")
|
||||||
|
if not hasattr(self.logger, "initialized"):
|
||||||
|
log_fmt_str = "[%(asctime)s] Title | %(levelname)s | %(message)s"
|
||||||
|
log_fmt = logging.Formatter(log_fmt_str)
|
||||||
|
|
||||||
|
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.config.server.log_dir, "title"), 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.title.loglevel)
|
||||||
|
coloredlogs.install(level=core_cfg.title.loglevel, logger=self.logger, fmt=log_fmt_str)
|
||||||
|
self.logger.initialized = True
|
||||||
|
|
||||||
|
if "game_registry" not in globals():
|
||||||
|
globals()["game_registry"] = Utils.get_all_titles()
|
||||||
|
|
||||||
|
def handle_GET(self, request: Request):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def handle_POST(self, request: Request):
|
||||||
|
pass
|
21
core/utils.py
Normal file
21
core/utils.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
from typing import Dict, List, Any, Optional
|
||||||
|
from types import ModuleType
|
||||||
|
import zlib, base64
|
||||||
|
import importlib
|
||||||
|
from os import walk
|
||||||
|
|
||||||
|
class Utils:
|
||||||
|
@classmethod
|
||||||
|
def get_all_titles(cls) -> Dict[str, ModuleType]:
|
||||||
|
ret: Dict[str, Any] = {}
|
||||||
|
|
||||||
|
for root, dirs, files in walk("titles"):
|
||||||
|
for dir in dirs:
|
||||||
|
if not dir.startswith("__"):
|
||||||
|
try:
|
||||||
|
mod = importlib.import_module(f"titles.{dir}")
|
||||||
|
ret[dir] = mod
|
||||||
|
|
||||||
|
except ImportError as e:
|
||||||
|
print(f"{dir} - {e}")
|
||||||
|
return ret
|
@ -4,7 +4,7 @@ server:
|
|||||||
allow_unregistered_games: True
|
allow_unregistered_games: True
|
||||||
name: "ARTEMiS"
|
name: "ARTEMiS"
|
||||||
is_develop: True
|
is_develop: True
|
||||||
log_dir: False
|
log_dir: "logs"
|
||||||
|
|
||||||
title:
|
title:
|
||||||
loglevel: "info"
|
loglevel: "info"
|
||||||
|
21
index.py
21
index.py
@ -6,6 +6,7 @@ from core import *
|
|||||||
|
|
||||||
from twisted.web import server
|
from twisted.web import server
|
||||||
from twisted.internet import reactor, endpoints
|
from twisted.internet import reactor, endpoints
|
||||||
|
from txroutes import Dispatcher
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="ARTEMiS main entry point")
|
parser = argparse.ArgumentParser(description="ARTEMiS main entry point")
|
||||||
@ -26,14 +27,14 @@ if __name__ == "__main__":
|
|||||||
print(f"Log directory {cfg.server.log_dir} NOT writable, please check permissions")
|
print(f"Log directory {cfg.server.log_dir} NOT writable, please check permissions")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
if cfg.aimedb.key == "":
|
if not cfg.aimedb.key:
|
||||||
print("!!AIMEDB KEY BLANK, SET KEY IN CORE.YAML!!")
|
print("!!AIMEDB KEY BLANK, SET KEY IN CORE.YAML!!")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
print(f"ARTEMiS starting in {'develop' if cfg.server.is_develop else 'production'} mode")
|
print(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}"
|
allnet_server_str = f"tcp:{cfg.allnet.port}:interface={cfg.server.listen_address}"
|
||||||
title_server_str = f"tcp:{cfg.billing.port}:interface={cfg.server.listen_address}"
|
title_server_str = f"tcp:{cfg.title.port}:interface={cfg.server.listen_address}"
|
||||||
adb_server_str = f"tcp:{cfg.aimedb.port}:interface={cfg.server.listen_address}"
|
adb_server_str = f"tcp:{cfg.aimedb.port}:interface={cfg.server.listen_address}"
|
||||||
|
|
||||||
billing_server_str = f"tcp:{cfg.billing.port}:interface={cfg.server.listen_address}"
|
billing_server_str = f"tcp:{cfg.billing.port}:interface={cfg.server.listen_address}"
|
||||||
@ -41,13 +42,23 @@ if __name__ == "__main__":
|
|||||||
billing_server_str = f"ssl:{cfg.billing.port}:interface={cfg.server.listen_address}"\
|
billing_server_str = f"ssl:{cfg.billing.port}:interface={cfg.server.listen_address}"\
|
||||||
f":privateKey={cfg.billing.ssl_key}:certKey={cfg.billing.ssl_cert}"
|
f":privateKey={cfg.billing.ssl_key}:certKey={cfg.billing.ssl_cert}"
|
||||||
|
|
||||||
endpoints.serverFromString(reactor, allnet_server_str).listen(server.Site(AllnetServlet(cfg, args.config)))
|
allnet_cls = AllnetServlet(cfg, args.config)
|
||||||
|
title_cls = TitleServlet(cfg, args.config)
|
||||||
|
|
||||||
|
dispatcher = Dispatcher()
|
||||||
|
dispatcher.connect('allnet_poweron', '/sys/servlet/PowerOn', allnet_cls, action='handle_poweron', conditions=dict(method=['POST']))
|
||||||
|
dispatcher.connect('allnet_downloadorder', '/sys/servlet/DownloadOrder', allnet_cls, action='handle_dlorder', conditions=dict(method=['POST']))
|
||||||
|
dispatcher.connect('allnet_billing', '/request', allnet_cls, action='handle_billing_request', conditions=dict(method=['POST']))
|
||||||
|
dispatcher.connect("title_get", "/{game}/{version}/{endpoint}", title_cls, action="handle_GET", conditions=dict(method=['GET']))
|
||||||
|
dispatcher.connect("title_post", "/{game}/{version}/{endpoint}", title_cls, action="handle_POST", conditions=dict(method=['POST']))
|
||||||
|
|
||||||
|
endpoints.serverFromString(reactor, allnet_server_str).listen(server.Site(dispatcher))
|
||||||
endpoints.serverFromString(reactor, adb_server_str).listen(AimedbFactory(cfg))
|
endpoints.serverFromString(reactor, adb_server_str).listen(AimedbFactory(cfg))
|
||||||
|
|
||||||
if cfg.billing.port > 0:
|
if cfg.billing.port > 0:
|
||||||
endpoints.serverFromString(reactor, billing_server_str).listen(server.Site(BillingServlet(cfg)))
|
endpoints.serverFromString(reactor, billing_server_str).listen(server.Site(dispatcher))
|
||||||
|
|
||||||
if cfg.title.port > 0:
|
if cfg.title.port > 0:
|
||||||
endpoints.serverFromString(reactor, title_server_str).listen(server.Site(TitleServlet(cfg, args.config)))
|
endpoints.serverFromString(reactor, title_server_str).listen(server.Site(dispatcher))
|
||||||
|
|
||||||
reactor.run() # type: ignore
|
reactor.run() # type: ignore
|
@ -12,3 +12,4 @@ inflection
|
|||||||
coloredlogs
|
coloredlogs
|
||||||
pylibmc
|
pylibmc
|
||||||
wacky
|
wacky
|
||||||
|
txroutes
|
||||||
|
@ -11,3 +11,4 @@ PyCryptodome
|
|||||||
inflection
|
inflection
|
||||||
coloredlogs
|
coloredlogs
|
||||||
wacky
|
wacky
|
||||||
|
txroutes
|
||||||
|
Loading…
Reference in New Issue
Block a user