forked from Dniel97/artemis
Merge branch 'develop' into fork_develop
This commit is contained in:
commit
38c1c31cf5
@ -176,18 +176,18 @@ class AllnetServlet:
|
|||||||
else AllnetJapanRegionId.AICHI.value
|
else AllnetJapanRegionId.AICHI.value
|
||||||
)
|
)
|
||||||
resp.region_name0 = (
|
resp.region_name0 = (
|
||||||
arcade["country"]
|
|
||||||
if arcade["country"] is not None
|
|
||||||
else AllnetCountryCode.JAPAN.value
|
|
||||||
)
|
|
||||||
resp.region_name1 = (
|
|
||||||
arcade["state"]
|
arcade["state"]
|
||||||
if arcade["state"] is not None
|
if arcade["state"] is not None
|
||||||
else AllnetJapanRegionId.AICHI.name
|
else AllnetJapanRegionId.AICHI.name
|
||||||
)
|
)
|
||||||
|
resp.region_name1 = (
|
||||||
|
arcade["country"]
|
||||||
|
if arcade["country"] is not None
|
||||||
|
else AllnetCountryCode.JAPAN.value
|
||||||
|
)
|
||||||
resp.region_name2 = arcade["city"] if arcade["city"] is not None else ""
|
resp.region_name2 = arcade["city"] if arcade["city"] is not None else ""
|
||||||
resp.client_timezone = (
|
resp.client_timezone = ( # lmao
|
||||||
arcade["timezone"] if arcade["timezone"] is not None else "+0900"
|
arcade["timezone"] if arcade["timezone"] is not None else "+0900" if req.format_ver == 3 else "+09:00"
|
||||||
)
|
)
|
||||||
|
|
||||||
if req.game_id not in self.uri_registry:
|
if req.game_id not in self.uri_registry:
|
||||||
@ -296,7 +296,6 @@ class AllnetServlet:
|
|||||||
|
|
||||||
return res_str
|
return res_str
|
||||||
|
|
||||||
|
|
||||||
def handle_dlorder_ini(self, request: Request, match: Dict) -> bytes:
|
def handle_dlorder_ini(self, request: Request, match: Dict) -> bytes:
|
||||||
if "file" not in match:
|
if "file" not in match:
|
||||||
return b""
|
return b""
|
||||||
|
@ -218,9 +218,16 @@ class ArcadeData(BaseData):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def find_arcade_by_name(self, name: str) -> List[Row]:
|
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}%")))
|
sql = arcade.select(or_(arcade.c.name.like(f"%{name}%"), arcade.c.nickname.like(f"%{name}%")))
|
||||||
result = self.execute(sql)
|
result = self.execute(sql)
|
||||||
if result is None:
|
if result is None:
|
||||||
return False
|
return None
|
||||||
|
return result.fetchall()
|
||||||
|
|
||||||
|
def get_arcades_by_ip(self, ip: str) -> Optional[List[Row]]:
|
||||||
|
sql = arcade.select().where(arcade.c.ip == ip)
|
||||||
|
result = self.execute(sql)
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
return result.fetchall()
|
return result.fetchall()
|
||||||
|
@ -21,6 +21,7 @@ class IUserSession(Interface):
|
|||||||
userId = Attribute("User's ID")
|
userId = Attribute("User's ID")
|
||||||
current_ip = Attribute("User's current ip address")
|
current_ip = Attribute("User's current ip address")
|
||||||
permissions = Attribute("User's permission level")
|
permissions = Attribute("User's permission level")
|
||||||
|
ongeki_version = Attribute("User's selected Ongeki Version")
|
||||||
|
|
||||||
class PermissionOffset(Enum):
|
class PermissionOffset(Enum):
|
||||||
USER = 0 # Regular user
|
USER = 0 # Regular user
|
||||||
@ -36,6 +37,7 @@ class UserSession(object):
|
|||||||
self.userId = 0
|
self.userId = 0
|
||||||
self.current_ip = "0.0.0.0"
|
self.current_ip = "0.0.0.0"
|
||||||
self.permissions = 0
|
self.permissions = 0
|
||||||
|
self.ongeki_version = 7
|
||||||
|
|
||||||
|
|
||||||
class FrontendServlet(resource.Resource):
|
class FrontendServlet(resource.Resource):
|
||||||
@ -304,9 +306,9 @@ class FE_System(FE_Base):
|
|||||||
def render_GET(self, request: Request):
|
def render_GET(self, request: Request):
|
||||||
uri = request.uri.decode()
|
uri = request.uri.decode()
|
||||||
template = self.environment.get_template("core/frontend/sys/index.jinja")
|
template = self.environment.get_template("core/frontend/sys/index.jinja")
|
||||||
usrlist = []
|
usrlist: List[Dict] = []
|
||||||
aclist = []
|
aclist: List[Dict] = []
|
||||||
cablist = []
|
cablist: List[Dict] = []
|
||||||
|
|
||||||
sesh: Session = request.getSession()
|
sesh: Session = request.getSession()
|
||||||
usr_sesh = IUserSession(sesh)
|
usr_sesh = IUserSession(sesh)
|
||||||
@ -339,6 +341,7 @@ class FE_System(FE_Base):
|
|||||||
ac_id_search = uri_parse.get("arcadeId")
|
ac_id_search = uri_parse.get("arcadeId")
|
||||||
ac_name_search = uri_parse.get("arcadeName")
|
ac_name_search = uri_parse.get("arcadeName")
|
||||||
ac_user_search = uri_parse.get("arcadeUser")
|
ac_user_search = uri_parse.get("arcadeUser")
|
||||||
|
ac_ip_search = uri_parse.get("arcadeIp")
|
||||||
|
|
||||||
if ac_id_search is not None:
|
if ac_id_search is not None:
|
||||||
u = self.data.arcade.get_arcade(ac_id_search[0])
|
u = self.data.arcade.get_arcade(ac_id_search[0])
|
||||||
@ -346,12 +349,20 @@ class FE_System(FE_Base):
|
|||||||
aclist.append(u._asdict())
|
aclist.append(u._asdict())
|
||||||
|
|
||||||
elif ac_name_search is not None:
|
elif ac_name_search is not None:
|
||||||
ul = self.data.arcade.find_arcade_by_name(ac_name_search[0])
|
ul = self.data.arcade.get_arcade_by_name(ac_name_search[0])
|
||||||
|
if ul is not None:
|
||||||
for u in ul:
|
for u in ul:
|
||||||
aclist.append(u._asdict())
|
aclist.append(u._asdict())
|
||||||
|
|
||||||
elif ac_user_search is not None:
|
elif ac_user_search is not None:
|
||||||
ul = self.data.arcade.get_arcades_managed_by_user(ac_user_search[0])
|
ul = 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])
|
||||||
|
if ul is not None:
|
||||||
for u in ul:
|
for u in ul:
|
||||||
aclist.append(u._asdict())
|
aclist.append(u._asdict())
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
<title>{{ title }}</title>
|
<title>{{ title }}</title>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
|
||||||
<style>
|
<style>
|
||||||
html {
|
html {
|
||||||
background-color: #181a1b !important;
|
background-color: #181a1b !important;
|
||||||
@ -77,6 +78,9 @@
|
|||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
width: 15%;
|
width: 15%;
|
||||||
}
|
}
|
||||||
|
.modal-content {
|
||||||
|
background-color: #181a1b;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -8,8 +8,8 @@
|
|||||||
<form id="usrLookup" name="usrLookup" action="/sys/lookup.user" class="form-inline">
|
<form id="usrLookup" name="usrLookup" action="/sys/lookup.user" class="form-inline">
|
||||||
<h3>User Search</h3>
|
<h3>User Search</h3>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="usrEmail">Email address</label>
|
<label for="usrId">User ID</label>
|
||||||
<input type="email" class="form-control" id="usrEmail" name="usrEmail" aria-describedby="emailHelp">
|
<input type="number" class="form-control" id="usrId" name="usrId">
|
||||||
</div>
|
</div>
|
||||||
OR
|
OR
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -18,8 +18,8 @@
|
|||||||
</div>
|
</div>
|
||||||
OR
|
OR
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="usrId">User ID</label>
|
<label for="usrEmail">Email address</label>
|
||||||
<input type="number" class="form-control" id="usrId" name="usrId">
|
<input type="email" class="form-control" id="usrEmail" name="usrEmail" aria-describedby="emailHelp">
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<button type="submit" class="btn btn-primary">Search</button>
|
<button type="submit" class="btn btn-primary">Search</button>
|
||||||
@ -30,20 +30,25 @@
|
|||||||
<div class="col-sm-6" style="max-width: 25%;">
|
<div class="col-sm-6" style="max-width: 25%;">
|
||||||
<form id="arcadeLookup" name="arcadeLookup" action="/sys/lookup.arcade" class="form-inline" >
|
<form id="arcadeLookup" name="arcadeLookup" action="/sys/lookup.arcade" class="form-inline" >
|
||||||
<h3>Arcade Search</h3>
|
<h3>Arcade Search</h3>
|
||||||
<div class="form-group">
|
|
||||||
<label for="arcadeName">Arcade Name</label>
|
|
||||||
<input type="text" class="form-control" id="arcadeName" name="arcadeName">
|
|
||||||
</div>
|
|
||||||
OR
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="arcadeId">Arcade ID</label>
|
<label for="arcadeId">Arcade ID</label>
|
||||||
<input type="number" class="form-control" id="arcadeId" name="arcadeId">
|
<input type="number" class="form-control" id="arcadeId" name="arcadeId">
|
||||||
</div>
|
</div>
|
||||||
OR
|
OR
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="arcadeName">Arcade Name</label>
|
||||||
|
<input type="text" class="form-control" id="arcadeName" name="arcadeName">
|
||||||
|
</div>
|
||||||
|
OR
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="arcadeUser">Owner User ID</label>
|
<label for="arcadeUser">Owner User ID</label>
|
||||||
<input type="number" class="form-control" id="arcadeUser" name="arcadeUser">
|
<input type="number" class="form-control" id="arcadeUser" name="arcadeUser">
|
||||||
</div>
|
</div>
|
||||||
|
OR
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="arcadeIp">Assigned IP Address</label>
|
||||||
|
<input type="text" class="form-control" id="arcadeIp" name="arcadeIp">
|
||||||
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<button type="submit" class="btn btn-primary">Search</button>
|
<button type="submit" class="btn btn-primary">Search</button>
|
||||||
</form>
|
</form>
|
||||||
@ -52,13 +57,13 @@
|
|||||||
<form id="cabLookup" name="cabLookup" action="/sys/lookup.cab" class="form-inline" >
|
<form id="cabLookup" name="cabLookup" action="/sys/lookup.cab" class="form-inline" >
|
||||||
<h3>Machine Search</h3>
|
<h3>Machine Search</h3>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="cabSerial">Machine Serial</label>
|
<label for="cabId">Machine ID</label>
|
||||||
<input type="text" class="form-control" id="cabSerial" name="cabSerial">
|
<input type="number" class="form-control" id="cabId" name="cabId">
|
||||||
</div>
|
</div>
|
||||||
OR
|
OR
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="cabId">Machine ID</label>
|
<label for="cabSerial">Machine Serial</label>
|
||||||
<input type="number" class="form-control" id="cabId" name="cabId">
|
<input type="text" class="form-control" id="cabSerial" name="cabSerial">
|
||||||
</div>
|
</div>
|
||||||
OR
|
OR
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -75,19 +80,19 @@
|
|||||||
{% if sesh.permissions >= 2 %}
|
{% if sesh.permissions >= 2 %}
|
||||||
<div id="userSearchResult" class="col-sm-6" style="max-width: 25%;">
|
<div id="userSearchResult" class="col-sm-6" style="max-width: 25%;">
|
||||||
{% for usr in usrlist %}
|
{% for usr in usrlist %}
|
||||||
<pre><a href=/user/{{ usr.id }}>{{ usr.id }} | {{ usr.username }}</a></pre>
|
<a href=/user/{{ usr.id }}><pre>{{ usr.id }} | {{ usr.username if usr.username != None else "<i>No Name Set</i>"}}</pre></a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if sesh.permissions >= 4 %}
|
{% if sesh.permissions >= 4 %}
|
||||||
<div id="arcadeSearchResult" class="col-sm-6" style="max-width: 25%;">
|
<div id="arcadeSearchResult" class="col-sm-6" style="max-width: 25%;">
|
||||||
{% for ac in aclist %}
|
{% for ac in aclist %}
|
||||||
<pre><a href=/arcade/{{ ac.id }}>{{ ac.id }} | {{ ac.name }}</a></pre>
|
<pre><a href=/arcade/{{ ac.id }}>{{ ac.id }} | {{ ac.name if ac.name != None else "<i>No Name Set</i>" }} | {{ ac.ip if ac.ip != None else "<i>No IP Assigned</i>"}}</pre></a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div
|
</div
|
||||||
><div id="cabSearchResult" class="col-sm-6" style="max-width: 25%;">
|
><div id="cabSearchResult" class="col-sm-6" style="max-width: 25%;">
|
||||||
{% for cab in cablist %}
|
{% for cab in cablist %}
|
||||||
<a href=/cab/{{ cab.id }}><pre>{{ cab.id }} | {{ cab.game if cab.game is defined else "ANY " }} | {{ cab.serial }}</pre></a>
|
<a href=/cab/{{ cab.id }}><pre>{{ cab.id }} | {{ cab.game if cab.game != None else "<i>ANY </i>" }} | {{ cab.serial }}</pre></a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -35,3 +35,6 @@ version:
|
|||||||
card_maker: 1.30.01
|
card_maker: 1.30.01
|
||||||
7:
|
7:
|
||||||
card_maker: 1.35.03
|
card_maker: 1.35.03
|
||||||
|
|
||||||
|
crypto:
|
||||||
|
encrypted_only: False
|
@ -169,8 +169,10 @@ class ChuniReader(BaseReader):
|
|||||||
fumen_path = MusicFumenData.find("file").find("path")
|
fumen_path = MusicFumenData.find("file").find("path")
|
||||||
|
|
||||||
if fumen_path is not None:
|
if fumen_path is not None:
|
||||||
chart_id = MusicFumenData.find("type").find("id").text
|
chart_type = MusicFumenData.find("type")
|
||||||
if chart_id == "4":
|
chart_id = chart_type.find("id").text
|
||||||
|
chart_diff = chart_type.find("str").text
|
||||||
|
if chart_diff == "WorldsEnd" and (chart_id == "4" or chart_id == "5"): # 4 in SDBT, 5 in SDHD
|
||||||
level = float(xml_root.find("starDifType").text)
|
level = float(xml_root.find("starDifType").text)
|
||||||
we_chara = (
|
we_chara = (
|
||||||
xml_root.find("worldsEndTagName")
|
xml_root.find("worldsEndTagName")
|
||||||
|
@ -2,9 +2,11 @@ from titles.ongeki.index import OngekiServlet
|
|||||||
from titles.ongeki.const import OngekiConstants
|
from titles.ongeki.const import OngekiConstants
|
||||||
from titles.ongeki.database import OngekiData
|
from titles.ongeki.database import OngekiData
|
||||||
from titles.ongeki.read import OngekiReader
|
from titles.ongeki.read import OngekiReader
|
||||||
|
from titles.ongeki.frontend import OngekiFrontend
|
||||||
|
|
||||||
index = OngekiServlet
|
index = OngekiServlet
|
||||||
database = OngekiData
|
database = OngekiData
|
||||||
reader = OngekiReader
|
reader = OngekiReader
|
||||||
|
frontend = OngekiFrontend
|
||||||
game_codes = [OngekiConstants.GAME_CODE]
|
game_codes = [OngekiConstants.GAME_CODE]
|
||||||
current_schema_version = 5
|
current_schema_version = 5
|
||||||
|
@ -978,35 +978,38 @@ class OngekiBase:
|
|||||||
"""
|
"""
|
||||||
Added in Bright
|
Added in Bright
|
||||||
"""
|
"""
|
||||||
rival_list = self.data.profile.get_rivals(data["userId"])
|
|
||||||
if rival_list is None or len(rival_list) < 1:
|
rival_list = []
|
||||||
|
user_rivals = self.data.profile.get_rivals(data["userId"])
|
||||||
|
for rival in user_rivals:
|
||||||
|
tmp = {}
|
||||||
|
tmp["rivalUserId"] = rival[0]
|
||||||
|
rival_list.append(tmp)
|
||||||
|
|
||||||
|
if user_rivals is None or len(rival_list) < 1:
|
||||||
return {
|
return {
|
||||||
"userId": data["userId"],
|
"userId": data["userId"],
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"userRivalList": [],
|
"userRivalList": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"userId": data["userId"],
|
"userId": data["userId"],
|
||||||
"length": len(rival_list),
|
"length": len(rival_list),
|
||||||
"userRivalList": rival_list._asdict(),
|
"userRivalList": rival_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_rival_data_api_reqiest(self, data: Dict) -> Dict:
|
def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
Added in Bright
|
Added in Bright
|
||||||
"""
|
"""
|
||||||
rivals = []
|
rivals = []
|
||||||
|
|
||||||
for rival in data["userRivalList"]:
|
for rival in data["userRivalList"]:
|
||||||
name = self.data.profile.get_profile_name(
|
name = self.data.profile.get_profile_name(
|
||||||
rival["rivalUserId"], self.version
|
rival["rivalUserId"], self.version
|
||||||
)
|
)
|
||||||
if name is None:
|
if name is None:
|
||||||
continue
|
continue
|
||||||
|
rivals.append({"rivalUserId": rival["rivalUserId"], "rivalUserName": name})
|
||||||
rivals.append({"rivalUserId": rival["rival"], "rivalUserName": name})
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"userId": data["userId"],
|
"userId": data["userId"],
|
||||||
"length": len(rivals),
|
"length": len(rivals),
|
||||||
@ -1027,7 +1030,6 @@ class OngekiBase:
|
|||||||
for song in music["userMusicList"]:
|
for song in music["userMusicList"]:
|
||||||
song["userRivalMusicDetailList"] = song["userMusicDetailList"]
|
song["userRivalMusicDetailList"] = song["userMusicDetailList"]
|
||||||
song.pop("userMusicDetailList")
|
song.pop("userMusicDetailList")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"userId": data["userId"],
|
"userId": data["userId"],
|
||||||
"rivalUserId": rival_id,
|
"rivalUserId": rival_id,
|
||||||
|
@ -48,9 +48,30 @@ class OngekiCardMakerVersionConfig:
|
|||||||
self.__config, "ongeki", "version", default={}
|
self.__config, "ongeki", "version", default={}
|
||||||
).get(version)
|
).get(version)
|
||||||
|
|
||||||
|
class OngekiCryptoConfig:
|
||||||
|
def __init__(self, parent_config: "OngekiConfig") -> None:
|
||||||
|
self.__config = parent_config
|
||||||
|
|
||||||
|
@property
|
||||||
|
def keys(self) -> Dict:
|
||||||
|
"""
|
||||||
|
in the form of:
|
||||||
|
internal_version: [key, iv]
|
||||||
|
all values are hex strings
|
||||||
|
"""
|
||||||
|
return CoreConfig.get_config_field(
|
||||||
|
self.__config, "ongeki", "crypto", "keys", default={}
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def encrypted_only(self) -> bool:
|
||||||
|
return CoreConfig.get_config_field(
|
||||||
|
self.__config, "ongeki", "crypto", "encrypted_only", default=False
|
||||||
|
)
|
||||||
|
|
||||||
class OngekiConfig(dict):
|
class OngekiConfig(dict):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.server = OngekiServerConfig(self)
|
self.server = OngekiServerConfig(self)
|
||||||
self.gachas = OngekiGachaConfig(self)
|
self.gachas = OngekiGachaConfig(self)
|
||||||
self.version = OngekiCardMakerVersionConfig(self)
|
self.version = OngekiCardMakerVersionConfig(self)
|
||||||
|
self.crypto = OngekiCryptoConfig(self)
|
||||||
|
87
titles/ongeki/frontend.py
Normal file
87
titles/ongeki/frontend.py
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import yaml
|
||||||
|
import jinja2
|
||||||
|
from twisted.web.http import Request
|
||||||
|
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.config import CoreConfig
|
||||||
|
|
||||||
|
from titles.ongeki.config import OngekiConfig
|
||||||
|
from titles.ongeki.const import OngekiConstants
|
||||||
|
from titles.ongeki.database import OngekiData
|
||||||
|
from titles.ongeki.base import OngekiBase
|
||||||
|
|
||||||
|
|
||||||
|
class OngekiFrontend(FE_Base):
|
||||||
|
def __init__(
|
||||||
|
self, cfg: CoreConfig, environment: jinja2.Environment, cfg_dir: str
|
||||||
|
) -> None:
|
||||||
|
super().__init__(cfg, environment)
|
||||||
|
self.data = OngekiData(cfg)
|
||||||
|
self.game_cfg = OngekiConfig()
|
||||||
|
if path.exists(f"{cfg_dir}/{OngekiConstants.CONFIG_NAME}"):
|
||||||
|
self.game_cfg.update(
|
||||||
|
yaml.safe_load(open(f"{cfg_dir}/{OngekiConstants.CONFIG_NAME}"))
|
||||||
|
)
|
||||||
|
self.nav_name = "O.N.G.E.K.I."
|
||||||
|
self.version_list = OngekiConstants.VERSION_NAMES
|
||||||
|
|
||||||
|
def render_GET(self, request: Request) -> bytes:
|
||||||
|
template = self.environment.get_template(
|
||||||
|
"titles/ongeki/frontend/ongeki_index.jinja"
|
||||||
|
)
|
||||||
|
sesh: Session = request.getSession()
|
||||||
|
usr_sesh = IUserSession(sesh)
|
||||||
|
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_data = {
|
||||||
|
"userRivalList": rival_list,
|
||||||
|
"userId": usr_sesh.userId
|
||||||
|
}
|
||||||
|
rival_info = OngekiBase.handle_get_user_rival_data_api_request(self, rival_data)
|
||||||
|
|
||||||
|
return template.render(
|
||||||
|
data=self.data.profile,
|
||||||
|
title=f"{self.core_config.server.name} | {self.nav_name}",
|
||||||
|
game_list=self.environment.globals["game_list"],
|
||||||
|
gachas=self.game_cfg.gachas.enabled_gachas,
|
||||||
|
profile_data=profile_data,
|
||||||
|
rival_info=rival_info["userRivalDataList"],
|
||||||
|
version_list=self.version_list,
|
||||||
|
version=self.version,
|
||||||
|
sesh=vars(usr_sesh)
|
||||||
|
).encode("utf-16")
|
||||||
|
else:
|
||||||
|
return redirectTo(b"/gate/", request)
|
||||||
|
|
||||||
|
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)
|
||||||
|
# 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)
|
||||||
|
# self.logger.info(f"{response}")
|
||||||
|
return redirectTo(b"/game/ongeki/", request)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
else:
|
||||||
|
return b"Something went wrong"
|
||||||
|
else:
|
||||||
|
return b"User is not logged in"
|
24
titles/ongeki/frontend/js/ongeki_scripts.js
Normal file
24
titles/ongeki/frontend/js/ongeki_scripts.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
function deleteRival(rivalUserId){
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
$.post("/game/ongeki/rival.delete",
|
||||||
|
{
|
||||||
|
rivalUserId
|
||||||
|
},
|
||||||
|
function(data,status){
|
||||||
|
window.location.replace("/game/ongeki/")
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function changeVersion(sel){
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
$.post("/game/ongeki/version.change",
|
||||||
|
{
|
||||||
|
version: sel.value
|
||||||
|
},
|
||||||
|
function(data,status){
|
||||||
|
window.location.replace("/game/ongeki/")
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
83
titles/ongeki/frontend/ongeki_index.jinja
Normal file
83
titles/ongeki/frontend/ongeki_index.jinja
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
{% extends "core/frontend/index.jinja" %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{% if sesh is defined and sesh["userId"] > 0 %}
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<h2> Profile </h2>
|
||||||
|
<h3>Version:
|
||||||
|
<select name="version" id="version" onChange="changeVersion(this)">
|
||||||
|
{% for ver in version_list %}
|
||||||
|
<option value={{loop.index0}} {{ "selected" if loop.index0==version else "" }} >{{ver}}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</h3>
|
||||||
|
<hr>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<h2> Name: {{ profile_data.userName if profile_data.userName is defined else "Profile not found" }}</h2>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<h4> ID: {{ profile_data.user if profile_data.user is defined else 'Profile not found' }}</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="row">
|
||||||
|
<h2> Rivals <button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#rival_add">Add</button></h2>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<table class="table table-dark table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">ID</th>
|
||||||
|
<th scope="col">Name</th>
|
||||||
|
<th scope="col">Delete</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for rival in rival_info%}
|
||||||
|
<tr id="{{rival.rivalUserId}}">
|
||||||
|
<td>{{rival.rivalUserId}}</td>
|
||||||
|
<td>{{rival.rivalUserName}}</td>
|
||||||
|
<td><button class="btn-danger btn btn-sm" onclick="deleteRival({{rival.rivalUserId}})">Delete</button></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="modal fade" id="rival_add" tabindex="-1" aria-labelledby="card_add_label" data-bs-theme="dark" aria-hidden="true">
|
||||||
|
<form id="rival" action="/game/ongeki/rival.add" method="post">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Modal title</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Note:<br>
|
||||||
|
Please use the ID show next to your name in the profile page.
|
||||||
|
<br>
|
||||||
|
<label for="rivalUserId">ID: </label><input form="rival" id="rivalUserId" name="rivalUserId" maxlength="5" type="number" required>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<input type=submit class="btn btn-primary" type="button" form="rival" value="Add">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
{% include 'titles/ongeki/frontend/js/ongeki_scripts.js' %}
|
||||||
|
</script>
|
||||||
|
{% else %}
|
||||||
|
<h2>Not Currently Logged In</h2>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock content %}
|
@ -7,6 +7,10 @@ import logging
|
|||||||
import coloredlogs
|
import coloredlogs
|
||||||
import zlib
|
import zlib
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
from Crypto.Cipher import AES
|
||||||
|
from Crypto.Util.Padding import pad
|
||||||
|
from Crypto.Protocol.KDF import PBKDF2
|
||||||
|
from Crypto.Hash import SHA1
|
||||||
from os import path
|
from os import path
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
@ -28,6 +32,7 @@ class OngekiServlet:
|
|||||||
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
||||||
self.core_cfg = core_cfg
|
self.core_cfg = core_cfg
|
||||||
self.game_cfg = OngekiConfig()
|
self.game_cfg = OngekiConfig()
|
||||||
|
self.hash_table: Dict[Dict[str, str]] = {}
|
||||||
if path.exists(f"{cfg_dir}/{OngekiConstants.CONFIG_NAME}"):
|
if path.exists(f"{cfg_dir}/{OngekiConstants.CONFIG_NAME}"):
|
||||||
self.game_cfg.update(
|
self.game_cfg.update(
|
||||||
yaml.safe_load(open(f"{cfg_dir}/{OngekiConstants.CONFIG_NAME}"))
|
yaml.safe_load(open(f"{cfg_dir}/{OngekiConstants.CONFIG_NAME}"))
|
||||||
@ -45,6 +50,8 @@ class OngekiServlet:
|
|||||||
]
|
]
|
||||||
|
|
||||||
self.logger = logging.getLogger("ongeki")
|
self.logger = logging.getLogger("ongeki")
|
||||||
|
|
||||||
|
if not hasattr(self.logger, "inited"):
|
||||||
log_fmt_str = "[%(asctime)s] Ongeki | %(levelname)s | %(message)s"
|
log_fmt_str = "[%(asctime)s] Ongeki | %(levelname)s | %(message)s"
|
||||||
log_fmt = logging.Formatter(log_fmt_str)
|
log_fmt = logging.Formatter(log_fmt_str)
|
||||||
fileHandler = TimedRotatingFileHandler(
|
fileHandler = TimedRotatingFileHandler(
|
||||||
@ -66,6 +73,37 @@ class OngekiServlet:
|
|||||||
coloredlogs.install(
|
coloredlogs.install(
|
||||||
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
|
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
|
||||||
)
|
)
|
||||||
|
self.logger.inited = True
|
||||||
|
|
||||||
|
for version, keys in self.game_cfg.crypto.keys.items():
|
||||||
|
if len(keys) < 3:
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.hash_table[version] = {}
|
||||||
|
|
||||||
|
method_list = [
|
||||||
|
method
|
||||||
|
for method in dir(self.versions[version])
|
||||||
|
if not method.startswith("__")
|
||||||
|
]
|
||||||
|
for method in method_list:
|
||||||
|
method_fixed = inflection.camelize(method)[6:-7]
|
||||||
|
# number of iterations is 64 on Bright Memory
|
||||||
|
iter_count = 64
|
||||||
|
hash = PBKDF2(
|
||||||
|
method_fixed,
|
||||||
|
bytes.fromhex(keys[2]),
|
||||||
|
128,
|
||||||
|
count=iter_count,
|
||||||
|
hmac_hash_module=SHA1,
|
||||||
|
)
|
||||||
|
|
||||||
|
hashed_name = hash.hex()[:32] # truncate unused bytes like the game does
|
||||||
|
self.hash_table[version][hashed_name] = method_fixed
|
||||||
|
|
||||||
|
self.logger.debug(
|
||||||
|
f"Hashed v{version} method {method_fixed} with {bytes.fromhex(keys[2])} to get {hash.hex()[:32]}"
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_allnet_info(
|
def get_allnet_info(
|
||||||
@ -100,6 +138,7 @@ class OngekiServlet:
|
|||||||
|
|
||||||
req_raw = request.content.getvalue()
|
req_raw = request.content.getvalue()
|
||||||
url_split = url_path.split("/")
|
url_split = url_path.split("/")
|
||||||
|
encrtped = False
|
||||||
internal_ver = 0
|
internal_ver = 0
|
||||||
endpoint = url_split[len(url_split) - 1]
|
endpoint = url_split[len(url_split) - 1]
|
||||||
client_ip = Utils.get_ip_addr(request)
|
client_ip = Utils.get_ip_addr(request)
|
||||||
@ -125,8 +164,45 @@ class OngekiServlet:
|
|||||||
# If we get a 32 character long hex string, it's a hash and we're
|
# If we get a 32 character long hex string, it's a hash and we're
|
||||||
# doing encrypted. The likelyhood of false positives is low but
|
# doing encrypted. The likelyhood of false positives is low but
|
||||||
# technically not 0
|
# technically not 0
|
||||||
self.logger.error("Encryption not supported at this time")
|
if internal_ver not in self.hash_table:
|
||||||
return b""
|
self.logger.error(
|
||||||
|
f"v{version} does not support encryption or no keys entered"
|
||||||
|
)
|
||||||
|
return 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"}')
|
||||||
|
|
||||||
|
endpoint = self.hash_table[internal_ver][endpoint.lower()]
|
||||||
|
|
||||||
|
try:
|
||||||
|
crypt = AES.new(
|
||||||
|
bytes.fromhex(self.game_cfg.crypto.keys[internal_ver][0]),
|
||||||
|
AES.MODE_CBC,
|
||||||
|
bytes.fromhex(self.game_cfg.crypto.keys[internal_ver][1]),
|
||||||
|
)
|
||||||
|
|
||||||
|
req_raw = crypt.decrypt(req_raw)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(
|
||||||
|
f"Failed to decrypt v{version} request to {endpoint} -> {e}"
|
||||||
|
)
|
||||||
|
return zlib.compress(b'{"stat": "0"}')
|
||||||
|
|
||||||
|
encrtped = True
|
||||||
|
|
||||||
|
if (
|
||||||
|
not encrtped
|
||||||
|
and self.game_cfg.crypto.encrypted_only
|
||||||
|
):
|
||||||
|
self.logger.error(
|
||||||
|
f"Unencrypted v{version} {endpoint} request, but config is set to encrypted only: {req_raw}"
|
||||||
|
)
|
||||||
|
return zlib.compress(b'{"stat": "0"}')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
unzip = zlib.decompress(req_raw)
|
unzip = zlib.decompress(req_raw)
|
||||||
@ -163,4 +239,17 @@ class OngekiServlet:
|
|||||||
|
|
||||||
self.logger.debug(f"Response {resp}")
|
self.logger.debug(f"Response {resp}")
|
||||||
|
|
||||||
return zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
zipped = zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
||||||
|
|
||||||
|
if not encrtped:
|
||||||
|
return zipped
|
||||||
|
|
||||||
|
padded = pad(zipped, 16)
|
||||||
|
|
||||||
|
crypt = AES.new(
|
||||||
|
bytes.fromhex(self.game_cfg.crypto.keys[internal_ver][0]),
|
||||||
|
AES.MODE_CBC,
|
||||||
|
bytes.fromhex(self.game_cfg.crypto.keys[internal_ver][1]),
|
||||||
|
)
|
||||||
|
|
||||||
|
return crypt.encrypt(padded)
|
@ -3,7 +3,7 @@ from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, an
|
|||||||
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, BigInteger
|
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, BigInteger
|
||||||
from sqlalchemy.engine.base import Connection
|
from sqlalchemy.engine.base import Connection
|
||||||
from sqlalchemy.schema import ForeignKey
|
from sqlalchemy.schema import ForeignKey
|
||||||
from sqlalchemy.sql import func, select
|
from sqlalchemy.sql import func, select, delete
|
||||||
from sqlalchemy.engine import Row
|
from sqlalchemy.engine import Row
|
||||||
from sqlalchemy.dialects.mysql import insert
|
from sqlalchemy.dialects.mysql import insert
|
||||||
|
|
||||||
@ -499,7 +499,7 @@ class OngekiProfileData(BaseData):
|
|||||||
def put_rival(self, aime_id: int, rival_id: int) -> Optional[int]:
|
def put_rival(self, aime_id: int, rival_id: int) -> Optional[int]:
|
||||||
sql = insert(rival).values(user=aime_id, rivalUserId=rival_id)
|
sql = insert(rival).values(user=aime_id, rivalUserId=rival_id)
|
||||||
|
|
||||||
conflict = sql.on_duplicate_key_update(rival=rival_id)
|
conflict = sql.on_duplicate_key_update(rivalUserId=rival_id)
|
||||||
|
|
||||||
result = self.execute(conflict)
|
result = self.execute(conflict)
|
||||||
if result is None:
|
if result is None:
|
||||||
@ -508,3 +508,10 @@ class OngekiProfileData(BaseData):
|
|||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
return result.lastrowid
|
return result.lastrowid
|
||||||
|
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)
|
||||||
|
if result is None:
|
||||||
|
self.logger.error(f"delete_rival: failed to delete! aime_id: {aime_id}, rival_id: {rival_id}")
|
||||||
|
else:
|
||||||
|
return result.rowcount
|
Loading…
Reference in New Issue
Block a user